diff options
Diffstat (limited to '')
238 files changed, 71163 insertions, 0 deletions
diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml new file mode 100644 index 0000000..3ea8e6f --- /dev/null +++ b/.github/workflows/builds.yml @@ -0,0 +1,90 @@ +name: builds + +on: + - push + - pull_request + +permissions: read-all + +env: + DEFAULT_CFLAGS: "-Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-function" + + # Apt sometimes likes to ask for user input, this will prevent that. + DEBIAN_FRONTEND: "noninteractive" + +jobs: + ubuntu-2004: + name: Ubuntu 20.04 + runs-on: ubuntu-latest + container: ubuntu:20.04 + steps: + - uses: actions/checkout@v3.1.0 + - name: Install system dependencies + run: | + apt update + apt-get upgrade -y + apt-get -y install make \ + autoconf \ + build-essential \ + autoconf \ + automake \ + dpkg-dev \ + debhelper \ + libtool \ + make \ + pkg-config \ + zlib1g-dev + - run: ./autogen.sh + - run: CFLAGS="${DEFAULT_CFLAGS}" ./configure + - run: make -j2 + - run: make install + - run: make distcheck + + ubuntu-2204: + name: Ubuntu 22.04 + runs-on: ubuntu-latest + container: ubuntu:22.04 + steps: + - uses: actions/checkout@v3.1.0 + - name: Install system dependencies + run: | + apt update + apt-get upgrade -y + apt-get -y install make \ + autoconf \ + build-essential \ + autoconf \ + automake \ + libtool \ + make \ + pkg-config \ + zlib1g-dev + - run: ./autogen.sh + - run: CFLAGS="${DEFAULT_CFLAGS}" ./configure + - run: make -j2 + - run: make install + - run: make distcheck + + centos-7: + name: CentOS 7 + runs-on: ubuntu-latest + container: centos:7 + steps: + - uses: actions/checkout@v3.1.0 + - name: Install system dependencies + run: | + yum -y install \ + autoconf \ + automake \ + gcc \ + gcc-c++ \ + libtool \ + make \ + pkgconfig \ + which \ + zlib-devel + - run: ./autogen.sh + - run: CFLAGS="${DEFAULT_CFLAGS}" ./configure + - run: make -j2 + - run: make install + - run: make distcheck diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 0000000..36fb6d1 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,26 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'libhtp' + dry-run: false + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'libhtp' + fuzz-seconds: 300 + dry-run: false + language: c++ + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd818ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +.deps
+.dirstamp
+.libs
+*~
+*.exe
+*.gcda
+*.gcno
+*.gcov
+*.la
+*.lo
+*.o
+*.slo
+*.stackdump
+*.dSYM
+*.a
+*.pdf
+*.sig
+*_config_auto_gen.h
+*_config_auto_gen.h.in
+htp-*.tar.gz
+compile
+.cproject
+/Makefile
+/Makefile.in
+/aclocal.m4
+/autom4te.cache
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.status
+/config.sub
+/configure
+/depcomp
+lcov
+/docs/doxygen
+/htp.pc
+/htp/.libs/
+/htp/Makefile
+/htp/Makefile.in
+/INSTALL
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+m4
+/nbproject/
+/nbproject/private/
+/stamp-h1
+/test-driver
+/test/.libs/
+/test/Makefile
+/test/Makefile.in
+/test/main*
+/test/test_bstr
+/test/test_hybrid
+/test/test_utils
+/test/test_all
+/test/test_all.log
+/test/test_all.trs
+/test/test-suite.log
+extras/ruby/*.gem
+/docs/Makefile
+/docs/Makefile.in
+/test/test_main
+/test/coverage.info
+/htp/htp_version.h
@@ -0,0 +1,6 @@ +ivanr = Ivan Ristic <ivanr@webkreator.com> +b1v1r = Brian Rectanus <brectanus@qualys.com> +wmetcalf = Will Metcalf <wmetcalf@qualys.com> +calfeld = Christopher Alfeld <calfeld@qualys.com> +glongo = Giuseppe Longo <giuseppe@glongo.me> +ironbee = The Iron Bee <theironbee@gmail.com> @@ -0,0 +1 @@ +See LICENSE.
\ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..d1d2396 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,473 @@ +0.5.47 (19 March 2024) +---------------------- + +- request: limit probing after missing protocol + +0.5.46 (08 February 2024) +------------------------- + +- tx: configurable number of maximum transactions + +- htp: offers possibility to remove transactions + +- headers: limit the size of folded headers + +- request: be more liberal about transfer-encoding value + +- request: continue processing even with invalid headers + +- http0.9: process headers if there are non-space characters + +- htp_util: fix spelling issue + +- src: fix -Wshorten-64-to-32 warnings + +- uri: normalization removes trailing spaces + +0.5.45 (11 July 2023) +--------------------- + +- log: resist allocation failure + +- support HTTP Bearer authentication + +0.5.44 (13 June 2023) +--------------------- + +- response: only trim spaces at headers names end + +- response: skips lines before response line + +- headers: log a warning for chunks extension + +0.5.43 (13 April 2023) +---------------------- + +- htp: do not log content-encoding: none + +- htp: do not error on multiple 100 Continue + +- readme: remove note on libhtp not being stable + +- uri: fix compile warning strict-prototypes + +- bstr: fix compile warning strict-prototypes + +- fuzz_diff: Free the rust test object. + +- github: add CIFuzz workflow + +0.5.42 (27 November 2022) +------------------------- + +- github: add initial workflow + +- htp: fixes warning about bad delimiter in URI + +- fuzz: fix a null dereference in a diff report + +- htp: fixes warning about integer + +0.5.41 (27 September 2022)
+--------------------------
+
+- trim white space of invalid folding for first header
+
+- clear buffered data for body data
+
+- minor optimization for decompression code
+
+0.5.40 (21 April 2022)
+----------------------
+
+- uri: optionally allows spaces in uri
+
+- ints: integer handling improvements
+
+- headers: continue on nul byte
+
+- headers: consistent trailing space handling
+
+- list: fix integer overflow
+
+- util: remove unused htp_utf8_decode
+
+- fix 100-continue with CL 0
+
+- lzma: don't do unnecessary realloc
+
+0.5.39 (16 November 2021)
+-------------------------
+
+- host: ipv6 address is a valid host
+
+- util: one char is not always empty line
+
+- test and fuzz improvements
+
+0.5.38 (30 June 2021)
+---------------------
+
+- consume empty lines when parsing chunks to avoid quadratic complexity
+
+- autotools fix for cygwin
+
+0.5.37 (2 March 2021)
+---------------------
+
+- support request body decompression
+
+- several accuracy fixes
+
+- fuzz improvments
+
+0.5.36 (3 December 2020)
+------------------------
+
+- fix a http pipelining issue (#304, fixed by #312)
+
+0.5.35 (8 October 2020)
+-----------------------
+
+- fix memory leak in tunnel traffoc
+
+- fix case where chunked data causes excessive CPU use
+
+0.5.34 (11 September 2020)
+--------------------------
+
+- support data GAP handling
+
+- support 100-continue Expect
+
+- lzma: give more control over settings
+
+0.5.33 (27 April 2020)
+----------------------
+
+- compression bomb protection
+
+- memory handling issue found by Oss-Fuzz
+
+- improve handling of anomalies in traffic
+
+0.5.32 (13 December 2019)
+--------------------------
+
+- bug fixes around pipelining
+
+0.5.31 (24 September 2019)
+--------------------------
+
+- various improvements related to 'HTTP Evader'
+
+- various fixes for issues found by oss-fuzz
+
+- adds optional LZMA decompression
+
+0.5.30 (07 March 2019)
+----------------------
+
+- array/list handing optimization by Philippe Antoine for an issue found be oss-fuzz
+
+- improved Windows support
+
+- fuzz targets improvements by Philippe Antoine
+
+- packaging improvements by Fabrice Fontaine
+
+- install doc improved by Wenhui Zhang
+
+0.5.29 (21 December 2018)
+-------------------------
+
+- prepare for oss-fuzz integration, by Philippe Antoine
+
+- fix undefined behavior signed int overflow
+
+- make status code parsing more robust
+
+0.5.28 (5 November 2018)
+------------------------
+
+- Fix potential memory leaks
+
+- Fix string truncation compile warning
+
+0.5.27 (18 July 2018)
+---------------------
+
+- Folded header field can be parsed as separate if there are no data available to peek into [#159]
+
+- libhtp crash at deal multiple decompression [#158]
+
+- Fix configure flag handling
+
+- Fix auth/digist header parsing out of bounds read
+
+0.5.26 (13 February 2018)
+-------------------------
+
+- allow missing requests [#128, #163]
+
+- fix memory leak when response line is body [#161]
+
+- fix build on MinGW [#162]
+
+- fix gcc7 compiler warnings [#157]
+
+0.5.25 (28 June 2017)
+---------------------
+
+- underscore in htp_validate_hostname [#149]
+
+- fix SONAME issue [#151]
+
+- remove unrelated docbook code from tree [#153]
+
+0.5.24 (07 June 2017)
+---------------------
+
+- fix HTTP connect handling issue [#150]
+
+0.5.23 (01 November 2016)
+--------------------------
+
+- enable -fPIC by default if supported and enable stack protection options on *BSD [#145]
+
+0.5.22 (06 September 2016)
+--------------------------
+
+- on "101 Switching Protocols", treat connection as a tunnel [#141]
+
+- Fix warning on OS X. [#142]
+
+0.5.21 (13 July 2016)
+---------------------
+
+- compression: fixed 'response_decompression_enabled' being
+ ignored in case of multiple encodings [#140]
+
+0.5.20 (7 June 2016)
+--------------------
+
+- compression: support multiple layers of compressed content [#133]
+
+- compression: opportunistic decompression [#137]
+
+- compression: implement rfc1950 deflate [#136]
+
+- chunked: handle mismatch between header and body [#135]
+
+- chunked: handle malformed chunked lengths [#134]
+
+0.5.19 (22 March 2016)
+----------------------
+
+- configure: improve strlcpy/strlcat checks [Victor Julien]
+
+- Fix uninitialized htp_tx_t::is_last value in htp_decompressors.c [Fedor Sakharov]
+
+- headers: fix memory leak on malformed headers [Victor Julien]
+
+- connect: handle response headers with 200 response [Victor Julien]
+
+0.5.18 (25 September 2015)
+--------------------------
+
+- Fixed [#120] Trigger request line parsing on
+ incomplete request [Victor Julien]
+
+- Fixed [#119] Fix uninitialized htp_tx_t::is_last value
+ in in htp_tx_res_process_body_data_ex() [Fedor Sakharov]
+
+- Fixed [#118] Coverity-identified missing break in switch [Sam Baskinger]
+
+- Fixed [#117] Coverity-identified issue of not checking
+ malloc() return value [Sam Baskinger]
+
+- Fixed [#116] Fix coverity-identified leaked file descriptors
+ in unit test [Sam Baskinger]
+
+- Fixed [#113] fix pkgconfig include dir [Eric Leblond]
+
+- Fixed [#111] Connect plain http [Victor Julien]
+
+- Fixed [#105] Do not invoke callbacks in htp_req_run_hook_body_data()
+ when there is no tx running. [Sam Baskinger]
+
+- Fixed [#104] Modifiying HTTP methods to be rfc3253 compliant [Andreas Moe]
+
+- Fixed [#103] Fixes [Victor Julien]
+
+- Fixed [#101] Make including the autoconf config header safer [Brian Rectanus]
+
+0.5.17 (25 February 2015)
+-------------------------
+
+- Fix URI parsing for non-std 'space' chars
+ [Fixed by Victor Julien / Reported by Darien Huss from Emerging Threats]
+
+- Fixing buffer overrun that was failing clang
+ -fsanitize=address checks [Sam Baskinger]
+
+- Replace strcat/sprintf by strlcat/snprintf [Giuseppe Longo]
+
+- Fix autogen on CentOS 5.11 [Victor Julien]
+
+- Fix dereferencing type-punned pointer on CentOS 5.11 [Giuseppe Longo]
+
+- Fix warning on OpenBSD [Giuseppe Longo]
+
+
+0.5.16 (11 December 2014)
+-------------------------
+
+- Per personality requestline leading whitespace handling [Victor Julien]
+
+- Improve request line parsing with leading spaces [Victor Julien]
+
+- Harden decompress code against memory stress [Victor Julien]
+
+
+0.5.15 (1 August 2014)
+----------------------
+
+- Fixed [#78] Make a case-insensitive comparision for the pattern "chunked"
+ for "Transfer-Encoding" [Anoop Saldanha]
+
+
+0.5.14 (22 July 2014)
+---------------------
+
+- Fixed the tests sometimes not returning the correct status code. Increased the
+ the compiler warnings for the tests.
+
+- Fixed [#77] Fix compiler warnings in the tests
+
+
+0.5.13 (16 July 2014)
+---------------------
+
+- Fixed [#56] Investigate clean-up performance with a large number of transactions
+ on a single connection
+
+
+0.5.12 (25 June 2014)
+---------------------
+
+- Fixed [#73] Fix double Content-Length issue [Wesley Shields]
+
+
+0.5.11 (5 April 2014)
+---------------------
+
+- Fixed [#72] On CONNECT requests inbound tx progress prematurely set to complete
+
+- Fixed [#71] Fix missing files in distribution target [Pierre Chifflier]
+
+
+0.5.10 (3 March 2014)
+--------------------
+
+- Fixed [#63] Final response body data callback missing on compressed responses.
+
+- Do not consume the byte that comes after an invalid UTF-8 character.
+
+- Use case insensitive comparison for content-coding values. Warn if unknown
+ response content encoding is encountered.
+
+- Small fixes. [#66, #69] [Victor Julien]
+
+
+0.5.9 (19 November 2013)
+------------------------
+
+- Fixed an HTP_HOST_AMBIGUOUS false positive.
+
+- Fixed the tests not compiling on OS X 10.9.
+
+
+0.5.8 (21 October 2013)
+-----------------------
+
+- Fixed [#54] Compression and base64 tests failing on some architectures.
+
+- Fixed [#55] Incorrect ambiguous host warning on some CONNECT requests.
+
+
+0.5.7 (18 September 2013)
+-------------------------
+
+- Use umask() with mkstemp() to ensure that temporary files are created with correct
+ permissions. This addresses the potential security problem, but creates another, because
+ umask() is not thread safe. For this and other reasons (see #52), file extraction will be
+ removed in a future release.
+
+- Fix copying hook_response_complete instead of hook_transaction_complete.
+
+- Fix several small memory leaks that occur when memory allocation fails.
+
+
+0.5.6 (22 July 2013)
+-------------------
+
+- Fix memory leaks in htp_tx_t::request_auth_username and htp_tx_t::request_auth_password.
+
+- [#43] When processing the response line, treat stream closure as the end of line.
+
+- Fix normalization when the URL begins with "./".
+
+- Do not fail a stream with an incorrectly formed digest username.
+
+- Do not stop processing request headers on PUT requests.
+
+
+0.5.5 (18 July 2013)
+--------------------
+
+- Tagging for a Suricata beta release.
+
+- [#46] Fix the segfault that occurs under certain conditions when an invalid hostname is supplied.
+
+- [#44] Fix libiconv detection on OpenBSD. [Victor Julien]
+
+
+0.5.4 (17 July 2013)
+--------------------
+
+- Tagging for a Suricata beta release.
+
+- Added htp_get_version(), which returns the complete library name (e.g., "LibHTP v0.5.4").
+
+- Hard field limit is now treated as specifying the maximum amount of memory LibHTP
+ will use for buffering per stream. Fields (e.g., headers) longer than this limit
+ will be accepted if they are contained within a single buffer submitted to LibHTP (i.e.,
+ if LibHTP does not have to do any buffering in order to process them). Soft limits
+ are currently not creating any warnings. This area will be improved in a future release.
+
+- Invalid headers no longer fail the entire stream. They are now treated as
+ headers without a name.
+
+- htp_conn_remove_tx() now returns HTP_DECLINED (was HTTP_ERROR) if the
+ specified transaction cannot be found.
+
+- htp_list_array_replace() now returns HTP_DECLINED (was HTP_ERROR) if the element at the
+ specified position does not exist.
+
+- New public functions:
+
+ htp_status_t htp_urldecode_inplace(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags);
+ htp_status_t htp_urldecode_inplace_ex(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags, int *expected_status_code);
+
+- Improved test coverage (84.1% lines, 91.3% functions).
+
+
+0.5.3 (14 June 2013)
+--------------------
+
+- Fix stream error when valid Basic Authentication information is provided.
+
+- Do not fail the entire stream if the Authorization header is invalid. Raise HTP_AUTH_INVALID instead.
+
+- When a request does not contain the request URI, leave htp_tx_t::request_uri NULL.
@@ -0,0 +1,30 @@ +Copyright (c) 2009-2010 Open Information Security Foundation +Copyright (c) 2010-2013 Qualys, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the Qualys, Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3f124f0 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,37 @@ + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = htp test docs + +DIST_SUBDIRS = htp test docs +EXTRA_DIST = LICENSE NOTICE docs/doxygen.conf docs/QUICK_START VERSION get-version.sh \ + docs/COMPATIBILITY_CHANGES + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = htp.pc + +test: all + @(cd test && $(MAKE) $@) + +test-compile-only: all + @(cd test && $(MAKE) $@) + +check-compile-only: all + @(cd test && $(MAKE) $@) + +doxygen doxygen-pdf: + @(cd docs && $(MAKE) $@) + +gcov: test + @ if [[ -x $(LCOV) ]]; then \ + lcov --capture --directory $(top_builddir)/htp/ --output-file $(top_builddir)/test/coverage.info --no-external ; \ + genhtml $(top_builddir)/test/coverage.info --output-directory $(top_builddir)/docs/lcov ; \ + echo "" ; \ + echo "Open docs/lcov/index.html to review lcov output" ; \ + fi + +clean-local: + rm -rf $(top_builddir)/docs/doxygen + rm -rf $(top_builddir)/docs/lcov + find $(top_builddir) -type f \( -name '*.gcda' -o -name '*.gcno' -o -name '*.gcov' \) -exec rm '{}' ';' + @@ -0,0 +1,70 @@ +LibHTP +============================================================================ +Copyright 2009-2010 Open Information Security Foundation +Copyright 2010-2013 Qualys, Inc. +============================================================================ + +LibHTP is a security-aware parser for the HTTP protocol and the related bits +and pieces. The goals of the project, in the order of importance, are as +follows: + + 1. Completeness of coverage; LibHTP must be able to parse virtually all + traffic that is found in practice. + + 2. Permissive parsing; LibHTP must never fail to parse a stream that would + be parsed by some other web server. + + 3. Awareness of evasion techniques; LibHTP must be able to detect and + effectively deal with various evasion techniques, producing, where + practical, identical or practically identical results as the web + server processing the same traffic stream. + + 4. Performance; The performance must be adequate for the desired tasks. + Completeness and security are often detrimental to performance. Our + idea of handling the conflicting requirements is to put the library + user in control, allowing him to choose the most desired library + characteristic. + + | STATUS LIBHTP IS VERY YOUNG AT THIS POINT. IT WILL BE SOME TIME BEFORE + | IT CAN BE CONSIDER COMPLETE. AT THE MOMENT, THE FOCUS OF DEVELOPMENT + | IS ON ACHIEVING THE FIRST TWO GOALS. + +See the LICENSE, COPYING and NOTICE files distributed with this work for +information regarding licensing, copying and copyright ownership. + + +INSTALLATION +------------ + +Assuming you're using an already packaged version of LibHTP, the installation +process should be as simple as: + + $ sudo chmod u+x autogen.sh + $ ./autogen.sh + $ ./configure + $ make + $ sudo make install + +If you've retrieved your LibHTP directly from the repository, you will need +to perform the following steps first: + + 1. Update the version number in VERSION. + + 2. Run autogen.sh, which will update the build system. + + + +DOCUMENTATION +------------- + +The best documentation at this time is the code itself and the Doxygen output (which +should be all right). There's also a quick start guide in the doc/ folder, which +should give you enough information to get going. + + +LICENSE +------- + +LibHTP is licensed under the BSD 3-Clause license (also known as "BSD New" and +"BSD Simplified".) The complete text of the license is enclosed in the file LICENSE. + @@ -0,0 +1,2 @@ +# This file is intended to be sourced by sh +PKG_VERSION=0.5.47 diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..ad55fda --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,18 @@ +environment: + matrix: + - COMPILER: mingw-w64 + MINGW_DIR: c:\msys64\mingw64 + MINGW_ARCH: x86_64 + + - COMPILER: mingw + MINGW_DIR: c:\msys64\mingw32 + MINGW_ARCH: i686 + +build_script: + - set Path=%MINGW_DIR%\bin;c:\msys64\usr\bin;%Path% + - bash autogen.sh + - bash configure + - make distcheck + +#on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..1d8b96c --- /dev/null +++ b/autogen.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Cleanup +rm -rf autom4te.cache + +# Generate +autoreconf -i -f -v diff --git a/config.rpath b/config.rpath new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/config.rpath diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..1ad3308 --- /dev/null +++ b/configure.ac @@ -0,0 +1,275 @@ + +dnl ---------------------- +dnl Initialization macros +dnl ---------------------- + +AC_INIT([LibHTP], m4_esyscmd([./get-version.sh VERSION])) +AM_INIT_AUTOMAKE() + +AC_CONFIG_HEADERS([htp_config_auto_gen.h]) +AC_CONFIG_FILES([htp/htp_version.h]) + + +dnl ----------------------------------------------- +dnl Package name and version number (user defined) +dnl ----------------------------------------------- + +GENERIC_LIBRARY_NAME=htp + +# This is _NOT_ the library release version, it's an API version. +GENERIC_LIBRARY_VERSION=2:0:0 +# | | | +# +------+ | +---+ +# | | | +# current:revision:age +# | | | +# | | +- increment if interfaces have been added +# | | set to zero if interfaces have been removed +# | | or changed +# | +- increment if source code has changed +# | set to zero if current is incremented +# +- increment if interfaces have been added, removed or changed +AC_SUBST(GENERIC_LIBRARY_VERSION) + +dnl -------------------------------- +dnl Package name and version number +dnl -------------------------------- + +PACKAGE=$GENERIC_LIBRARY_NAME +AC_SUBST(GENERIC_LIBRARY_NAME) + +GENERIC_VERSION=$PACKAGE_VERSION +GENERIC_RELEASE=$PACKAGE_VERSION +AC_SUBST(GENERIC_RELEASE) +AC_SUBST(GENERIC_VERSION) + +VERSION=$GENERIC_VERSION + +AC_CONFIG_MACRO_DIR([m4]) + +dnl -------------------------------- +dnl Options +dnl -------------------------------- + +AC_ARG_ENABLE(debug, [ --enable-debug Enable debug mode],, [ enable_debug=no ]) +if test "x$enable_debug" = "xyes"; then + CFLAGS="${CFLAGS} -DHTP_DEBUG" + echo "Debug mode enabled" +fi + +OLEVEL=2 + +AC_ARG_ENABLE(devmode, [ --enable-devmode Enable development mode],, [ enable_devmode=no ]) +if test "$enable_devmode" = "yes"; then + OLEVEL=0 + CFLAGS="${CFLAGS} -Werror -Wfatal-errors" + CPPFLAGS="${CPPFLAGS} -Werror -Wfatal-errors" + echo "Development mode enabled" +fi + +AC_ARG_ENABLE(gcov, [ --enable-gcov Enable gcov support],, [ enable_gcov=no ]) +if test "$enable_gcov" = "yes"; then + OLEVEL=0 + CFLAGS="${CFLAGS} --coverage -fprofile-arcs -ftest-coverage" + CPPFLAGS="${CPPFLAGS} --coverage -fprofile-arcs -ftest-coverage" + LDFLAGS="${LDFLAGS} -lgcov --coverage -fprofile-arcs" + echo "gcov support enabled" +fi + +CFLAGS="${CFLAGS} -O${OLEVEL}" +CPPFLAGS="${CPPFLAGS} -O${OLEVEL}" + +dnl ----------------------------------------------- +dnl Checks for programs. +dnl ----------------------------------------------- + +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CXX +AM_PROG_LIBTOOL +AM_SANITY_CHECK + +# Checks for library functions +#AC_CHECK_FUNCS([strlcpy strlcat]) + OCFLAGS=$CFLAGS + CFLAGS="" + AC_CHECK_FUNCS([strlcpy strlcat]) + CFLAGS=$OCFLAGS + +dnl ----------------------------------------------- +dnl Checks for libs. +dnl ----------------------------------------------- +AC_CHECK_HEADER(zlib.h,,[AC_ERROR(zlib.h not found ...)]) +ZLIB="" +AC_CHECK_LIB(z, inflate,, ZLIB="no") +if test "$ZLIB" = "no"; then + echo + echo " ERROR! zlib library not found" + echo + exit 1 +fi + +# Determine the OS +AC_MSG_CHECKING([OS]) +OS=`uname -s` +case "$OS" in + MINGW*) + AC_MSG_RESULT(MinGW) + OS_WINDOWS="true" + NO_STACK_PROTECTOR="true" + ;; + MSYS*) + AC_MSG_RESULT(MSYS) + OS_WINDOWS="true" + NO_STACK_PROTECTOR="true" + ;; + CYGWIN*) + AC_MSG_RESULT(Cygwin) + OS_CYGWIN="true" + NO_STACK_PROTECTOR="true" + ;; + FreeBSD*) + AC_MSG_RESULT(FreeBSD) + OS_FREEBSD="true" + CPPFLAGS="${CPPFLAGS} -I/usr/local/include" + LDFLAGS="${LDFLAGS} -L/usr/local/lib" + ;; + OpenBSD*) + AC_MSG_RESULT(OpenBSD) + OS_OPENBSD="true" + CPPFLAGS="${CPPFLAGS} -I/usr/local/include" + LDFLAGS="${LDFLAGS} -L/usr/local/lib" + ;; + Linux*) + AC_MSG_RESULT(Linux) + OS_LINUX="true" + ;; + *) + AC_MSG_RESULT(no) + ;; +esac +#We need to call the iconv macro after OS detection for FreeBSD to work properly +sinclude(m4/iconv.m4) +sinclude(m4/lib-ld.m4) +sinclude(m4/lib-link.m4) +sinclude(m4/lib-prefix.m4) +AM_ICONV +AM_CONDITIONAL([CYGWIN], [test x${OS_CYGWIN} = xtrue]) + +# iconvctl is not standard, it is defined only in GNU libiconv +AC_MSG_CHECKING(for iconvctl) +TMPLIBS="${LIBS}" +LIBS="${LIBS} ${LIBICONV}" + +AC_TRY_LINK([#include <stdlib.h> + #include <iconv.h>], + [int iconv_param = 0; + iconv_t cd = iconv_open("",""); + iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &iconv_param); + iconv_close(cd);], + [ac_cv_func_iconvctl=yes]) +AC_MSG_RESULT($ac_cv_func_iconvctl) +if test "$ac_cv_func_iconvctl" == yes; then + AC_DEFINE(HAVE_ICONVCTL,1,"Define to 1 if you have the `iconvctl' function.") +fi +LIBS="${TMPLIBS}" + +dnl ----------------------------------------------- +dnl Check and enable the GCC opts we want to use. +dnl We may need to add more checks +dnl ----------------------------------------------- + +dnl ----------------------------------------------- +dnl Check for GCC signed overflow warning support +dnl ----------------------------------------------- +AC_MSG_CHECKING(for gcc support of -Wstrict-overflow=1) +TMPCFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -Wstrict-overflow=1" +AC_TRY_COMPILE(,,[gcc_have_strict_overflow=yes],[gcc_have_strict_overflow=no]) +AC_MSG_RESULT($gcc_have_strict_overflow) +if test "$gcc_have_strict_overflow" != "yes"; then + CFLAGS="${TMPCFLAGS}" +fi + +if test "$NO_STACK_PROTECTOR" != "true"; then +dnl ----------------------------------------------- +dnl Check for GCC stack smashing protection +dnl ----------------------------------------------- +AC_MSG_CHECKING(for gcc support of stack smashing protection) +TMPCFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -fstack-protector" +AC_TRY_COMPILE(,,[gcc_have_fstack_protector=yes],[gcc_have_fstack_protector=no]) +AC_MSG_RESULT($gcc_have_fstack_protector) +if test "$gcc_have_fstack_protector" != "yes"; then + CFLAGS="${TMPCFLAGS}" +fi +fi + +dnl ----------------------------------------------- +dnl Check for GCC -D_FORTIFY_SOURCE support +dnl ----------------------------------------------- +AC_MSG_CHECKING(for gcc support of FORTIFY_SOURCE) +TMPCFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -D_FORTIFY_SOURCE=2" +AC_TRY_COMPILE(,,[gcc_have_fortify_source=yes],[gcc_have_fortify_source=no]) +AC_MSG_RESULT($gcc_have_fortify_source) +if test "$gcc_have_fortify_source" != "yes"; then + CFLAGS="${TMPCFLAGS}" +fi + +dnl ----------------------------------------------- +dnl Check for GCC -Wformat-security support +dnl ----------------------------------------------- +AC_MSG_CHECKING(for gcc support of -Wformat -Wformat-security) +TMPCFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -Wformat -Wformat-security" +AC_TRY_COMPILE(,,[gcc_have_format_security=yes],[gcc_have_format_security=no]) +AC_MSG_RESULT($gcc_have_format_security) +if test "$gcc_have_format_security" != "yes"; then + CFLAGS="${TMPCFLAGS}" +fi + +AC_MSG_CHECKING(for gcc support of -fPIC) +TMPCFLAGS="${CFLAGS}" +CFLAGS="${CFLAGS} -fPIC" +AC_TRY_COMPILE(,,[gcc_have_fpic=yes],[gcc_have_fpic=no]) +AC_MSG_RESULT($gcc_have_fpic) +if test "$gcc_have_fpic" != "yes"; then + CFLAGS="${TMPCFLAGS}" +fi + +dnl ----------------------------------------------- +dnl Check for doxygen +dnl ----------------------------------------------- +AC_ARG_WITH([doxygen], + [ --with-doxygen=PROG doxygen executable], + [doxygen="$withval"],[doxygen=no]) + +if test "$doxygen" != "no"; then + AC_MSG_NOTICE([Using doxygen: $doxygen]) +else + AC_PATH_PROGS([doxygen],[doxygen],[]) +fi + +DOXYGEN=$doxygen +AC_SUBST(DOXYGEN) + +dnl ----------------------------------------------- +dnl Check for lcov +dnl ----------------------------------------------- +AC_PATH_PROG(LCOV, lcov, [no]) +AC_SUBST(LCOV) + + +dnl ----------------------------------------------- +dnl Generates Makefiles, configuration files and scripts +dnl ----------------------------------------------- + +AC_PREFIX_DEFAULT(/usr/local) +AC_OUTPUT(Makefile \ + htp.pc \ + htp/Makefile \ + htp/lzma/Makefile \ + test/Makefile \ + docs/Makefile +) diff --git a/docs/COMPATIBILITY_CHANGES b/docs/COMPATIBILITY_CHANGES new file mode 100644 index 0000000..070b1fd --- /dev/null +++ b/docs/COMPATIBILITY_CHANGES @@ -0,0 +1,79 @@ +0.4 +--- + +- Removed the HOOK_* constants. Use the equivalent HTP_* constants instead. + +- Added the "htp_" prefix to all hook functions. + +- Renamed hooks.h to htp_hooks.h and hooks.c to htp_hooks.c. + +- Added the "htp_" prefix to all table structures and functions. + +- list_array_replace returns HTP_ERROR (-1) instead of 0 when the index does not exist. + +- Removed list iterators. Use this instead: + + for (int i = 0, n = htp_list_size(l); i < n; i++) { + bstr *b = htp_list_get(l, i); + // Do something with b here + } + +- Removed table iterators. Use this instead: + + bstr *key = NULL; + bstr *value = NULL; + for (int i = 0, n = htp_table_size(t); i < n; i++) { + value = htp_table_get_index(l, i, &key); + // Do something with key and value here + } + +- Removed htp_connp_create_copycfg(), along with the ability of connection parsers to + have private configurations. + +- htp_conn_remove_tx() now returns HTP_ERROR on error (was 0). + +- Renamed STREAM_STATE_* constants to HTP_STREAM_* + +- Personality HTP_SERVER_APACHE_2_2 renamed to HTP_SERVER_APACHE_2. Personality HTP_SERVER_APACHE removed. + +- Request parameters are now stored in a single structure called request_params. Previously, there + were 2 structures, one for query string parameters (GET) and another for body parameters (e.g., POST). + Further, before LibHTP stored parameter names and values in these structures. Now there is htp_param_t, + which stores additional useful information (e.g., allows parameters to be tracked back to the parsers)parsers. + +- Improve the table code to support 3 key management strategies. Strategy is determined when the first add + function is invoked, with consistency checks to ensure that approach is always used. + +- A number of *_destroy functions and bstr_free() used to take a pointer to a pointer. Now all such + functions are taking pointers to the structures that need to be destroyed. + +- Renamed HTP_FIELD_NUL_BYTE flag to HTP_FIELD_RAW_NUL. + +- Renamed HTP_PATH_FULLWIDTH_EVASION to HTP_PATH_HALF_FULL_RANGE. + +- Removed htp_tx_t::request_line_raw, htp_tx_t::response_line_raw. + +- Removed htp_tx_t::request_header_lines, htp_tx_t::response_header_lines. + +- Removed htp_tx_get_request_headers_raw() and htp_tx_get_response_headers_raw(). + +- Changed REQUEST_LINE callback signature from int (*callback_fn)(htp_connp_t *) + to int (*callback_fn)(htp_connp_t *, unsigned char *, size_t). The additional parameters are + used to expose the entire request line (incl. line terminators) to the callback. + +- Refactor how normalization options are configured. LibHTP now supports multiple normalization + contexts, with 2 used at this time: HTP_DECODER_URL_PATH and HTP_DECODER_URLENCODED. + +- New hooks to receive raw request header and trailer data: REQUEST_HEADER_DATA and REQUEST_TRAILER_DATA. + +- New hooks to receive raw response header and trailer data: RESPONSE_HEADER_DATA and RESPONSE_TRAILER_DATA. + +- Removed field htp_tx_t::request_uri_normalized. + +- Removed fields htp_tx_t::request_line_nul and htp_tx_t::request_line_nul_offset. + +- Renamed htp_tx_t::parsed_uri_incomplete to htp_tx_t::parsed_uri_raw. + +- Added request_hostname and request_port_number to htp_tx_t. These fields will hold the information + on what's the correct hostname/port, per RFC. Before, this information was in parsed_uri, but parsed_uri + now stores only what was actually supplied in the URI. diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..57a8325 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,18 @@ +CLEANFILES = *.pdf + +clean-local: + rm -rf doxygen + +doxygen: doxygen.conf $(DOXYGEN_DEPS) + @if test -n "$(DOXYGEN)"; then \ + $(DOXYGEN) $<; \ + else \ + echo "You need doxygen installed to build the docs."; \ + fi; + +doxygen-pdf: doxygen doxygen.pdf + +doxygen.pdf: doxygen + (cd doxygen/latex && make refman.pdf) && cp doxygen/latex/refman.pdf doxygen.pdf + +.PHONY: doxygen-pdf diff --git a/docs/QUICK_START b/docs/QUICK_START new file mode 100644 index 0000000..30943e0 --- /dev/null +++ b/docs/QUICK_START @@ -0,0 +1,106 @@ +
+QUICK START
+-----------
+
+LibHTP is envisioned to be many things, but the only scenario in which it has been tested
+so far is that when you need to parse a duplex HTTP stream which you have obtained by
+passively intercepting a communication channel. The assumption is that you have raw TCP data
+(after SSL, if SSL is used).
+
+Every parsing operation needs to follow these steps:
+
+ 1. Configure-time:
+
+ 1.1. Create one or more parser configuration structures.
+
+ 1.2. Tweak the configuration of each parser to match the behaviour of
+ the server you're intercepting the communication of (htp_config_set_* functions).
+
+ 1.3. Register the parser callbacks you'll need. You will need to use parser callbacks
+ if you want to monitor parsing events as they occur, and gain access to partial
+ transaction information. If you are processing data in batch (off-line) you may
+ simply parse entire streams at a time and only analyze complete transaction data
+ after the fact.
+
+ If you need to gain access to request and response bodies, your only option at
+ this time is to use the callbacks, because the parser will not preserve that
+ information.
+
+ For callback registration, look up the htp_config_register_* functions.
+
+ If your program operates in real-time then it may be desirable to dispose of
+ the used resources after each transaction is parsed. To do that, use the
+ htp_config_set_tx_auto_destroy() function to tell LibHTP to delete transactions
+ after they are no longer needed.
+
+ 2. Run-time:
+
+ 2.1. Create a parser instance for every TCP stream you want to process.
+
+ 2.2. Feed the parser inbound and outbound data.
+
+ The parser will typically always consume complete data chunks and return
+ STREAM_STATE_DATA, which means that you can continue to feed it more data
+ when you have it. If you have a queue of data chunks, always first send the
+ parser all the _request_ chunks you have. That will ensure that the parser
+ never encounters a response for which it had not seen a request (which
+ would result with a fatal error).
+
+ If you get STREAM_STATE_ERROR, the parser has encountered a fatal error and
+ is unable to continue to parse the stream. An error should never happen for
+ a valid HTTP stream. If you encounter such an error and you believe the
+ HTTP stream is valid, please send us the PCAP file we can use to diagnose
+ the problem.
+
+ There is one situation when the parser will not be able to consume a complete
+ request data chunk, in which case it will return STREAM_STATE_DATA_OTHER. This
+ means that the parser needs to see some response data. You will then need to
+ do the following:
+
+ 2.2.1. Remember how many bytes of the request chunk data were consumed (using
+ htp_connp_req_data_consumed()).
+
+ 2.2.2. Suspend request parsing until you get some response data.
+
+ 2.2.3. Feed some response data (when you have it) to the parser.
+
+ Note that it is also possible to receive STREAM_STATE_DATA_OTHER
+ from the response parser. If that happens, you will need to
+ remember how many bytes were consumed using
+ htp_connp_res_data_consumed().
+
+ 2.2.4. After each chunk of response data fed to the parser, attempt
+ to resume request stream parsing.
+
+ 2.2.5. If you again receive STREAM_STATE_DATA_OTHER go back to 2.2.3.
+
+ 2.2.6. Otherwise, feed to the parser all the request data you have. This is
+ necessary to prevent the case of the parser seeing more responses
+ than requests (which would inevitably result with an error).
+
+ 2.2.7. Send unprocessed response data from 2.2.3 (if any).
+
+ 2.2.8. Continue sending request/response data as normal.
+
+ The above situation should occur very rarely.
+
+ 2.3. Analyze transaction data in callbacks (if you want to have access to
+ the data as it is being produced).
+
+ 2.4. Analyze transaction data after an entire TCP stream has been processed.
+
+ 2.4. Destroy parser instance to free up the allocated resources.
+
+
+USER DATA
+---------
+
+If you're using the callbacks and you need to keep state between invocations, you have two
+options:
+
+ 1. Associate one opaque structure with a parser instance, using htp_connp_set_user_data().
+
+ 2. Associate one opaque structure with a transaction instance, using htp_tx_set_user_data().
+ The best place to do this is in a TRANSACTION_START callback. Don't forget to free up
+ any resources you allocate on per-transaction basis, before you delete each transaction.
+
diff --git a/docs/doxygen.conf b/docs/doxygen.conf new file mode 100644 index 0000000..83f3fe2 --- /dev/null +++ b/docs/doxygen.conf @@ -0,0 +1,1356 @@ +# Doxyfile 1.5.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = HTP + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.5 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxygen/ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../htp + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.c *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = ./ + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is enabled by default, which results in a transparent +# background. Warning: Depending on the platform used, enabling this option +# may lead to badly anti-aliased labels on the edges of a graph (i.e. they +# become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/examples/mod_libhtp/mod_libhtp.c b/examples/mod_libhtp/mod_libhtp.c new file mode 100644 index 0000000..07a779c --- /dev/null +++ b/examples/mod_libhtp/mod_libhtp.c @@ -0,0 +1,161 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "ap_config.h" + +#include "htp.h" +#include "htp_transaction.h" + +module AP_MODULE_DECLARE_DATA libhtp_module; + +// XXX Handle all allocation failures + +static int convert_method_number(int method_number) { + // We can cheat here because LibHTP reuses Apache's + // method identifiers. But we really shouldn't. + if ((method_number >= 0)&&(method_number <= 26)) { + return method_number; + } + + // TODO Decouple this functions from Apache's internals. + + return HTP_M_UNKNOWN; +} + +static int convert_protocol_number(int protocol_number) { + // In Apache, 1.1 is stored as 1001. In LibHTP, + // the same protocol number is stored as 101. + return (protocol_number / 1000) * 100 + (protocol_number % 1000); +} + +static apr_status_t transaction_cleanup(htp_tx_t *tx) { + htp_tx_destroy(tx); + return APR_SUCCESS; +} + +static int libhtp_post_read_request(request_rec *r) { + htp_connp_t *connp = ap_get_module_config(r->connection->conn_config, &libhtp_module); + if (connp == NULL) return DECLINED; + + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + if (tx == NULL) return DECLINED; + + // Request begins + htp_tx_state_request_start(tx); + + // Populate request line + htp_tx_req_set_method_c(tx, r->method, HTP_ALLOC_REUSE); + htp_tx_req_set_method_number(tx, convert_method_number(r->method_number)); + htp_tx_req_set_uri_c(tx, r->uri, HTP_ALLOC_REUSE); + htp_tx_req_set_query_string_c(tx, r->args, HTP_ALLOC_REUSE); + htp_tx_req_set_protocol_c(tx, r->protocol, HTP_ALLOC_REUSE); + htp_tx_req_set_protocol_number(tx, convert_protocol_number(r->proto_num)); + htp_tx_req_set_protocol_0_9(tx, r->assbackwards); + + // Request line available + htp_tx_state_request_line(tx); + + // Populate request headers + size_t i; + const apr_array_header_t *arr = apr_table_elts(r->headers_in); + const apr_table_entry_t *te = (apr_table_entry_t *) arr->elts; + for (i = 0; i < arr->nelts; i++) { + htp_tx_req_set_header_c(tx, te[i].key, te[i].val, HTP_ALLOC_REUSE); + } + + // Request headers available + htp_tx_state_request_headers(tx); + + // Attach LibHTP's transaction to Apache's request + ap_set_module_config(r->request_config, &libhtp_module, tx); + apr_pool_cleanup_register(r->pool, (void *)tx, + (apr_status_t (*)(void *))transaction_cleanup, apr_pool_cleanup_null); + + return DECLINED; +} + +static apr_status_t connection_cleanup(htp_connp_t *connp) { + htp_config_destroy(connp->cfg); + htp_connp_destroy(connp); + + return APR_SUCCESS; +} + +static int libhtp_pre_connection(conn_rec *c, void *csd) { + // Configuration; normally you'd read the configuration from + // a file, or some other type of storage, but, because this is + // just an example, we have it hard-coded. + htp_cfg_t *cfg = htp_config_create(); + if (cfg == NULL) return OK; + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2_2); + htp_config_register_urlencoded_parser(cfg); + htp_config_register_multipart_parser(cfg); + + // Connection parser + htp_connp_t *connp = htp_connp_create(cfg); + if (connp == NULL) { + htp_config_destroy(cfg); + free(connp); + return OK; + } + + // Open connection + htp_connp_open(connp, c->remote_ip, /* XXX remote port */ 0, c->local_ip, /* XXX local port */0, NULL); + + ap_set_module_config(c->conn_config, &libhtp_module, connp); + apr_pool_cleanup_register(c->pool, (void *)connp, + (apr_status_t (*)(void *))connection_cleanup, apr_pool_cleanup_null); + + return OK; +} + +static void libhtp_register_hooks(apr_pool_t *p) { + ap_hook_pre_connection(libhtp_pre_connection, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_post_read_request(libhtp_post_read_request, NULL, NULL, APR_HOOK_MIDDLE); +} + +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA libhtp_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + libhtp_register_hooks /* register hooks */ +}; + diff --git a/extras/htptest.c b/extras/htptest.c new file mode 100644 index 0000000..472db5e --- /dev/null +++ b/extras/htptest.c @@ -0,0 +1,569 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +/* + * This program is a simple example of how to use LibHTP to parse a HTTP + * connection stream. It uses libnids for TCP reassembly and LibHTP for + * HTTP parsing. + * + * This program is only meant as an demonstration; it is not suitable + * to be used in production. Furthermore, libnids itself was unreliable + * in my tests. + * + * Compile with: + * + * $ gcc htptest.c -lhtp -lz -lnids -o htptest + * + */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdio.h> +#include "nids.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <htp/htp.h> +#include <htp/htp_list.h> +#include <htp/htp_table.h> + +#define DIRECTION_CLIENT 1 +#define DIRECTION_SERVER 2 + +typedef struct chunk_t chunk_t; +typedef struct stream_data stream_data; + +/** Data chunk structure */ +struct chunk_t { + char *data; + size_t len; + int direction; + size_t consumed; +}; + +/** Per-stream data structure */ +struct stream_data { + int id; + htp_connp_t *connp; + int direction; + int fd; + int chunk_counter; + int log_level; + int req_count; + htp_list_t *chunks; + htp_list_t *inbound_chunks; + htp_list_t *outbound_chunks; +}; + +/** LibHTP parser configuration */ +htp_cfg_t *cfg; + +/** Connection counter */ +int counter = 1000; + +/** + * Free stream data. + * + * @param[in] sd + */ +void free_stream_data(stream_data *sd) { + if (sd == NULL) return; + + // Free stream chunks, if any + if (sd->chunks != NULL) { + for (int i = 0, n = htp_list_size(sd->chunks); i < n; i++) { + chunk_t *chunk = htp_list_get(sd->chunks, i); + free(chunk->data); + free(chunk); + } + + htp_list_destroy(sd->chunks); + sd->chunked = NULL; + } + + // Free inbound chunks, if any + if (sd->inbound_chunks != NULL) { + for (int i = 0, n = htp_list_size(sd->inbound_chunks); i < n; i++) { + chunk_t *chunk = htp_list_get(sd->inbound_chunkds, i); + free(chunk->data); + free(chunk); + } + + htp_list_destroy(sd->inbound_chunks); + sd->inbound_chunks = NULL; + } + + // Free outbound chunks, if any + if (sd->outbound_chunks != NULL) { + for (int i = 0, n = htp_list_size(sd->outbound_chunks); i < n; i++) { + chunk_t *chunk = htp_list_get(sd->outbound_chunkds, i); + free(chunk->data); + free(chunk); + } + + htp_list_destroy(sd->outbound_chunks); + sd->outbound_chunks = NULL; + } + + // Close the stream file, if we have it open + if (sd->fd != -1) { + close(sd->fd); + } + + free(sd); +} + +/** + * Process as much buffered inbound and outbound data as possible + * (in that order) + * + * @param[in] sd + */ +void process_stored_stream_data(stream_data *sd) { + int loop = 0; + + do { + // Reset loop + loop = 0; + + // Send as much inbound data as possible + while((sd->connp->in_status == HTP_STREAM_DATA)&&(htp_list_size(sd->inbound_chunks) > 0)) { + chunk_t *chunk = (chunk_t *)htp_list_get(sd->inbound_chunks, 0); + + int rc = htp_connp_req_data(sd->connp, 0, chunk->data + chunk->consumed, chunk->len - chunk->consumed); + if (rc == HTP_STREAM_DATA) { + // The entire chunk was consumed + htp_list_shift(sd->inbound_chunks); + free(chunk->data); + free(chunk); + } else { + // Partial consumption + chunk->consumed = htp_connp_req_data_consumed(sd->connp); + } + } + + // Send as much outbound data as possible + while((sd->connp->out_status == HTP_STREAM_DATA)&&(htp_list_size(sd->outbound_chunks) > 0)) { + chunk_t *chunk = (chunk_t *)htp_list_get(sd->outbound_chunks, 0); + + int rc = htp_connp_res_data(sd->connp, 0, chunk->data + chunk->consumed, chunk->len - chunk->consumed); + if (rc == HTP_STREAM_DATA) { + // The entire chunk was consumed + htp_list_shift(sd->outbound_chunks); + free(chunk->data); + free(chunk); + } else { + // Partial consumption + chunk->consumed = htp_connp_res_data_consumed(sd->connp); + } + + // Whenever we send some outbound data to the parser, + // we need to go back and try to send inbound data + // if we have it. + loop = 1; + } + } while(loop); +} + +/** + * Process a chunk of the connection stream. + * + * @param[in] sd + * @param[in] direction + * @param[in] hlf + */ +void process_stream_data(stream_data *sd, int direction, struct half_stream *hlf) { + chunk_t *chunk = NULL; + int rc; + + //printf("#DATA direction %d bytes %d\n", sd->direction, hlf->count_new); + + if (sd->direction == direction) { + // Inbound data + + switch(sd->connp->in_status) { + case HTP_STREAM_NEW : + case HTP_STREAM_DATA : + // Send data to parser + + rc = htp_connp_req_data(sd->connp, 0, hlf->data, hlf->count_new); + if (rc == HTP_STREAM_DATA_OTHER) { + // Encountered inbound parsing block + + // Store partial chunk for later + chunk = calloc(1, sizeof(chunk_t)); + // TODO + chunk->len = hlf->count_new - htp_connp_req_data_consumed(sd->connp); + chunk->data = malloc(chunk->len); + // TODO + memcpy(chunk->data, hlf->data + htp_connp_req_data_consumed(sd->connp), chunk->len); + htp_list_add(sd->inbound_chunks, chunk); + } else + if (rc != HTP_STREAM_DATA) { + // Inbound parsing error + sd->log_level = 0; + fprintf(stderr, "[#%d] Inbound parsing error: %d\n", sd->id, rc); + // TODO Write connection to disk + } + break; + + case HTP_STREAM_ERROR : + // Do nothing + break; + + case HTP_STREAM_DATA_OTHER : + // Store data for later + chunk = calloc(1, sizeof(chunk_t)); + // TODO + chunk->len = hlf->count_new; + chunk->data = malloc(chunk->len); + // TODO + memcpy(chunk->data, hlf->data, chunk->len); + htp_list_add(sd->inbound_chunks, chunk); + break; + } + } else { + // Outbound data + switch(sd->connp->out_status) { + case HTP_STREAM_NEW : + case HTP_STREAM_DATA : + // Send data to parser + + rc = htp_connp_res_data(sd->connp, 0, hlf->data, hlf->count_new); + if (rc == HTP_STREAM_DATA_OTHER) { + // Encountered outbound parsing block + + // Store partial chunk for later + chunk = calloc(1, sizeof(chunk_t)); + // TODO + chunk->len = hlf->count_new - htp_connp_res_data_consumed(sd->connp); + chunk->data = malloc(chunk->len); + // TODO + memcpy(chunk->data, hlf->data + htp_connp_res_data_consumed(sd->connp), chunk->len); + htp_list_add(sd->outbound_chunks, chunk); + } else + if (rc != HTP_STREAM_DATA) { + // Outbound parsing error + sd->log_level = 0; + fprintf(stderr, "[#%d] Outbound parsing error: %d\n", sd->id, rc); + } + break; + + case HTP_STREAM_ERROR : + // Do nothing + break; + + case HTP_STREAM_DATA_OTHER : + // Store data for later + chunk = calloc(1, sizeof(chunk_t)); + // TODO + chunk->len = hlf->count_new; + chunk->data = malloc(chunk->len); + // TODO + memcpy(chunk->data, hlf->data, chunk->len); + htp_list_add(sd->outbound_chunks, chunk); + break; + } + } + + // Process as much stored data as possible + process_stored_stream_data(sd); +} + +/** + * Called by libnids whenever it has an event we have to handle. + * + * @param[in] tcp + * @param[in] user_data + */ +void tcp_callback (struct tcp_stream *tcp, void **user_data) { + stream_data *sd = *user_data; + + // New connection + if (tcp->nids_state == NIDS_JUST_EST) { + tcp->client.collect++; + tcp->server.collect++; + tcp->server.collect_urg++; + tcp->client.collect_urg++; + + // Allocate custom per-stream data + sd = calloc(1, sizeof(stream_data)); + sd->id = counter++; + sd->direction = -1; + sd->fd = -1; + sd->log_level = -1; + sd->chunks = htp_list_array_create(16); + sd->inbound_chunks = htp_list_array_create(16); + sd->outbound_chunks = htp_list_array_create(16); + sd->req_count = 1; + + // Init LibHTP parser + sd->connp = htp_connp_create(cfg); + if (sd->connp == NULL) { + fprintf(stderr, "Failed to create LibHTP parser instance.\n"); + exit(1); + } + + // Associate TCP stream information with the HTTP connection parser + htp_connp_set_user_data(sd->connp, sd); + + // Associate TCP stream information with the libnids structures + *user_data = sd; + + return; + } + + // Connection close + if (tcp->nids_state == NIDS_CLOSE) { + if (sd == NULL) return; + + // Destroy parser + htp_connp_destroy_all(sd->connp); + + // Free custom per-stream data + free_stream_data(sd); + + return; + } + + // Connection close (RST) + if (tcp->nids_state == NIDS_RESET) { + if (sd == NULL) return; + + // Destroy parser + htp_connp_destroy_all(sd->connp); + + // Free custom per-stream data + free_stream_data(sd); + + return; + } + + if (tcp->nids_state == NIDS_DATA) { + struct half_stream *hlf; + int direction; + + if (tcp->client.count_new) { + hlf = &tcp->client; + direction = DIRECTION_SERVER; + } else { + hlf = &tcp->server; + direction = DIRECTION_CLIENT; + } + + if (sd == NULL) return; + + if (sd->direction == -1) { + sd->direction = direction; + } + + // Write data to disk or store for later + if (sd->fd == -1) { + // Store data, as we may need it later + chunk_t *chunk = calloc(1, sizeof(chunk_t)); + // TODO + chunk->direction = direction; + chunk->data = malloc(hlf->count_new); + // TODO + chunk->len = hlf->count_new; + memcpy(chunk->data, hlf->data, chunk->len); + + htp_list_add(sd->chunks, chunk); + } else { + // No need to store, write directly to file + + if (sd->chunk_counter != 0) { + write(sd->fd, "\r\n", 2); + } + + if (sd->direction == direction) { + write(sd->fd, ">>>\r\n", 5); + } else { + write(sd->fd, "<<<\r\n", 5); + } + + write(sd->fd, hlf->data, hlf->count_new); + + sd->chunk_counter++; + } + + // Process data + process_stream_data(sd, direction, hlf); + + return; + } +} + +/** + * Invoked at the end of every transaction. + * + * @param[in] connp + */ +int callback_response(htp_connp_t *connp) { + stream_data *sd = (stream_data *)htp_connp_get_user_data(connp); + + char *x = bstr_util_strdup_to_c(connp->out_tx->request_line); + fprintf(stdout, "[#%d/%d] %s\n", sd->id, sd->req_count, x); + free(x); + + sd->req_count++; +} + +/** + * Invoked every time LibHTP wants to log. + * + * @param[in] log + */ +int callback_log(htp_log_t *log) { + stream_data *sd = (stream_data *)htp_connp_get_user_data(log->connp); + + if ((sd->log_level == -1)||(sd->log_level > log->level)) { + sd->log_level = log->level; + } + + if (log->code != 0) { + fprintf(stderr, "[#%d/%d][%d][code %d][file %s][line %d] %s\n", sd->id, sd->req_count, + log->level, log->code, log->file, log->line, log->msg); + } else { + fprintf(stderr, "[#%d/%d][%d][file %s][line %d] %s\n", sd->id, sd->req_count, + log->level, log->file, log->line, log->msg); + } + + // If this is the first time a log message was generated for this connection, + // start writing the entire thing to a file on disk. + if (sd->fd == -1) { + char filename[256]; + + // TODO Use IP addresses and ports in filename + snprintf(filename, 255, "conn-%d.t", sd->id); + + sd->fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); + if (sd->fd == -1) { + fprintf(stderr, "Failed to create file %s: %s\n", filename, strerror(errno)); + exit(1); + } + + // Write to disk the data we have in memory + for (int i = 0, n = htp_list_size(sd->chunks); i < n; i++) { + chunk_t *chunk = htp_list_get(sd->chunks, i); + + if (sd->chunk_counter != 0) { + write(sd->fd, "\r\n", 2); + } + + if (sd->direction == chunk->direction) { + write(sd->fd, ">>>\r\n", 5); + } else { + write(sd->fd, "<<<\r\n", 5); + } + + write(sd->fd, chunk->data, chunk->len); + + sd->chunk_counter++; + } + } +} + +/** + * Prints usage. + */ +void print_usage() { + fprintf(stdout, "Usage: htpMon [-r file] [\"expression\"]\n"); +} + +/** + * Main entry point for this program. + * + * @param[in] argc + * @param[in] argv + */ +int main(int argc, char *argv[]) { + // Check parameters + if ((argc < 2)||(argc > 4)) { + print_usage(); + return 1; + } + + // Configure libnids + if (argc > 2) { + if (strcmp(argv[1], "-r") != 0) { + print_usage(); + return 1; + } + + nids_params.filename = argv[2]; + + if (argc == 4) { + nids_params.pcap_filter = argv[3]; + } + } else { + nids_params.pcap_filter = argv[1]; + } + + // Initialize libnids + if (!nids_init()) { + fprintf(stderr, "libnids initialization failed: %s\n", nids_errbuf); + return 1; + } + + // Create LibHTP configuration + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2_2); + + htp_config_register_response_complete(cfg, callback_response); + htp_config_register_log(cfg, callback_log); + + // Run libnids + nids_register_tcp(tcp_callback); + nids_run(); + + // Destroy LibHTP configuration + htp_config_destroy(cfg); + + return 0; +} + diff --git a/extras/ruby/HTP.c b/extras/ruby/HTP.c new file mode 100644 index 0000000..c83092d --- /dev/null +++ b/extras/ruby/HTP.c @@ -0,0 +1,1008 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @author Christopher Alfeld <calfeld@qualys.com> + */ + +#include <ruby.h> +#include <htp/htp.h> + +/* Status + * Complete: Tx, Header, HeaderLine, URI, all numeric constants. + * Incomplete: Cfg, Connp + * Missing completely: file, file_data, log, tx_data (probably not needed) + */ + +// Debug +#ifdef RBHTP_DBEUG +#include <stdio.h> +#define P( value ) { VALUE inspect = rb_funcall( value, rb_intern( "inspect" ), 0 ); printf("%s\n",StringValueCStr(inspect)); } +#else +#define P( value ) +#endif + +static VALUE mHTP; +static VALUE cCfg; +static VALUE cConnp; +static VALUE cTx; +static VALUE cHeader; +static VALUE cHeaderLine; +static VALUE cURI; +static VALUE cFile; +static VALUE cConn; + +#define BSTR_TO_RSTR( B ) ( rb_str_new( bstr_ptr( B ), bstr_len( B ) ) ) + +// Accessor Helpers +#define RBHTP_R_INT( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return INT2FIX( x->N ); \ + } + +#define RBHTP_R_TV( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return rb_time_new( x->N.tv_sec, x->N.tv_usec ); \ + } + +#define RBHTP_R_CSTR( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + if ( x->N == NULL ) return Qnil; \ + return rb_str_new2( x->N ); \ + } + +#define RBHTP_W_INT( T, N ) \ + VALUE rbhtp_## T ##_ ## N ## _set( VALUE self, VALUE v ) \ + { \ + Check_Type( v, T_FIXNUM ); \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + x->N = FIX2INT( v ); \ + return Qnil; \ + } + +#define RBHTP_RW_INT( T, N ) \ + RBHTP_R_INT( T, N ) \ + RBHTP_W_INT( T, N ) + +#define RBHTP_R_BOOL( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return x->N == 1 ? Qtrue : Qfalse; \ + } + +#define RBHTP_R_STRING( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + if ( x->N == NULL ) \ + return Qnil; \ + return BSTR_TO_RSTR( x->N ); \ + } + +#define RBHTP_R_HTP( T, N, H ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + if ( x->N == NULL ) \ + return Qnil; \ + return rb_funcall( H, rb_intern( "new" ), 1, \ + Data_Wrap_Struct( rb_cObject, 0, 0, x->N ) ); \ + } + +#define RBHTP_R_URI( T, N ) RBHTP_R_HTP( T, N, cURI ) + +static VALUE rbhtp_r_string_table( htp_table_t* table ) +{ + if ( table == NULL ) return Qnil; + + bstr *k, *v; + VALUE r = rb_ary_new(); + for (int i = 0, n = htp_table_size(table); i < n; i++) { + v = htp_table_get_index(table, i, &k); + rb_ary_push( r, rb_ary_new3( 2, + BSTR_TO_RSTR( *k ), BSTR_TO_RSTR( *v ) ) ); + } + return r; +} + +#define RBHTP_R_STRING_TABLE( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return rbhtp_r_string_table( x->N ); \ + } + +// We don't push the keys as they are duplicated in the header. +static VALUE rbhtp_r_header_table( htp_table_t* table ) +{ + if ( table == NULL ) return Qnil; + htp_header_t* v; + VALUE r = rb_ary_new(); + + for (int i = 0, n = htp_table_size(table); i < n; i++) { + v = htp_table_get_index(table, i, NULL); + rb_ary_push( r, + rb_funcall( cHeader, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, 0, v ) ) ); + } + + return r; +} + +#define RBHTP_R_HEADER_TABLE( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return rbhtp_r_header_table( x->N ); \ + } + +static VALUE rbhtp_r_header_line_list( htp_list_t* list ) +{ + if ( list == NULL ) return Qnil; + VALUE r = rb_ary_new(); + for (int i = 0, n = htp_list_size(list); i < n; i++) { + htp_header_line_t *v = htp_list_get(list, i); + + rb_ary_push( r, + rb_funcall( cHeaderLine, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, 0, v ) ) ); + } + return r; +} + +#define RBHTP_R_HEADER_LINE_LIST( T, N ) \ + VALUE rbhtp_ ## T ## _ ## N( VALUE self ) \ + { \ + htp_ ## T ## _t* x = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@" #T ), htp_ ## T ## _t, x ); \ + return rbhtp_r_header_line_list( x->N ); \ + } + +// This function is only needed when we malloc the URI ourselves. +void rbhtp_free_uri( void* p ) +{ + htp_uri_t* uri = (htp_uri_t*)p; + free( uri ); +} + +//---- HTP --- +VALUE rbhtp_get_version( VALUE self ) +{ + return rb_str_new2( htp_get_version() ); +} + +// We return a HTP::URI and throw an exception on error. +VALUE rbhtp_parse_uri( VALUE self, VALUE input ) +{ + Check_Type( input, T_STRING ); + bstr* input_b = bstr_dup_mem( RSTRING_PTR( input ), RSTRING_LEN( input ) ); + htp_uri_t* uri = NULL; // htp_parse_uri will alloc. + + int result = htp_parse_uri( input_b, &uri ); + if ( result != HTP_OK ) { + bstr_free( input_b ); + free( uri ); + rb_raise( rb_eRuntimeError, "HTP error in htp_parse_uri: %d", result ); + return Qnil; // Ignored? + } + + bstr_free( input_b ); // Okay, as htp_parse_uri dups the data it needs. + + return rb_funcall( cURI, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, rbhtp_free_uri, uri ) + ); +} + +//---- Cfg ---- + +// Terminate list with "". +static char* const rbhtp_config_pvars[] = { + "@request_proc", + "@request_proc", + "@transaction_start", + "@request_line", + "@request_headers", + "@request_trailer", + "@response_line", + "@response_headers", + "@response_trailers", + "" +}; + +void rbhtp_config_free( void* p ) +{ + htp_cfg_t* cfg = (htp_cfg_t*)p; + htp_config_destroy( cfg ); +} + +VALUE rbhtp_config_initialize( VALUE self ) +{ + char* const* v = &rbhtp_config_pvars[0]; + while ( *v[0] != '\0' ) { + rb_iv_set( self, *v, Qnil ); + ++v; + } + + htp_cfg_t* cfg = htp_config_create(); + + rb_iv_set( self, "@cfg", + Data_Wrap_Struct( rb_cObject, 0, rbhtp_config_free, cfg ) + ); + + return Qnil; +} + +VALUE rbhtp_config_copy( VALUE self ) +{ + // We create one too many copies here. + VALUE new_config = rb_funcall( cCfg, rb_intern( "new" ), 0 ); + htp_cfg_t* cfg = NULL; + Data_Get_Struct( rb_iv_get( self, "@cfg" ), htp_cfg_t, cfg ); + + // Note that the existing new_config @cfg will be garbage collected as a + // result of this set. + + rb_iv_set( new_config, "@cfg", + Data_Wrap_Struct( rb_cObject, 0, rbhtp_config_free, + htp_config_copy( cfg ) ) ); + + // Now copy over all our callbacks. + char* const* v = &rbhtp_config_pvars[0]; + while ( *v[0] != '\0' ) { + rb_iv_set( new_config, *v, rb_iv_get( self, *v ) ); + ++v; + } + + return new_config; +} + +VALUE rbhtp_config_set_server_personality( VALUE self, VALUE personality ) +{ + Check_Type( personality, T_FIXNUM ); + + htp_cfg_t* cfg = NULL; + Data_Get_Struct( rb_iv_get( self, "@cfg" ), htp_cfg_t, cfg ); + + return INT2FIX( + htp_config_set_server_personality( cfg, FIX2INT( personality ) ) + ); +} + +VALUE rbhtp_config_register_urlencoded_parser( VALUE self ) +{ + htp_cfg_t* cfg = NULL; + Data_Get_Struct( rb_iv_get( self, "@cfg" ), htp_cfg_t, cfg ); + + htp_config_register_urlencoded_parser( cfg ); + + return Qnil; +} + +#define RBHTP_CALLBACK_SUB( N ) \ + VALUE rbhtp_config_register_ ## N( VALUE self ) \ + { \ + if ( ! rb_block_given_p() ) { \ + rb_raise( rb_eArgError, "A block is required." ); \ + return Qnil; \ + } \ + VALUE proc = rb_iv_get( self, "@" #N "_proc" ); \ + if ( proc == Qnil ) { \ + htp_cfg_t* cfg = NULL; \ + Data_Get_Struct( rb_iv_get( self, "@cfg" ), htp_cfg_t, cfg ); \ + htp_config_register_## N( cfg, rbhtp_config_callback_ ## N ); \ + } \ + rb_iv_set( self, "@" #N "_proc", rb_block_proc() ); \ + return self; \ + } + +#define RBHTP_CONNP_CALLBACK( N ) \ + int rbhtp_config_callback_ ## N( htp_connp_t* connp ) \ + { \ + VALUE userdata = (VALUE)htp_connp_get_user_data( connp ); \ + VALUE config = rb_iv_get( userdata, "@cfg" ); \ + VALUE proc = rb_iv_get( config, "@" #N "_proc" ); \ + if ( proc != Qnil ) { \ + return INT2FIX( \ + rb_funcall( proc, rb_intern( "call" ), 1, userdata ) \ + ); \ + } \ + return 1; \ + } \ + RBHTP_CALLBACK_SUB( N ) + +// Tx data is a tx and a data block. For *_body_data callbacks we pass +// in the tx as first argument and the data as a string as the second argument. +#define RBHTP_TXDATA_CALLBACK( N ) \ + int rbhtp_config_callback_ ##N( htp_tx_data_t* txdata ) \ + { \ + htp_connp_t* connp = txdata->tx->connp; \ + VALUE userdata = (VALUE)htp_connp_get_user_data( connp ); \ + VALUE config = rb_iv_get( userdata, "@cfg" ); \ + VALUE proc = rb_iv_get( config, "@" #N "_proc" ); \ + if ( proc != Qnil ) { \ + VALUE data = Qnil; \ + if ( txdata->data ) \ + data = rb_str_new( (char*)txdata->data, txdata->len ); \ + return INT2FIX( \ + rb_funcall( proc, rb_intern( "call" ), 2, \ + rb_funcall( cTx, rb_intern( "new" ), 3, \ + Data_Wrap_Struct( rb_cObject, 0, 0, txdata->tx ), \ + config, \ + userdata \ + ), \ + data \ + ) \ + ); \ + } \ + return 1; \ + } \ + RBHTP_CALLBACK_SUB( N ) + + +RBHTP_CONNP_CALLBACK( request ) +RBHTP_CONNP_CALLBACK( response ) +RBHTP_CONNP_CALLBACK( transaction_start ) +RBHTP_CONNP_CALLBACK( request_line ) +RBHTP_CONNP_CALLBACK( request_headers ) +RBHTP_CONNP_CALLBACK( request_trailer ) +RBHTP_CONNP_CALLBACK( response_line ) +RBHTP_CONNP_CALLBACK( response_headers ) +RBHTP_CONNP_CALLBACK( response_trailer ) + +RBHTP_TXDATA_CALLBACK( request_body_data ) +RBHTP_TXDATA_CALLBACK( response_body_data ) + +RBHTP_R_INT( cfg, spersonality ) +RBHTP_RW_INT( cfg, parse_request_cookies ) + +// File data is a tx, file information, and file data. The callback thus +// takes those three as arguments. +int rbhtp_config_callback_request_file_data( htp_file_data_t* filedata ) +{ + htp_connp_t* connp = filedata->tx->connp; + VALUE userdata = (VALUE)htp_connp_get_user_data( connp ); + VALUE config = rb_iv_get( userdata, "@cfg" ); + VALUE proc = rb_iv_get( config, "@request_file_data_proc" ); + if ( proc != Qnil ) { + VALUE data = Qnil; + if ( filedata->data ) + data = rb_str_new( (char*)filedata->data, filedata->len ); + return INT2FIX( + rb_funcall( proc, rb_intern( "call" ), 2, + rb_funcall( cTx, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, 0, filedata->tx ) + ), + rb_funcall( cFile, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, 0, filedata->file ) + ), + data + ) + ); + } + return 1; +} +RBHTP_CALLBACK_SUB( request_file_data ) + +//---- Connp ---- + +void rbhtp_connp_free( void* p ) +{ + htp_connp_t* connp = (htp_connp_t*)p; + if ( connp ) + htp_connp_destroy_all( connp ); +} + +VALUE rbhtp_connp_initialize( VALUE self, VALUE config ) +{ + rb_iv_set( self, "@cfg", config ); + + htp_cfg_t* cfg = NULL; + Data_Get_Struct( rb_iv_get( config, "@cfg" ), htp_cfg_t, cfg ); + + htp_connp_t* connp = htp_connp_create( cfg ); + htp_connp_set_user_data( connp, (void*)self ); + rb_iv_set( self, "@connp", + Data_Wrap_Struct( rb_cObject, 0, rbhtp_connp_free, connp ) + ); + + return Qnil; +} + +VALUE rbhtp_connp_req_data( VALUE self, VALUE timestamp, VALUE data ) +{ + if ( strncmp( "Time", rb_class2name( CLASS_OF( timestamp ) ), 4 ) != 0 ) { + rb_raise( rb_eTypeError, "First argument must be a Time." ); + return Qnil; + } + + StringValue( data ); // try to make data a string. + Check_Type( data, T_STRING ); + + size_t len = RSTRING_LEN( data ); + char* data_c = RSTRING_PTR( data ); + + htp_time_t timestamp_c; + timestamp_c.tv_sec = + FIX2INT( rb_funcall( timestamp, rb_intern( "tv_sec" ), 0 ) ); + timestamp_c.tv_usec = + FIX2INT( rb_funcall( timestamp, rb_intern( "tv_usec" ), 0 ) ); + + VALUE connp_r = rb_iv_get( self, "@connp" ); + htp_connp_t* connp = NULL; + Data_Get_Struct( connp_r, htp_connp_t, connp ); + + int result = + htp_connp_req_data( connp, ×tamp_c, (unsigned char*)data_c, len ); + + return INT2FIX( result ); +} + +VALUE rbhtp_connp_in_tx( VALUE self ) +{ + VALUE connp_r = rb_iv_get( self, "@connp" ); + VALUE config = rb_iv_get( self, "@cfg" ); + htp_connp_t* connp = NULL; + Data_Get_Struct( connp_r, htp_connp_t, connp ); + + if ( connp->in_tx == NULL ) + return Qnil; + + return rb_funcall( cTx, rb_intern( "new" ), 3, + Data_Wrap_Struct( rb_cObject, 0, 0, connp->in_tx ), + config, + self + ); +} + +VALUE rbhtp_connp_conn( VALUE self ) +{ + htp_connp_t* connp = NULL; + Data_Get_Struct( rb_iv_get( self, "@connp" ), htp_connp_t, connp ); + if ( connp->conn == NULL ) + return Qnil; + return rb_funcall( cConn, rb_intern( "new" ), 2, + Data_Wrap_Struct( rb_cObject, 0, 0, connp->conn ), + self + ); +} + +// Unlike Connp and Cfg, these are just wrapper. The lifetime of the +// underlying objects are bound to the Connp. + +//---- Header ---- +VALUE rbhtp_header_initialize( VALUE self, VALUE raw_header ) +{ + rb_iv_set( self, "@header", raw_header ); + return Qnil; +} + +RBHTP_R_STRING( header, name ); +RBHTP_R_STRING( header, value ); +RBHTP_R_INT( header, flags ); + +// ---- Header Line ---- +VALUE rbhtp_header_line_initialize( VALUE self, VALUE raw_header_line ) +{ + rb_iv_set( self, "@header_line", raw_header_line ); + return Qnil; +} + +VALUE rbhtp_header_line_header( VALUE self ) +{ + htp_header_line_t* hline = NULL; + Data_Get_Struct( rb_iv_get( self, "@header_line" ), htp_header_line_t, hline ); + + if ( hline->header == NULL ) + return Qnil; + + return rb_funcall( cHeader, rb_intern( "new" ), 1, + Data_Wrap_Struct( rb_cObject, 0, 0, hline->header ) + ); +} + +RBHTP_R_STRING( header_line, line ); +RBHTP_R_INT( header_line, name_offset ); +RBHTP_R_INT( header_line, name_len ); +RBHTP_R_INT( header_line, value_offset ); +RBHTP_R_INT( header_line, value_len ); +RBHTP_R_INT( header_line, has_nulls ); +RBHTP_R_INT( header_line, first_nul_offset ); +RBHTP_R_INT( header_line, flags ); + +// ---- URI ---- +VALUE rbhtp_uri_initialize( VALUE self, VALUE raw_uri ) +{ + rb_iv_set( self, "@uri", raw_uri ); + return Qnil; +} + +RBHTP_R_STRING( uri, scheme ); +RBHTP_R_STRING( uri, username ); +RBHTP_R_STRING( uri, password ); +RBHTP_R_STRING( uri, hostname ); +RBHTP_R_STRING( uri, port ); +RBHTP_R_INT( uri, port_number ); +RBHTP_R_STRING( uri, path ); +RBHTP_R_STRING( uri, query ); +RBHTP_R_STRING( uri, fragment ); + +//---- Tx ---- + +VALUE rbhtp_tx_initialize( + VALUE self, + VALUE raw_txn, + VALUE cfg, + VALUE connp ) +{ + rb_iv_set( self, "@tx", raw_txn ); + rb_iv_set( self, "@cfg", cfg ); + rb_iv_set( self, "@connp", connp ); + + return Qnil; +} + +RBHTP_R_INT( tx, request_ignored_lines ) +RBHTP_R_INT( tx, request_line_nul ) +RBHTP_R_INT( tx, request_line_nul_offset ) +RBHTP_R_INT( tx, request_method_number ) +RBHTP_R_INT( tx, request_protocol_number ) +RBHTP_R_INT( tx, protocol_is_simple ) +RBHTP_R_INT( tx, request_message_len ) +RBHTP_R_INT( tx, request_entity_len ) +RBHTP_R_INT( tx, request_nonfiledata_len ) +RBHTP_R_INT( tx, request_filedata_len ) +RBHTP_R_INT( tx, request_header_lines_no_trailers ) +RBHTP_R_INT( tx, request_headers_raw_lines ) +RBHTP_R_INT( tx, request_transfer_coding ) +RBHTP_R_INT( tx, request_content_encoding ) +RBHTP_R_INT( tx, request_params_query_reused ) +RBHTP_R_INT( tx, request_params_body_reused ) +RBHTP_R_INT( tx, request_auth_type ) +RBHTP_R_INT( tx, response_ignored_lines ) +RBHTP_R_INT( tx, response_protocol_number ) +RBHTP_R_INT( tx, response_status_number ) +RBHTP_R_INT( tx, response_status_expected_number ) +RBHTP_R_INT( tx, seen_100continue ) +RBHTP_R_INT( tx, response_message_len ) +RBHTP_R_INT( tx, response_entity_len ) +RBHTP_R_INT( tx, response_transfer_coding ) +RBHTP_R_INT( tx, response_content_encoding ) +RBHTP_R_INT( tx, flags ) +RBHTP_R_INT( tx, progress ) + +RBHTP_R_STRING( tx, request_method ) +RBHTP_R_STRING( tx, request_line ) +RBHTP_R_STRING( tx, request_uri ) +RBHTP_R_STRING( tx, request_uri_normalized ) +RBHTP_R_STRING( tx, request_protocol ) +RBHTP_R_STRING( tx, request_headers_raw ) +RBHTP_R_STRING( tx, request_headers_sep ) +RBHTP_R_STRING( tx, request_content_type ) +RBHTP_R_STRING( tx, request_auth_username ) +RBHTP_R_STRING( tx, request_auth_password ) +RBHTP_R_STRING( tx, response_line ) +RBHTP_R_STRING( tx, response_protocol ) +RBHTP_R_STRING( tx, response_status ) +RBHTP_R_STRING( tx, response_message ) +RBHTP_R_STRING( tx, response_headers_sep ) + +RBHTP_R_STRING_TABLE( tx, request_params_query ) +RBHTP_R_STRING_TABLE( tx, request_params_body ) +RBHTP_R_STRING_TABLE( tx, request_cookies ) +RBHTP_R_HEADER_TABLE( tx, request_headers ) +RBHTP_R_HEADER_TABLE( tx, response_headers ) + +RBHTP_R_HEADER_LINE_LIST( tx, request_header_lines ); +RBHTP_R_HEADER_LINE_LIST( tx, response_header_lines ); + +RBHTP_R_URI( tx, parsed_uri ) +RBHTP_R_URI( tx, parsed_uri_incomplete ) + +VALUE rbhtp_tx_conn( VALUE self ) +{ + htp_tx_t* tx = NULL; + Data_Get_Struct( rb_iv_get( self, "@tx" ), htp_tx_t, tx ); + if ( tx->conn == NULL ) + return Qnil; + return rb_funcall( cConn, rb_intern( "new" ), 2, + Data_Wrap_Struct( rb_cObject, 0, 0, tx->conn ), + rb_iv_get( self, "@connp" ) + ); +} + +// ---- File ---- +VALUE rbhtp_file_initialize( VALUE self, VALUE raw_file ) +{ + rb_iv_set( self, "@file", raw_file ); + return Qnil; +} + +RBHTP_R_INT( file, source ) +RBHTP_R_STRING( file, filename ) +RBHTP_R_INT( file, len ) +RBHTP_R_CSTR( file, tmpname ) +RBHTP_R_INT( file, fd ) + +// ---- Conn ---- +VALUE rbhtp_conn_initialize( VALUE self, VALUE raw_conn, VALUE connp ) +{ + rb_iv_set( self, "@conn", raw_conn ); + rb_iv_set( self, "@connp", connp ); + return Qnil; +} + +RBHTP_R_CSTR( conn, remote_addr ) +RBHTP_R_INT( conn, remote_port ) +RBHTP_R_CSTR( conn, local_addr ) +RBHTP_R_INT( conn, local_port ) +RBHTP_R_INT( conn, flags ) +RBHTP_R_INT( conn, in_data_counter ) +RBHTP_R_INT( conn, out_data_counter ) +RBHTP_R_INT( conn, in_packet_counter ) +RBHTP_R_INT( conn, out_packet_counter ) +RBHTP_R_TV( conn, open_timestamp ) +RBHTP_R_TV( conn, close_timestamp ) + +VALUE rbhtp_conn_transactions( VALUE self ) +{ + htp_conn_t* conn = NULL; + Data_Get_Struct( rb_iv_get( self, "@conn" ), htp_conn_t, conn ); + + if ( conn->transactions == NULL ) return Qnil; + + VALUE connp = rb_iv_get( self, "@connp" ); + VALUE cfg = rb_iv_get( connp, "@cfg" ); + + VALUE r = rb_ary_new(); + + for (int i = 0, n = htp_list_size(conn->transactions); i < n; i++) { + htp_tx_t *v = htp_list_get(conn->transactions, i); + + rb_ary_push( r, + rb_funcall( cTx, rb_intern( "new" ), 3, + Data_Wrap_Struct( rb_cObject, 0, 0, v ), + cfg, + connp + ) + ); + } + return r; +} + +//---- Init ---- +void Init_htp( void ) +{ + mHTP = rb_define_module( "HTP" ); + + rb_define_singleton_method( mHTP, "get_version", rbhtp_get_version, 0 ); + rb_define_singleton_method( mHTP, "parse_uri", rbhtp_parse_uri, 1 ); + + // All numeric constants from htp.h. + rb_define_const( mHTP, "HTP_ERROR", INT2FIX( HTP_ERROR ) ); + rb_define_const( mHTP, "HTP_OK", INT2FIX( HTP_OK ) ); + rb_define_const( mHTP, "HTP_DATA", INT2FIX( HTP_DATA ) ); + rb_define_const( mHTP, "HTP_DATA_OTHER", INT2FIX( HTP_DATA_OTHER ) ); + rb_define_const( mHTP, "HTP_DECLINED", INT2FIX( HTP_DECLINED ) ); + rb_define_const( mHTP, "PROTOCOL_UNKNOWN", INT2FIX( HTP_PROTOCOL_UNKNOWN ) ); + rb_define_const( mHTP, "HTTP_0_9", INT2FIX( HTP_PROTOCOL_0_9 ) ); + rb_define_const( mHTP, "HTTP_1_0", INT2FIX( HTP_PROTOCOL_1_0 ) ); + rb_define_const( mHTP, "HTTP_1_1", INT2FIX( HTP_PROTOCOL_1_1 ) ); + rb_define_const( mHTP, "HTP_LOG_ERROR", INT2FIX( HTP_LOG_ERROR ) ); + rb_define_const( mHTP, "HTP_LOG_WARNING", INT2FIX( HTP_LOG_WARNING ) ); + rb_define_const( mHTP, "HTP_LOG_NOTICE", INT2FIX( HTP_LOG_NOTICE ) ); + rb_define_const( mHTP, "HTP_LOG_INFO", INT2FIX( HTP_LOG_INFO ) ); + rb_define_const( mHTP, "HTP_LOG_DEBUG", INT2FIX( HTP_LOG_DEBUG ) ); + rb_define_const( mHTP, "HTP_LOG_DEBUG2", INT2FIX( HTP_LOG_DEBUG2 ) ); + rb_define_const( mHTP, "HTP_HEADER_MISSING_COLON", INT2FIX( HTP_HEADER_MISSING_COLON ) ); + rb_define_const( mHTP, "HTP_HEADER_INVALID_NAME", INT2FIX( HTP_HEADER_INVALID_NAME ) ); + rb_define_const( mHTP, "HTP_HEADER_LWS_AFTER_FIELD_NAME", INT2FIX( HTP_HEADER_LWS_AFTER_FIELD_NAME ) ); + rb_define_const( mHTP, "HTP_LINE_TOO_LONG_HARD", INT2FIX( HTP_LINE_TOO_LONG_HARD ) ); + rb_define_const( mHTP, "HTP_LINE_TOO_LONG_SOFT", INT2FIX( HTP_LINE_TOO_LONG_SOFT ) ); + rb_define_const( mHTP, "HTP_HEADER_LIMIT_HARD", INT2FIX( HTP_HEADER_LIMIT_HARD ) ); + rb_define_const( mHTP, "HTP_HEADER_LIMIT_SOFT", INT2FIX( HTP_HEADER_LIMIT_SOFT ) ); + rb_define_const( mHTP, "HTP_VALID_STATUS_MIN", INT2FIX( HTP_VALID_STATUS_MIN ) ); + rb_define_const( mHTP, "HTP_VALID_STATUS_MAX", INT2FIX( HTP_VALID_STATUS_MAX ) ); + rb_define_const( mHTP, "M_UNKNOWN", INT2FIX( M_UNKNOWN ) ); + rb_define_const( mHTP, "M_GET", INT2FIX( M_GET ) ); + rb_define_const( mHTP, "M_PUT", INT2FIX( M_PUT ) ); + rb_define_const( mHTP, "M_POST", INT2FIX( M_POST ) ); + rb_define_const( mHTP, "M_DELETE", INT2FIX( M_DELETE ) ); + rb_define_const( mHTP, "M_CONNECT", INT2FIX( M_CONNECT ) ); + rb_define_const( mHTP, "M_OPTIONS", INT2FIX( M_OPTIONS ) ); + rb_define_const( mHTP, "M_TRACE", INT2FIX( M_TRACE ) ); + rb_define_const( mHTP, "M_PATCH", INT2FIX( M_PATCH ) ); + rb_define_const( mHTP, "M_PROPFIND", INT2FIX( M_PROPFIND ) ); + rb_define_const( mHTP, "M_PROPPATCH", INT2FIX( M_PROPPATCH ) ); + rb_define_const( mHTP, "M_MKCOL", INT2FIX( M_MKCOL ) ); + rb_define_const( mHTP, "M_COPY", INT2FIX( M_COPY ) ); + rb_define_const( mHTP, "M_MOVE", INT2FIX( M_MOVE ) ); + rb_define_const( mHTP, "M_LOCK", INT2FIX( M_LOCK ) ); + rb_define_const( mHTP, "M_UNLOCK", INT2FIX( M_UNLOCK ) ); + rb_define_const( mHTP, "M_VERSION_CONTROL", INT2FIX( M_VERSION_CONTROL ) ); + rb_define_const( mHTP, "M_CHECKOUT", INT2FIX( M_CHECKOUT ) ); + rb_define_const( mHTP, "M_UNCHECKOUT", INT2FIX( M_UNCHECKOUT ) ); + rb_define_const( mHTP, "M_CHECKIN", INT2FIX( M_CHECKIN ) ); + rb_define_const( mHTP, "M_UPDATE", INT2FIX( M_UPDATE ) ); + rb_define_const( mHTP, "M_LABEL", INT2FIX( M_LABEL ) ); + rb_define_const( mHTP, "M_REPORT", INT2FIX( M_REPORT ) ); + rb_define_const( mHTP, "M_MKWORKSPACE", INT2FIX( M_MKWORKSPACE ) ); + rb_define_const( mHTP, "M_MKACTIVITY", INT2FIX( M_MKACTIVITY ) ); + rb_define_const( mHTP, "M_BASELINE_CONTROL", INT2FIX( M_BASELINE_CONTROL ) ); + rb_define_const( mHTP, "M_MERGE", INT2FIX( M_MERGE ) ); + rb_define_const( mHTP, "M_INVALID", INT2FIX( M_INVALID ) ); + rb_define_const( mHTP, "M_HEAD", INT2FIX( HTP_M_HEAD ) ); + rb_define_const( mHTP, "HTP_FIELD_UNPARSEABLE", INT2FIX( HTP_FIELD_UNPARSEABLE ) ); + rb_define_const( mHTP, "HTP_FIELD_INVALID", INT2FIX( HTP_FIELD_INVALID ) ); + rb_define_const( mHTP, "HTP_FIELD_FOLDED", INT2FIX( HTP_FIELD_FOLDED ) ); + rb_define_const( mHTP, "HTP_FIELD_REPEATED", INT2FIX( HTP_FIELD_REPEATED ) ); + rb_define_const( mHTP, "HTP_FIELD_LONG", INT2FIX( HTP_FIELD_LONG ) ); + rb_define_const( mHTP, "HTP_FIELD_NUL_BYTE", INT2FIX( HTP_FIELD_RAW_NUL ) ); + rb_define_const( mHTP, "HTP_REQUEST_SMUGGLING", INT2FIX( HTP_REQUEST_SMUGGLING ) ); + rb_define_const( mHTP, "HTP_INVALID_FOLDING", INT2FIX( HTP_INVALID_FOLDING ) ); + rb_define_const( mHTP, "HTP_INVALID_CHUNKING", INT2FIX( HTP_REQUEST_INVALID_T_E ) ); + rb_define_const( mHTP, "HTP_MULTI_PACKET_HEAD", INT2FIX( HTP_MULTI_PACKET_HEAD ) ); + rb_define_const( mHTP, "HTP_HOST_MISSING", INT2FIX( HTP_HOST_MISSING ) ); + rb_define_const( mHTP, "HTP_AMBIGUOUS_HOST", INT2FIX( HTP_HOST_AMBIGUOUS ) ); + rb_define_const( mHTP, "HTP_PATH_ENCODED_NUL", INT2FIX( HTP_PATH_ENCODED_NUL ) ); + rb_define_const( mHTP, "HTP_PATH_INVALID_ENCODING", INT2FIX( HTP_PATH_INVALID_ENCODING ) ); + rb_define_const( mHTP, "HTP_PATH_INVALID", INT2FIX( HTP_PATH_INVALID ) ); + rb_define_const( mHTP, "HTP_PATH_OVERLONG_U", INT2FIX( HTP_PATH_OVERLONG_U ) ); + rb_define_const( mHTP, "HTP_PATH_ENCODED_SEPARATOR", INT2FIX( HTP_PATH_ENCODED_SEPARATOR ) ); + rb_define_const( mHTP, "HTP_PATH_UTF8_VALID", INT2FIX( HTP_PATH_UTF8_VALID ) ); + rb_define_const( mHTP, "HTP_PATH_UTF8_INVALID", INT2FIX( HTP_PATH_UTF8_INVALID ) ); + rb_define_const( mHTP, "HTP_PATH_UTF8_OVERLONG", INT2FIX( HTP_PATH_UTF8_OVERLONG ) ); + rb_define_const( mHTP, "HTP_PATH_FULLWIDTH_EVASION", INT2FIX( HTP_PATH_HALF_FULL_RANGE ) ); + rb_define_const( mHTP, "HTP_STATUS_LINE_INVALID", INT2FIX( HTP_STATUS_LINE_INVALID ) ); + rb_define_const( mHTP, "PIPELINED_CONNECTION", INT2FIX( HTP_CONN_PIPELINED ) ); + rb_define_const( mHTP, "HTP_SERVER_MINIMAL", INT2FIX( HTP_SERVER_MINIMAL ) ); + rb_define_const( mHTP, "HTP_SERVER_GENERIC", INT2FIX( HTP_SERVER_GENERIC ) ); + rb_define_const( mHTP, "HTP_SERVER_IDS", INT2FIX( HTP_SERVER_IDS ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_4_0", INT2FIX( HTP_SERVER_IIS_4_0 ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_5_0", INT2FIX( HTP_SERVER_IIS_5_0 ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_5_1", INT2FIX( HTP_SERVER_IIS_5_1 ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_6_0", INT2FIX( HTP_SERVER_IIS_6_0 ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_7_0", INT2FIX( HTP_SERVER_IIS_7_0 ) ); + rb_define_const( mHTP, "HTP_SERVER_IIS_7_5", INT2FIX( HTP_SERVER_IIS_7_5 ) ); + rb_define_const( mHTP, "HTP_SERVER_TOMCAT_6_0", INT2FIX( HTP_SERVER_TOMCAT_6_0 ) ); + rb_define_const( mHTP, "HTP_SERVER_APACHE", INT2FIX( HTP_SERVER_APACHE ) ); + rb_define_const( mHTP, "HTP_SERVER_APACHE_2_2", INT2FIX( HTP_SERVER_APACHE_2_2 ) ); + rb_define_const( mHTP, "NONE", INT2FIX( HTP_AUTH_NONE ) ); + rb_define_const( mHTP, "IDENTITY", INT2FIX( HTP_CODING_IDENTITY ) ); + rb_define_const( mHTP, "CHUNKED", INT2FIX( HTP_CODING_CHUNKED ) ); + rb_define_const( mHTP, "TX_PROGRESS_NEW", INT2FIX( HTP_REQUEST_NOT_STARTED ) ); + rb_define_const( mHTP, "TX_PROGRESS_REQ_LINE", INT2FIX( HTP_REQUEST_LINE ) ); + rb_define_const( mHTP, "TX_PROGRESS_REQ_HEADERS", INT2FIX( HTP_REQUEST_HEADERS ) ); + rb_define_const( mHTP, "TX_PROGRESS_REQ_BODY", INT2FIX( HTP_REQUEST_BODY ) ); + rb_define_const( mHTP, "TX_PROGRESS_REQ_TRAILER", INT2FIX( HTP_REQUEST_TRAILER ) ); + rb_define_const( mHTP, "RESPONSE_WAIT", INT2FIX( HTP_REQUEST_COMPLETE ) ); + rb_define_const( mHTP, "TX_PROGRESS_RES_LINE", INT2FIX( HTP_RESPONSE_LINE ) ); + rb_define_const( mHTP, "RESPONSE_HEADERS", INT2FIX( HTP_RESPONSE_HEADERS ) ); + rb_define_const( mHTP, "RESPONSE_BODY", INT2FIX( HTP_RESPONSE_BODY ) ); + rb_define_const( mHTP, "TX_PROGRESS_RES_TRAILER", INT2FIX( HTP_RESPONSE_TRAILER ) ); + rb_define_const( mHTP, "TX_PROGRESS_COMPLETE", INT2FIX( HTP_RESPONSE_COMPLETE ) ); + rb_define_const( mHTP, "HTP_STREAM_NEW", INT2FIX( HTP_STREAM_NEW ) ); + rb_define_const( mHTP, "HTP_STREAM_OPEN", INT2FIX( HTP_STREAM_OPEN ) ); + rb_define_const( mHTP, "HTP_STREAM_CLOSED", INT2FIX( HTP_STREAM_CLOSED ) ); + rb_define_const( mHTP, "HTP_STREAM_ERROR", INT2FIX( HTP_STREAM_ERROR ) ); + rb_define_const( mHTP, "HTP_STREAM_TUNNEL", INT2FIX( HTP_STREAM_TUNNEL ) ); + rb_define_const( mHTP, "HTP_STREAM_DATA_OTHER", INT2FIX( HTP_STREAM_DATA_OTHER ) ); + rb_define_const( mHTP, "HTP_STREAM_DATA", INT2FIX( HTP_STREAM_DATA ) ); + rb_define_const( mHTP, "URL_DECODER_PRESERVE_PERCENT", INT2FIX( HTP_URL_DECODE_PRESERVE_PERCENT ) ); + rb_define_const( mHTP, "URL_DECODER_REMOVE_PERCENT", INT2FIX( HTP_URL_DECODE_REMOVE_PERCENT ) ); + rb_define_const( mHTP, "URL_DECODER_DECODE_INVALID", INT2FIX( HTP_URL_DECODE_PROCESS_INVALID ) ); + rb_define_const( mHTP, "URL_DECODER_STATUS_400", INT2FIX( HTP_URL_DECODE_STATUS_400 ) ); + rb_define_const( mHTP, "NO", INT2FIX( NO ) ); + rb_define_const( mHTP, "BESTFIT", INT2FIX( BESTFIT ) ); + rb_define_const( mHTP, "YES", INT2FIX( YES ) ); + rb_define_const( mHTP, "TERMINATE", INT2FIX( TERMINATE ) ); + rb_define_const( mHTP, "STATUS_400", INT2FIX( STATUS_400 ) ); + rb_define_const( mHTP, "STATUS_404", INT2FIX( STATUS_404 ) ); + rb_define_const( mHTP, "HTP_AUTH_NONE", INT2FIX( HTP_AUTH_NONE ) ); + rb_define_const( mHTP, "HTP_AUTH_BASIC", INT2FIX( HTP_AUTH_BASIC ) ); + rb_define_const( mHTP, "HTP_AUTH_DIGEST", INT2FIX( HTP_AUTH_DIGEST ) ); + rb_define_const( mHTP, "HTP_AUTH_UNKNOWN", INT2FIX( HTP_AUTH_UNRECOGNIZED ) ); + rb_define_const( mHTP, "HTP_FILE_MULTIPART", INT2FIX( HTP_FILE_MULTIPART ) ); + rb_define_const( mHTP, "HTP_FILE_PUT", INT2FIX( HTP_FILE_PUT ) ); + rb_define_const( mHTP, "CFG_NOT_SHARED", INT2FIX( CFG_NOT_SHARED ) ); + rb_define_const( mHTP, "CFG_SHARED", INT2FIX( CFG_SHARED ) ); + + cCfg = rb_define_class_under( mHTP, "Cfg", rb_cObject ); + rb_define_method( cCfg, "initialize", rbhtp_config_initialize, 0 ); + rb_define_method( cCfg, "copy", rbhtp_config_copy, 0 ); + + rb_define_method( cCfg, "register_response", rbhtp_config_register_response, 0 ); + rb_define_method( cCfg, "register_request", rbhtp_config_register_request, 0 ); + rb_define_method( cCfg, "register_transaction_start", rbhtp_config_register_transaction_start, 0 ); + rb_define_method( cCfg, "register_request_line", rbhtp_config_register_request_line, 0 ); + rb_define_method( cCfg, "register_request_headers", rbhtp_config_register_request_headers, 0 ); + rb_define_method( cCfg, "register_request_trailer", rbhtp_config_register_request_trailer, 0 ); + rb_define_method( cCfg, "register_response_line", rbhtp_config_register_response_line, 0 ); + rb_define_method( cCfg, "register_response_headers", rbhtp_config_register_response_headers, 0 ); + rb_define_method( cCfg, "register_response_trailer", rbhtp_config_register_response_trailer, 0 ); + + rb_define_method( cCfg, "register_urlencoded_parser", rbhtp_config_register_urlencoded_parser, 0 ); + rb_define_method( cCfg, "register_request_body_data", rbhtp_config_register_request_body_data, 0 ); + rb_define_method( cCfg, "register_response_body_data", rbhtp_config_register_request_body_data, 0 ); + rb_define_method( cCfg, "register_request_file_data", rbhtp_config_register_request_file_data, 0 ); + + // server_personality= and server_personality are defined in htp_ruby.rb + rb_define_method( cCfg, "set_server_personality", rbhtp_config_set_server_personality, 1 ); + rb_define_method( cCfg, "spersonality", rbhtp_cfg_spersonality, 0 ); + + rb_define_method( cCfg, "parse_request_cookies", rbhtp_cfg_parse_request_cookies, 0 ); + rb_define_method( cCfg, "parse_request_cookies=", rbhtp_cfg_parse_request_cookies_set, 1 ); + // TODO: Much more to add. + + cConnp = rb_define_class_under( mHTP, "Connp", rb_cObject ); + rb_define_method( cConnp, "initialize", rbhtp_connp_initialize, 1 ); + rb_define_method( cConnp, "req_data", rbhtp_connp_req_data, 2 ); + rb_define_method( cConnp, "in_tx", rbhtp_connp_in_tx, 0 ); + rb_define_method( cConnp, "conn", rbhtp_connp_conn, 0 ); + // TODO: Much more to Add. + + cHeader = rb_define_class_under( mHTP, "Header", rb_cObject ); + rb_define_method( cHeader, "initialize", rbhtp_header_initialize, 1 ); + rb_define_method( cHeader, "name", rbhtp_header_name, 0 ); + rb_define_method( cHeader, "value", rbhtp_header_value, 0 ); + rb_define_method( cHeader, "flags", rbhtp_header_flags, 0 ); + + cHeaderLine = rb_define_class_under( mHTP, "HeaderLine", rb_cObject ); + rb_define_method( cHeaderLine, "initialize", rbhtp_header_line_initialize, 1 ); + rb_define_method( cHeaderLine, "header", rbhtp_header_line_header, 0 ); + rb_define_method( cHeaderLine, "line", rbhtp_header_line_line, 0 ); + rb_define_method( cHeaderLine, "name_offset", rbhtp_header_line_name_offset, 0 ); + rb_define_method( cHeaderLine, "name_len", rbhtp_header_line_name_len, 0 ); + rb_define_method( cHeaderLine, "value_offset", rbhtp_header_line_value_offset, 0 ); + rb_define_method( cHeaderLine, "value_len", rbhtp_header_line_value_len, 0 ); + rb_define_method( cHeaderLine, "has_nulls", rbhtp_header_line_has_nulls, 0 ); + rb_define_method( cHeaderLine, "first_nul_offset", rbhtp_header_line_first_nul_offset, 0 ); + rb_define_method( cHeaderLine, "flags", rbhtp_header_line_flags, 0 ); + + cURI = rb_define_class_under( mHTP, "URI", rb_cObject ); + rb_define_method( cURI, "initialize", rbhtp_uri_initialize, 1 ); + + rb_define_method( cURI, "scheme", rbhtp_uri_scheme, 0 ); + rb_define_method( cURI, "username", rbhtp_uri_username, 0 ); + rb_define_method( cURI, "password", rbhtp_uri_password, 0 ); + rb_define_method( cURI, "hostname", rbhtp_uri_hostname, 0 ); + rb_define_method( cURI, "port", rbhtp_uri_port, 0 ); + rb_define_method( cURI, "port_number", rbhtp_uri_port_number, 0 ); + rb_define_method( cURI, "path", rbhtp_uri_path, 0 ); + rb_define_method( cURI, "query", rbhtp_uri_query, 0 ); + rb_define_method( cURI, "fragment", rbhtp_uri_fragment, 0 ); + + cTx = rb_define_class_under( mHTP, "Tx", rb_cObject ); + rb_define_method( cTx, "initialize", rbhtp_tx_initialize, 3 ); + + rb_define_method( cTx, "request_ignored_lines", rbhtp_tx_request_ignored_lines, 0 ); + rb_define_method( cTx, "request_line_nul", rbhtp_tx_request_line_nul, 0 ); + rb_define_method( cTx, "request_line_nul_offset", rbhtp_tx_request_line_nul_offset, 0 ); + rb_define_method( cTx, "request_method_number", rbhtp_tx_request_method_number, 0 ); + rb_define_method( cTx, "request_line", rbhtp_tx_request_line, 0 ); + rb_define_method( cTx, "request_method", rbhtp_tx_request_method, 0 ); + rb_define_method( cTx, "request_uri", rbhtp_tx_request_uri, 0 ); + rb_define_method( cTx, "request_uri_normalized", rbhtp_tx_request_uri_normalized, 0 ); + rb_define_method( cTx, "request_protocol", rbhtp_tx_request_protocol, 0 ); + rb_define_method( cTx, "request_headers_raw", rbhtp_tx_request_headers_raw, 0 ); + rb_define_method( cTx, "request_headers_sep", rbhtp_tx_request_headers_sep, 0 ); + rb_define_method( cTx, "request_content_type", rbhtp_tx_request_content_type, 0 ); + rb_define_method( cTx, "request_auth_username", rbhtp_tx_request_auth_username, 0 ); + rb_define_method( cTx, "request_auth_password", rbhtp_tx_request_auth_password, 0 ); + rb_define_method( cTx, "response_line", rbhtp_tx_response_line, 0 ); + rb_define_method( cTx, "response_protocol", rbhtp_tx_response_protocol, 0 ); + rb_define_method( cTx, "response_status", rbhtp_tx_response_status, 0 ); + rb_define_method( cTx, "response_message", rbhtp_tx_response_message, 0 ); + rb_define_method( cTx, "response_headers_sep", rbhtp_tx_response_headers_sep, 0 ); + rb_define_method( cTx, "request_protocol_number", rbhtp_tx_request_protocol_number, 0 ); + rb_define_method( cTx, "protocol_is_simple", rbhtp_tx_protocol_is_simple, 0 ); + rb_define_method( cTx, "request_message_len", rbhtp_tx_request_message_len, 0 ); + rb_define_method( cTx, "request_entity_len", rbhtp_tx_request_entity_len, 0 ); + rb_define_method( cTx, "request_nonfiledata_len", rbhtp_tx_request_nonfiledata_len, 0 ); + rb_define_method( cTx, "request_filedata_len", rbhtp_tx_request_filedata_len, 0 ); + rb_define_method( cTx, "request_header_lines_no_trailers", rbhtp_tx_request_header_lines_no_trailers, 0 ); + rb_define_method( cTx, "request_headers_raw_lines", rbhtp_tx_request_headers_raw_lines, 0 ); + rb_define_method( cTx, "request_transfer_coding", rbhtp_tx_request_transfer_coding, 0 ); + rb_define_method( cTx, "request_content_encoding", rbhtp_tx_request_content_encoding, 0 ); + rb_define_method( cTx, "request_params_query_reused", rbhtp_tx_request_params_query_reused, 0 ); + rb_define_method( cTx, "request_params_body_reused", rbhtp_tx_request_params_body_reused, 0 ); + rb_define_method( cTx, "request_auth_type", rbhtp_tx_request_auth_type, 0 ); + rb_define_method( cTx, "response_ignored_lines", rbhtp_tx_response_ignored_lines, 0 ); + rb_define_method( cTx, "response_protocol_number", rbhtp_tx_response_protocol_number, 0 ); + rb_define_method( cTx, "response_status_number", rbhtp_tx_response_status_number, 0 ); + rb_define_method( cTx, "response_status_expected_number", rbhtp_tx_response_status_expected_number, 0 ); + rb_define_method( cTx, "seen_100continue", rbhtp_tx_seen_100continue, 0 ); + rb_define_method( cTx, "response_message_len", rbhtp_tx_response_message_len, 0 ); + rb_define_method( cTx, "response_entity_len", rbhtp_tx_response_entity_len, 0 ); + rb_define_method( cTx, "response_transfer_coding", rbhtp_tx_response_transfer_coding, 0 ); + rb_define_method( cTx, "response_content_encoding", rbhtp_tx_response_content_encoding, 0 ); + rb_define_method( cTx, "flags", rbhtp_tx_flags, 0 ); + rb_define_method( cTx, "progress", rbhtp_tx_progress, 0 ); + + rb_define_method( cTx, "request_params_query", rbhtp_tx_request_params_query, 0 ); + rb_define_method( cTx, "request_params_body", rbhtp_tx_request_params_body, 0 ); + rb_define_method( cTx, "request_cookies", rbhtp_tx_request_cookies, 0 ); + rb_define_method( cTx, "request_headers", rbhtp_tx_request_headers, 0 ); + rb_define_method( cTx, "response_headers", rbhtp_tx_response_headers, 0 ); + + rb_define_method( cTx, "request_header_lines", rbhtp_tx_request_header_lines, 0 ); + rb_define_method( cTx, "response_header_lines", rbhtp_tx_response_header_lines, 0 ); + + rb_define_method( cTx, "parsed_uri", rbhtp_tx_parsed_uri, 0 ); + rb_define_method( cTx, "parsed_uri_incomplete", rbhtp_tx_parsed_uri_incomplete, 0 ); + + rb_define_method( cTx, "conn", rbhtp_tx_conn, 0 ); + + cFile = rb_define_class_under( mHTP, "File", rb_cObject ); + rb_define_method( cFile, "initialize", rbhtp_file_initialize, 1 ); + + rb_define_method( cFile, "source", rbhtp_file_source, 0 ); + rb_define_method( cFile, "filename", rbhtp_file_filename, 0 ); + rb_define_method( cFile, "len", rbhtp_file_len, 0 ); + rb_define_method( cFile, "tmpname", rbhtp_file_tmpname, 0 ); + rb_define_method( cFile, "fd", rbhtp_file_fd, 0 ); + + cConn = rb_define_class_under( mHTP, "Conn", rb_cObject ); + rb_define_method( cConn, "initialize", rbhtp_conn_initialize, 2 ); + + rb_define_method( cConn, "remote_addr", rbhtp_conn_remote_addr, 0 ); + rb_define_method( cConn, "remote_port", rbhtp_conn_remote_port, 0 ); + rb_define_method( cConn, "local_addr", rbhtp_conn_local_addr, 0 ); + rb_define_method( cConn, "local_port", rbhtp_conn_local_port, 0 ); + rb_define_method( cConn, "flags", rbhtp_conn_flags, 0 ); + rb_define_method( cConn, "in_data_counter", rbhtp_conn_in_data_counter, 0 ); + rb_define_method( cConn, "out_data_counter", rbhtp_conn_out_data_counter, 0 ); + rb_define_method( cConn, "in_packet_counter", rbhtp_conn_in_packet_counter, 0 ); + rb_define_method( cConn, "out_packet_counter", rbhtp_conn_out_packet_counter, 0 ); + rb_define_method( cConn, "transactions", rbhtp_conn_transactions, 0 ); + rb_define_method( cConn, "open_timestamp", rbhtp_conn_open_timestamp, 0 ); + rb_define_method( cConn, "close_timestamp", rbhtp_conn_close_timestamp, 0 ); + + // Load ruby code. + rb_require( "htp_ruby" ); +} diff --git a/extras/ruby/README b/extras/ruby/README new file mode 100644 index 0000000..f946c41 --- /dev/null +++ b/extras/ruby/README @@ -0,0 +1,58 @@ += Introduction = + +Here are ruby bindings for libHTP. This project was intended for rapid +prototyping (and as an exercise for learning libHTP) and is not intended for +production use. + +The library provides a partial interface to libHTP which, where it exists, +closely matches the C interface. The main classes are HTP::Cfg and +HTP::Connp which correspond to htp_config_t and htp_connp, respectively. +Functions that begin htp_config_ and htp_connp_ are methods of HTP::Cfg and +HTP::Connp respectively. + +All callbacks are taken as blocks. E.g., + + config.register_request do |connp| + ... + end + +See example.rb. + +libHTP constants (#defines) exist as constants in HTP. + +In addition, classes exist for the other HTP structures: HTP::Tx, HTP::URI, +HTP::Header, HTP::HeaderLine, etc. These classes provide read accessors for +the various fields. In addition, some additional methods are provided for more +Rubyish style, e.g., Header#invalid?. See htp_ruby.rb for a complete list of +API additions. + +HTP::Cfg and HTP::Connp lifetimes are managed by the usual Ruby cycle, +i.e., garbage collected when no longer references. All other classes are bound +to the lifetimes of either of those classes. So, make sure you keep your +config and connection parser around as long as you need any of the data from +them or copy your data out into non-HTP classes. + +If performance is a concern, you should not be using Ruby or these bindings. +That being said, some small effort has been made to avoid binding performance +penalties for data you don't use. For example, if you never look at +Tx#request_headers, that data will not be converted into Rubyspace. A side +effect of this behavior, is that return values should be cached. E.g., +Tx#request_cookies generates an array of cookies every time it is called, so +consider caching the return value if you need to access it multiple times. + + += Missing = + +* Cfg and Connp are only minimally implemented. +* Conn#messages is missing. +* Logging is completely missing. +* Connp, and Cfg lack meaningful to_s or inspect. +* Bool support. As per C-interface, 0 and 1 are used instead of false and + true. Should add ...? accessors which latter values. +* Automated unit tests. +* API doc. +* libHTP version detection. +* Iterator parsing interface: Takes iterator of chunks and handles the various + parsing return codes. This allows the user to provide chunks as possible + via an iterator and have the parser just-work. + diff --git a/extras/ruby/example.rb b/extras/ruby/example.rb new file mode 100644 index 0000000..e802003 --- /dev/null +++ b/extras/ruby/example.rb @@ -0,0 +1,116 @@ +#!/usr/bin/env ruby + +# Copyright (c) 2009-2010 Open Information Security Foundation +# Copyright (c) 2010-2013 Qualys, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# - Neither the name of the Qualys, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +$:.unshift( File.dirname( __FILE__ ) ) + +require 'htp' + +# parse_uri example. +uri = HTP::parse_uri( "http://host.com/hello/world" ) +puts uri + +puts "----" + +# Config and Connp example. +cfg = HTP::Cfg.new + +cfg.server_personality = :apache +cfg.register_urlencoded_parser +# Comment out this line and notice that cookies vanish from output. +cfg.parse_request_cookies = 1 + +cfg.register_request do |connp| + tx = connp.in_tx + + puts "Parsed URI: " + puts " " + tx.parsed_uri + + if tx.request_headers + puts "Request Headers: " + tx.request_headers.each {|h| puts " " + h} + end + + if tx.request_cookies + puts "Request Cookies: " + tx.request_cookies.each {|k,v| puts " #{k} = #{v}"} + end + + if tx.request_params_query + puts "Request Params Query: " + tx.request_params_query.each {|k,v| puts " #{k} = #{v}"} + end + + if tx.request_params_body + puts "Request Body Query: " + tx.request_params_body.each {|k,v| puts " #{k} = #{v}"} + end + + 0 +end + +cfg.register_request_body_data do |tx,data| + puts "Body Data: #{data}" + + 0 +end + +cfg.register_request_file_data do |tx,fileinfo,data| + puts "File Data for #{fileinfo}: #{data}" + + 0 +end + +connp = HTP::Connp.new( cfg ) +input = DATA.read + +connp.req_data( Time.now, input ) + +# Non-Callback Interface. +puts "----" + +connp.conn.transactions.each do |tx| + # Might be an empty transaction. + next if ! tx.request_line + puts tx +end + +__END__ +POST http://user@password:host/%61/b/c?foo=bar#hi HTTP/1.1 +User-Agent: Mozilla +Cookie: foo=bar +Content-Type: text/plain +Content-Length: 9 + +Body Text + + diff --git a/extras/ruby/extconf.rb b/extras/ruby/extconf.rb new file mode 100644 index 0000000..ad66ab0 --- /dev/null +++ b/extras/ruby/extconf.rb @@ -0,0 +1,6 @@ +require 'mkmf' + +dir_config( 'htp' ) +have_library( 'htp', 'htp_connp_create' ) || abort( "Can't find HTP library." ) +have_header( 'htp/htp.h' ) || abort( "Can't find htp.h" ) +create_makefile( 'htp' ) diff --git a/extras/ruby/htp.gemspec b/extras/ruby/htp.gemspec new file mode 100644 index 0000000..37223de --- /dev/null +++ b/extras/ruby/htp.gemspec @@ -0,0 +1,12 @@ +Gem::Specification.new do |s| + s.name = "htp" + s.version = "0.1" + + s.authors = ["Chrustopher Alfeld"] + s.description = "Ruby Bindings for libHTP." + s.email = "calfeld@qualys.com" + s.files = ["htp_ruby.rb", "HTP.c", "extconf.rb", "example.rb"] + s.extensions = ["extconf.rb"] + s.summary = "libHTP Ruby bindings." + s.require_path = '.' +end diff --git a/extras/ruby/htp_ruby.rb b/extras/ruby/htp_ruby.rb new file mode 100644 index 0000000..0eccaac --- /dev/null +++ b/extras/ruby/htp_ruby.rb @@ -0,0 +1,247 @@ +# Copyright (c) 2009-2010 Open Information Security Foundation +# Copyright (c) 2010-2013 Qualys, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. + +# - Neither the name of the Qualys, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Author: Christopher Alfeld <calfeld@qualys.com> + +module HTP + # TODO: Lots to do. Good inspect for all classes would be a good start. + # As would an easier parsing interface that takes care of the return codes. + + class Cfg + # Object.dup will just create a Config that points to the same underlying + # htp_cfg_t. By using #copy which maps to htp_config_copy, we can do + # the expected dup behavior. + alias :dup :copy + + SERVER_PERSONALITY_ASSOC = [ + [ :minimal, HTP_SERVER_MINIMAL ], + [ :generic, HTP_SERVER_GENERIC ], + [ :ids, HTP_SERVER_IDS ], + [ :iis_4_0, HTP_SERVER_IIS_4_0 ], + [ :iis_5_0, HTP_SERVER_IIS_5_0 ], + [ :iis_5_1, HTP_SERVER_IIS_5_1 ], + [ :iis_6_0, HTP_SERVER_IIS_6_0 ], + [ :iis_7_0, HTP_SERVER_IIS_7_0 ], + [ :iis_7_5, HTP_SERVER_IIS_7_5 ], + [ :tomcat_6_0, HTP_SERVER_TOMCAT_6_0 ], + [ :apache, HTP_SERVER_APACHE ], + [ :apache_2_2, HTP_SERVER_APACHE_2_2 ] + ].freeze + + def server_personality + personality_id = spersonality + personality = SERVER_PERSONALITY_ASSOC.rassoc( personality_id )[0] + personality.nil? ? personality_id : personality + end + def server_personality=( personality ) + if personality.is_a?( String ) + personality = personality.to_sym + end + if personality.is_a?( Symbol ) + personality_id = SERVER_PERSONALITY_ASSOC.assoc( personality )[1] + if personality_id.nil? + raise TypeError.new( "Unknown personality: #{personality}" ) + end + personality = personality_id + end + if ! personality.is_a?( Fixnum ) + raise TypeError.new( "Can't understand personality." ) + end + set_server_personality( personality ) + end + end + + class Connp + attr_reader :cfg + end + + class Header + def invalid? + flags & HTP_FIELD_INVALID != 0 + end + + def folded? + flags & HTP_FIELD_FOLDED != 0 + end + + def repeated? + flags & HTP_FIELD_REPEATED != 0 + end + + def to_s + r = "#{name}: #{value}" + r += " <INVALID>" if invalid? + r += " <FOLDER>" if folded? + r += " <REPEATED>" if repeated? + r + end + + alias :inspect :to_s + alias :to_str :to_s + end + + class HeaderLine + def invalid? + flags & HTP_FIELD_INVALID != 0 + end + + def long? + flags & HTP_FIELD_LONG != 0 + end + + def nul_byte? + flags & HTP_FIELD_NUL_BYTE != 0 + end + + def to_s + line + end + + alias :inspect :to_s + alias :to_str :to_s + end + + class URI + def to_s + if hostname + "http://" + + ( username ? username : '' ) + + ( password ? ":#{password}" : '' ) + + ( hostname && ( username || password ) ? '@' : '' ) + + ( hostname ? "#{hostname}:#{port}" : '' ) + else + '' + end + + ( path ? path : '' ) + + ( query ? "?#{query}" : '' ) + + ( fragment ? "##{fragment}" : '' ) + end + + alias :inspect :to_s + alias :to_str :to_s + end + + class Tx + attr_reader :connp + attr_reader :cfg + + # Here we cache a variety of values that are built on demand. + [ + :request_params_query, + :request_params_body, + :request_cookies, + :request_headers, + :response_headers, + :request_header_lines, + :response_header_lines + ].each do |name| + raw_name = ( "_" + name.to_s ).to_sym + alias_method( raw_name, name ) + private( raw_name ) + remove_method( name ) + define_method name do + @cache ||= {} + @cache[name] ||= send( raw_name ) + end + end + + def invalid_chunking? + flags & HTP_INVALID_CHUNKING != 0 + end + + def invalid_folding? + flags & HTP_INVALID_FOLDING != 0 + end + + def request_smuggling? + flags & HTP_REQUEST_SMUGGLING != 0 + end + + def multi_packet_header? + flags & HTP_MULTI_PACKET_HEAD != 0 + end + + def field_unparseable? + flags & HTP_FIELD_UNPARSABLE != 0 + end + + def request_params_as_hash + if ! @request_params + @request_params = Hash.new {|h,k| h[k] = []} + [ request_params_query, request_params_body ].compact.each do |result| + result.each do |k,v| + @request_params[k] << v + end + end + end + @request_params + end + + def request_cookies_as_hash + if ! @request_cookies + @request_cookies = Hash.new {|h,k| h[k] = []} + result = request_cookies + if result + result.each do |k,v| + @request_cookies[k] << v + end + end + end + @request_cookies + end + + alias :to_s :request_line + alias :to_str :to_s + alias :inspect :to_s + end + + class File + alias :to_s :filename + alias :inspect :to_s + alias :to_str :to_s + end + + class Conn + attr_reader :connp + + def pipelined_connection? + flags & PIPELINED_CONNECTION + end + + def to_s + ( local_addr || "???" ) + ":#{local_port} -> " + + ( remote_addr || "???" ) + ":#{remote_port}" + end + + alias :to_str :to_s + alias :inspect :to_s + end +end
\ No newline at end of file diff --git a/get-version.sh b/get-version.sh new file mode 100755 index 0000000..f962146 --- /dev/null +++ b/get-version.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Needed to remove the new line when echoing the version +nl=' +' + +VERSION= + +usage="\ +Usage: $0 version_file +Print a version string. +" + +version_file="$1" +if test -z "$version_file"; then + echo "$usage" + exit 1 +fi +if test -f $version_file; then + . ./$version_file +else + echo "Version file $version_file not found" + exit 1 +fi + +if test -z $PKG_VERSION; then + echo "No version found in $version_file" + exit 1 +fi + +# Omit the trailing newline +echo "$PKG_VERSION" | tr -d "$nl" diff --git a/htp.pc.in b/htp.pc.in new file mode 100644 index 0000000..9b1a6cc --- /dev/null +++ b/htp.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: A security-aware HTTP parser, designed for use in IDS/IPS and WAF products. +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lhtp +Libs.private: -lz @LIBICONV@ +Cflags: -I${includedir} -I${libdir}/htp/include + diff --git a/htp/Makefile.am b/htp/Makefile.am new file mode 100644 index 0000000..a3e8245 --- /dev/null +++ b/htp/Makefile.am @@ -0,0 +1,35 @@ + +SUBDIRS = lzma + +h_sources = bstr.h bstr_builder.h htp.h htp_base64.h htp_config.h htp_connection_parser.h \ + htp_core.h htp_decompressors.h htp_hooks.h htp_list.h \ + htp_multipart.h htp_table.h htp_transaction.h \ + htp_urlencoded.h htp_utf8_decoder.h htp_version.h + +h_sources_private = htp_config_private.h htp_connection_private.h htp_connection_parser_private.h htp_list_private.h \ + htp_multipart_private.h htp_private.h htp_table_private.h htp_config_auto.h + +c_sources = bstr.c bstr_builder.c htp_base64.c htp_config.c htp_connection.c htp_connection_parser.c \ + htp_content_handlers.c htp_cookies.c htp_decompressors.c htp_hooks.c htp_list.c htp_multipart.c htp_parsers.c \ + htp_php.c htp_request.c htp_request_apache_2_2.c htp_request_generic.c htp_request_parsers.c htp_response.c \ + htp_response_generic.c htp_table.c htp_transaction.c htp_transcoder.c htp_urlencoded.c htp_util.c htp_utf8_decoder.c \ + strlcpy.c strlcat.c + +library_includedir = $(includedir)/$(GENERIC_LIBRARY_NAME) +library_include_HEADERS = $(h_sources) + +AM_CFLAGS = -I$(top_srcdir) -I$(top_builddir)/htp -D_GNU_SOURCE -g -Wall -Wextra -std=gnu99 -pedantic \ + -Wextra -Wno-missing-field-initializers -Wshadow -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wno-unused-parameter + +noinst_LTLIBRARIES = libhtp-c.la +libhtp_c_la_SOURCES = $(h_sources) $(h_sources_private) $(c_sources) + +lib_LTLIBRARIES = libhtp.la +libhtp_la_SOURCES = +libhtp_la_LIBADD = libhtp-c.la lzma/liblzma-c.la +libhtp_la_LDFLAGS = -version-info $(GENERIC_LIBRARY_VERSION) +if CYGWIN +libhtp_la_LIBADD += $(LIBICONV) +libhtp_la_LDFLAGS += -no-undefined +endif diff --git a/htp/bstr.c b/htp/bstr.c new file mode 100644 index 0000000..7673c68 --- /dev/null +++ b/htp/bstr.c @@ -0,0 +1,638 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <ctype.h> + +#include "bstr.h" + +bstr *bstr_alloc(size_t len) { + bstr *b = malloc(sizeof (bstr) + len); + if (b == NULL) return NULL; + + b->len = 0; + b->size = len; + b->realptr = NULL; + + return b; +} + +bstr *bstr_add(bstr *destination, const bstr *source) { + return bstr_add_mem(destination, bstr_ptr(source), bstr_len(source)); +} + +bstr *bstr_add_c(bstr *bdestination, const char *csource) { + return bstr_add_mem(bdestination, csource, strlen(csource)); +} + +bstr *bstr_add_c_noex(bstr *destination, const char *source) { + return bstr_add_mem_noex(destination, source, strlen(source)); +} + +bstr *bstr_add_mem(bstr *destination, const void *data, size_t len) { + // Expand the destination if necessary + if (bstr_size(destination) < bstr_len(destination) + len) { + destination = bstr_expand(destination, bstr_len(destination) + len); + if (destination == NULL) return NULL; + } + + // Add source to destination + bstr *b = (bstr *) destination; + memcpy(bstr_ptr(destination) + bstr_len(b), data, len); + bstr_adjust_len(b, bstr_len(b) + len); + + return destination; +} + +bstr *bstr_add_mem_noex(bstr *destination, const void *data, size_t len) { + size_t copylen = len; + + // Is there enough room in the destination? + if (bstr_size(destination) < bstr_len(destination) + copylen) { + copylen = bstr_size(destination) - bstr_len(destination); + if (copylen <= 0) return destination; + } + + // Copy over the bytes + bstr *b = (bstr *) destination; + memcpy(bstr_ptr(destination) + bstr_len(b), data, copylen); + bstr_adjust_len(b, bstr_len(b) + copylen); + + return destination; +} + +bstr *bstr_add_noex(bstr *destination, const bstr *source) { + return bstr_add_mem_noex(destination, bstr_ptr(source), bstr_len(source)); +} + +void bstr_adjust_len(bstr *b, size_t newlen) { + b->len = newlen; +} + +void bstr_adjust_realptr(bstr *b, void *newrealptr) { + b->realptr = newrealptr; +} + +void bstr_adjust_size(bstr *b, size_t newsize) { + b->size = newsize; +} + +int bstr_begins_with(const bstr *haystack, const bstr *needle) { + return bstr_begins_with_mem(haystack, bstr_ptr(needle), bstr_len(needle)); +} + +int bstr_begins_with_c(const bstr *haystack, const char *needle) { + return bstr_begins_with_mem(haystack, needle, strlen(needle)); +} + +int bstr_begins_with_c_nocase(const bstr *haystack, const char *needle) { + return bstr_begins_with_mem_nocase(haystack, needle, strlen(needle)); +} + +int bstr_begins_with_nocase(const bstr *haystack, const bstr *needle) { + return bstr_begins_with_mem_nocase(haystack, bstr_ptr(needle), bstr_len(needle)); +} + +int bstr_begins_with_mem(const bstr *haystack, const void *_data, size_t len) { + const unsigned char *data = (unsigned char *) _data; + const unsigned char *hdata = bstr_ptr(haystack); + size_t hlen = bstr_len(haystack); + size_t pos = 0; + + while ((pos < len) && (pos < hlen)) { + if (hdata[pos] != data[pos]) { + return 0; + } + + pos++; + } + + if (pos == len) { + return 1; + } else { + return 0; + } +} + +int bstr_begins_with_mem_nocase(const bstr *haystack, const void *_data, size_t len) { + const unsigned char *data = (const unsigned char *) _data; + const unsigned char *hdata = bstr_ptr(haystack); + size_t hlen = bstr_len(haystack); + size_t pos = 0; + + while ((pos < len) && (pos < hlen)) { + if (tolower((int) hdata[pos]) != tolower((int) data[pos])) { + return 0; + } + + pos++; + } + + if (pos == len) { + return 1; + } else { + return 0; + } +} + +int bstr_char_at(const bstr *b, size_t pos) { + unsigned char *data = bstr_ptr(b); + size_t len = bstr_len(b); + + if (pos >= len) return -1; + return data[pos]; +} + +int bstr_char_at_end(const bstr *b, size_t pos) { + unsigned char *data = bstr_ptr(b); + size_t len = bstr_len(b); + + if (pos >= len) return -1; + return data[len - 1 - pos]; +} + +void bstr_chop(bstr *b) { + if (bstr_len(b) > 0) { + bstr_adjust_len(b, bstr_len(b) - 1); + } +} + +int bstr_chr(const bstr *b, int c) { + unsigned char *data = bstr_ptr(b); + size_t len = bstr_len(b); + + size_t i = 0; + while (i < len) { + if (data[i] == c) { + return (int) i; + } + + i++; + } + + return -1; +} + +int bstr_cmp(const bstr *b1, const bstr *b2) { + return bstr_util_cmp_mem(bstr_ptr(b1), bstr_len(b1), bstr_ptr(b2), bstr_len(b2)); +} + +int bstr_cmp_c(const bstr *b, const char *c) { + return bstr_util_cmp_mem(bstr_ptr(b), bstr_len(b), c, strlen(c)); +} + +int bstr_cmp_c_nocase(const bstr *b, const char *c) { + return bstr_util_cmp_mem_nocase(bstr_ptr(b), bstr_len(b), c, strlen(c)); +} + +int bstr_cmp_c_nocasenorzero(const bstr *b, const char *c) { + return bstr_util_cmp_mem_nocasenorzero(bstr_ptr(b), bstr_len(b), c, strlen(c)); +} + +int bstr_cmp_mem(const bstr *b, const void *data, size_t len) { + return bstr_util_cmp_mem(bstr_ptr(b), bstr_len(b), data, len); +} + +int bstr_cmp_mem_nocase(const bstr *b, const void *data, size_t len) { + return bstr_util_cmp_mem_nocase(bstr_ptr(b), bstr_len(b), data, len); +} + +int bstr_cmp_nocase(const bstr *b1, const bstr *b2) { + return bstr_util_cmp_mem_nocase(bstr_ptr(b1), bstr_len(b1), bstr_ptr(b2), bstr_len(b2)); +} + +bstr *bstr_dup(const bstr *b) { + return bstr_dup_ex(b, 0, bstr_len(b)); +} + +bstr *bstr_dup_c(const char *cstr) { + return bstr_dup_mem(cstr, strlen(cstr)); +} + +bstr *bstr_dup_ex(const bstr *b, size_t offset, size_t len) { + bstr *bnew = bstr_alloc(len); + if (bnew == NULL) return NULL; + memcpy(bstr_ptr(bnew), bstr_ptr(b) + offset, len); + bstr_adjust_len(bnew, len); + return bnew; +} + +bstr *bstr_dup_lower(const bstr *b) { + return bstr_to_lowercase(bstr_dup(b)); +} + +bstr *bstr_dup_mem(const void *data, size_t len) { + bstr *bnew = bstr_alloc(len); + if (bnew == NULL) return NULL; + memcpy(bstr_ptr(bnew), data, len); + bstr_adjust_len(bnew, len); + return bnew; +} + +bstr *bstr_expand(bstr *b, size_t newsize) { + if (bstr_realptr(b) != NULL) { + // Refuse to expand a wrapped bstring. In the future, + // we can change this to make a copy of the data, thus + // leaving the original memory area intact. + return NULL; + } + + // Catch attempts to "expand" to a smaller size + if (bstr_size(b) > newsize) return NULL; + + bstr *bnew = realloc(b, sizeof (bstr) + newsize); + if (bnew == NULL) return NULL; + + bstr_adjust_size(bnew, newsize); + + return bnew; +} + +void bstr_free(bstr *b) { + if (b == NULL) return; + free(b); +} + +int bstr_index_of(const bstr *haystack, const bstr *needle) { + return bstr_index_of_mem(haystack, bstr_ptr(needle), bstr_len(needle)); +} + +int bstr_index_of_c(const bstr *haystack, const char *needle) { + return bstr_index_of_mem(haystack, needle, strlen(needle)); +} + +int bstr_index_of_c_nocase(const bstr *haystack, const char *needle) { + return bstr_index_of_mem_nocase(haystack, needle, strlen(needle)); +} + +int bstr_index_of_c_nocasenorzero(const bstr *haystack, const char *needle) { + return bstr_util_mem_index_of_mem_nocasenorzero(bstr_ptr(haystack), bstr_len(haystack), needle, strlen(needle)); +} + +int bstr_index_of_mem(const bstr *haystack, const void *_data2, size_t len2) { + return bstr_util_mem_index_of_mem(bstr_ptr(haystack), bstr_len(haystack), _data2, len2); +} + +int bstr_index_of_mem_nocase(const bstr *haystack, const void *_data2, size_t len2) { + return bstr_util_mem_index_of_mem_nocase(bstr_ptr(haystack), bstr_len(haystack), _data2, len2); +} + +int bstr_index_of_nocase(const bstr *haystack, const bstr *needle) { + return bstr_index_of_mem_nocase(haystack, bstr_ptr(needle), bstr_len(needle)); +} + +int bstr_rchr(const bstr *b, int c) { + const unsigned char *data = bstr_ptr(b); + size_t len = bstr_len(b); + + size_t i = len; + while (i > 0) { + if (data[i - 1] == c) { + return (int) (i - 1); + } + + i--; + } + + return -1; +} + +bstr *bstr_to_lowercase(bstr *b) { + if (b == NULL) return NULL; + + unsigned char *data = bstr_ptr(b); + size_t len = bstr_len(b); + + size_t i = 0; + while (i < len) { + data[i] = (uint8_t)tolower(data[i]); + i++; + } + + return b; +} + +int bstr_util_cmp_mem(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (const unsigned char *) _data1; + const unsigned char *data2 = (const unsigned char *) _data2; + size_t p1 = 0, p2 = 0; + + while ((p1 < len1) && (p2 < len2)) { + if (data1[p1] != data2[p2]) { + // Difference. + return (data1[p1] < data2[p2]) ? -1 : 1; + } + + p1++; + p2++; + } + + if ((p1 == len2) && (p2 == len1)) { + // They're identical. + return 0; + } else { + // One string is shorter. + if (p1 == len1) return -1; + else return 1; + } +} + +int bstr_util_cmp_mem_nocase(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (const unsigned char *) _data1; + const unsigned char *data2 = (const unsigned char *) _data2; + size_t p1 = 0, p2 = 0; + + while ((p1 < len1) && (p2 < len2)) { + if (tolower(data1[p1]) != tolower(data2[p2])) { + // Difference. + return (tolower(data1[p1]) < tolower(data2[p2])) ? -1 : 1; + } + + p1++; + p2++; + } + + if ((p1 == len2) && (p2 == len1)) { + // They're identical. + return 0; + } else { + // One string is shorter. + if (p1 == len1) return -1; + else return 1; + } +} + +int bstr_util_cmp_mem_nocasenorzero(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (const unsigned char *) _data1; + const unsigned char *data2 = (const unsigned char *) _data2; + size_t p1 = 0, p2 = 0; + + while ((p1 < len1) && (p2 < len2)) { + if (data1[p1] == 0) { + p1++; + continue; + } + if (tolower(data1[p1]) != tolower(data2[p2])) { + // Difference. + return (tolower(data1[p1]) < tolower(data2[p2])) ? -1 : 1; + } + + p1++; + p2++; + } + + while((p1 < len1) && (data1[p1] == 0)) { + p1++; + } + if ((p1 == len1) && (p2 == len2)) { + // They're identical. + return 0; + } else { + // One string is shorter. + if (p1 == len1) return -1; + else return 1; + } +} + +int64_t bstr_util_mem_to_pint(const void *_data, size_t len, int base, size_t *lastlen) { + const unsigned char *data = (unsigned char *) _data; + int64_t rval = 0, tflag = 0; + size_t i = 0; + + *lastlen = i; + + for (i = 0; i < len; i++) { + int d = data[i]; + + *lastlen = i; + + // Convert character to digit. + if ((d >= '0') && (d <= '9')) { + d -= '0'; + } else if ((d >= 'a') && (d <= 'z')) { + d -= 'a' - 10; + } else if ((d >= 'A') && (d <= 'Z')) { + d -= 'A' - 10; + } else { + d = -1; + } + + // Check that the digit makes sense with the base we are using. + if ((d == -1) || (d >= base)) { + if (tflag) { + // Return what we have so far; lastlen points + // to the first non-digit position. + return rval; + } else { + // We didn't see a single digit. + return -1; + } + } + + if (tflag) { + if (((INT64_MAX - d) / base) < rval) { + // Overflow + return -2; + } + + rval *= base; + rval += d; + } else { + rval = d; + tflag = 1; + } + } + + *lastlen = i + 1; + + return rval; +} + +int bstr_util_mem_index_of_c(const void *_data1, size_t len1, const char *cstr) { + return bstr_util_mem_index_of_mem(_data1, len1, cstr, strlen(cstr)); +} + +int bstr_util_mem_index_of_c_nocase(const void *_data1, size_t len1, const char *cstr) { + return bstr_util_mem_index_of_mem_nocase(_data1, len1, cstr, strlen(cstr)); +} + +int bstr_util_mem_index_of_mem(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (unsigned char *) _data1; + const unsigned char *data2 = (unsigned char *) _data2; + size_t i, j; + + // If we ever want to optimize this function, the following link + // might be useful: http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm + + for (i = 0; i < len1; i++) { + size_t k = i; + + for (j = 0; ((j < len2) && (k < len1)); j++, k++) { + if (data1[k] != data2[j]) break; + } + + if (j == len2) { + return (int) i; + } + } + + return -1; +} + +int bstr_util_mem_index_of_mem_nocase(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (unsigned char *) _data1; + const unsigned char *data2 = (unsigned char *) _data2; + size_t i, j; + + // If we ever want to optimize this function, the following link + // might be useful: http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm + + for (i = 0; i < len1; i++) { + size_t k = i; + + for (j = 0; ((j < len2) && (k < len1)); j++, k++) { + if (toupper(data1[k]) != toupper(data2[j])) break; + } + + if (j == len2) { + return (int) i; + } + } + + return -1; +} + +int bstr_util_mem_index_of_mem_nocasenorzero(const void *_data1, size_t len1, const void *_data2, size_t len2) { + const unsigned char *data1 = (unsigned char *) _data1; + const unsigned char *data2 = (unsigned char *) _data2; + size_t i, j; + + // If we ever want to optimize this function, the following link + // might be useful: http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm + + for (i = 0; i < len1; i++) { + size_t k = i; + if (data1[i] == 0) { + // skip leading zeroes to avoid quadratic complexity + continue; + } + + for (j = 0; ((j < len2) && (k < len1)); j++, k++) { + if (data1[k] == 0) { + j--; + continue; + } + if (toupper(data1[k]) != toupper(data2[j])) break; + } + + if (j == len2) { + return (int) i; + } + } + + return -1; +} + +void bstr_util_mem_trim(unsigned char **data, size_t *len) { + if ((data == NULL)||(len == NULL)) return; + + unsigned char *d = *data; + size_t l = *len; + + // Ignore whitespace at the beginning. + size_t pos = 0; + while ((pos < l) && isspace(d[pos])) pos++; + d += pos; + l -= pos; + + // Ignore whitespace at the end. + while ((l > 0)&&(isspace(d[l - 1]))) l--; + + *data = d; + *len = l; +} + +char *bstr_util_memdup_to_c(const void *_data, size_t len) { + const unsigned char *data = (unsigned char *) _data; + + // Count how many NUL bytes we have in the string. + size_t i, nulls = 0; + for (i = 0; i < len; i++) { + if (data[i] == '\0') { + nulls++; + } + } + + // Now copy the string into a NUL-terminated buffer. + + char *r, *d; + r = d = malloc(len + nulls + 1); + if (d == NULL) return NULL; + + while (len--) { + if (*data == '\0') { + data++; + *d++ = '\\'; + *d++ = '0'; + } else { + *d++ = *data++; + } + } + + *d = '\0'; + + return r; +} + +char *bstr_util_strdup_to_c(const bstr *b) { + if (b == NULL) return NULL; + return bstr_util_memdup_to_c(bstr_ptr(b), bstr_len(b)); +} + +bstr *bstr_wrap_c(const char *cstr) { + return bstr_wrap_mem((unsigned char *) cstr, strlen(cstr)); +} + +bstr *bstr_wrap_mem(const void *data, size_t len) { + bstr *b = (bstr *) malloc(sizeof (bstr)); + if (b == NULL) return NULL; + + b->size = b->len = len; + b->realptr = (unsigned char *) data; + + return b; +} diff --git a/htp/bstr.h b/htp/bstr.h new file mode 100644 index 0000000..eb6497b --- /dev/null +++ b/htp/bstr.h @@ -0,0 +1,678 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _BSTR_H +#define _BSTR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct bstr_t bstr; + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "bstr_builder.h" + +// Data structures + +struct bstr_t { + /** The length of the string stored in the buffer. */ + size_t len; + + /** The current size of the buffer. If there is extra room in the + * buffer the string will be able to expand without reallocation. + */ + size_t size; + + /** Optional buffer pointer. If this pointer is NULL the string buffer + * will immediately follow this structure. If the pointer is not NUL, + * it points to the actual buffer used, and there's no data following + * this structure. + */ + unsigned char *realptr; +}; + + +// Defines + +#define bstr_len(X) ((*(X)).len) +#define bstr_size(X) ((*(X)).size) +#define bstr_ptr(X) ( ((*(X)).realptr == NULL) ? ((unsigned char *)(X) + sizeof(bstr)) : (unsigned char *)(*(X)).realptr ) +#define bstr_realptr(X) ((*(X)).realptr) + + +// Functions + +/** + * Append source bstring to destination bstring, growing destination if + * necessary. If the destination bstring is expanded, the pointer will change. + * You must replace the original destination pointer with the returned one. + * Destination is not changed on memory allocation failure. + * + * @param[in] bdestination + * @param[in] bsource + * @return Updated bstring, or NULL on memory allocation failure. + */ +bstr *bstr_add(bstr *bdestination, const bstr *bsource); + +/** + * Append a NUL-terminated source to destination, growing destination if + * necessary. If the string is expanded, the pointer will change. You must + * replace the original destination pointer with the returned one. Destination + * is not changed on memory allocation failure. + * + * @param[in] b + * @param[in] cstr + * @return Updated bstring, or NULL on memory allocation failure. + */ +bstr *bstr_add_c(bstr *b, const char *cstr); + +/** + * Append as many bytes from the source to destination bstring. The + * destination storage will not be expanded if there is not enough space in it + * already to accommodate all of the data. + * + * @param[in] b + * @param[in] cstr + * @return The destination bstring. + */ +bstr *bstr_add_c_noex(bstr *b, const char *cstr); + +/** + * Append a memory region to destination, growing destination if necessary. If + * the string is expanded, the pointer will change. You must replace the + * original destination pointer with the returned one. Destination is not + * changed on memory allocation failure. + * + * @param[in] b + * @param[in] data + * @param[in] len + * @return Updated bstring, or NULL on memory allocation failure. + */ +bstr *bstr_add_mem(bstr *b, const void *data, size_t len); + +/** + * Append as many bytes from the source to destination bstring. The + * destination storage will not be expanded if there is not enough space in it + * already to accommodate all of the data. + * + * @param[in] b + * @param[in] data + * @param[in] len + * @return The destination bstring. + */ +bstr *bstr_add_mem_noex(bstr *b, const void *data, size_t len); + +/** + * Append as many bytes from the source bstring to destination bstring. The + * destination storage will not be expanded if there is not enough space in it + * already to accommodate all of the data. + * + * @param[in] bdestination + * @param[in] bsource + * @return The destination bstring. + */ +bstr *bstr_add_noex(bstr *bdestination, const bstr *bsource); + +/** + * Adjust bstring length. You will need to use this method whenever + * you work directly with the string contents, and end up changing + * its length by direct structure manipulation. + * + * @param[in] b + * @param[in] newlen + */ +void bstr_adjust_len(bstr *b, size_t newlen); + +/** + * Change the external pointer used by bstring. You will need to use this + * function only if you're messing with bstr internals. Use with caution. + * + * @param[in] b + * @param[in] newrealptr + */ +void bstr_adjust_realptr(bstr *b, void *newrealptr); + +/** + * Adjust bstring size. This does not change the size of the storage behind + * the bstring, just changes the field that keeps track of how many bytes + * there are in the storage. You will need to use this function only if + * you're messing with bstr internals. Use with caution. + * + * @param[in] b + * @param[in] newsize + */ +void bstr_adjust_size(bstr *b, size_t newsize); + +/** + * Allocate a zero-length bstring, reserving space for at least size bytes. + * + * @param[in] size + * @return New string instance + */ +bstr *bstr_alloc(size_t size); + +/** + * Checks whether bstring begins with another bstring. Case sensitive. + * + * @param[in] bhaystack + * @param[in] bneedle + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with(const bstr *bhaystack, const bstr *bneedle); + +/** + * Checks whether bstring begins with NUL-terminated string. Case sensitive. + * + * @param[in] bhaystack + * @param[in] cneedle + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with_c(const bstr *bhaystack, const char *cneedle); + +/** + * Checks whether bstring begins with NUL-terminated string. Case insensitive. + * + * @param[in] bhaystack + * @param[in] cneedle + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with_c_nocase(const bstr *bhaystack, const char *cneedle); + +/** + * Checks whether the bstring begins with the given memory block. Case sensitive. + * + * @param[in] bhaystack + * @param[in] data + * @param[in] len + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with_mem(const bstr *bhaystack, const void *data, size_t len); + +/** + * Checks whether bstring begins with memory block. Case insensitive. + * + * @param[in] bhaystack + * @param[in] data + * @param[in] len + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with_mem_nocase(const bstr *bhaystack, const void *data, size_t len); + +/** + * Checks whether bstring begins with another bstring. Case insensitive. + * + * @param[in] bhaystack + * @param[in] cneedle + * @return 1 if true, otherwise 0. + */ +int bstr_begins_with_nocase(const bstr *bhaystack, const bstr *cneedle); + +/** + * Return the byte at the given position. + * + * @param[in] b + * @param[in] pos + * @return The byte at the given location, or -1 if the position is out of range. + */ +int bstr_char_at(const bstr *b, size_t pos); + +/** + * Return the byte at the given position, counting from the end of the string (e.g., + * byte at position 0 is the last byte in the string.) + * + * @param[in] b + * @param[in] pos + * @return The byte at the given location, or -1 if the position is out of range. + */ +int bstr_char_at_end(const bstr *b, size_t pos); + +/** + * Remove the last byte from bstring, assuming it contains at least one byte. This + * function will not reduce the storage that backs the string, only the amount + * of data used. + * + * @param[in] b + */ +void bstr_chop(bstr *b); + +/** + * Return the first position of the provided byte. + * + * @param[in] b + * @param[in] c + * @return The first position of the byte, or -1 if it could not be found + */ +int bstr_chr(const bstr *b, int c); + +/** + * Case-sensitive comparison of two bstrings. + * + * @param[in] b1 + * @param[in] b2 + * @return Zero on string match, 1 if b1 is greater than b2, and -1 if b2 is + * greater than b1. + */ +int bstr_cmp(const bstr *b1, const bstr *b2); + +/** + * Case-sensitive comparison of a bstring and a NUL-terminated string. + * + * @param[in] b + * @param[in] cstr + * @return Zero on string match, 1 if b is greater than cstr, and -1 if cstr is + * greater than b. + */ +int bstr_cmp_c(const bstr *b, const char *cstr); + +/** + * Case-insensitive comparison of a bstring with a NUL-terminated string. + * + * @param[in] b + * @param[in] cstr + * @return Zero on string match, 1 if b is greater than cstr, and -1 if cstr is greater than b. + */ +int bstr_cmp_c_nocase(const bstr *b, const char *cstr); + +/** + * Case-insensitive zero-skipping comparison of a bstring with a NUL-terminated string. + * + * @param[in] b + * @param[in] cstr + * @return Zero on string match, 1 if b is greater than cstr, and -1 if cstr is greater than b. + */ +int bstr_cmp_c_nocasenorzero(const bstr *b, const char *cstr); + +/** + * Performs a case-sensitive comparison of a bstring with a memory region. + * + * @param[in] b + * @param[in] data + * @param[in] len + * @return Zero ona match, 1 if b is greater than data, and -1 if data is greater than b. + */ +int bstr_cmp_mem(const bstr *b, const void *data, size_t len); + +/** + * Performs a case-insensitive comparison of a bstring with a memory region. + * + * @param[in] b + * @param[in] data + * @param[in] len + * @return Zero ona match, 1 if b is greater than data, and -1 if data is greater than b. + */ +int bstr_cmp_mem_nocase(const bstr *b, const void *data, size_t len); + +/** + * Case-insensitive comparison two bstrings. + * + * @param[in] b1 + * @param[in] b2 + * @return Zero on string match, 1 if b1 is greater than b2, and -1 if b2 is + * greater than b1. + */ +int bstr_cmp_nocase(const bstr *b1, const bstr *b2); + +/** + * Case-insensitive and zero skipping comparison two bstrings. + * + * @param[in] b1 + * @param[in] b2 + * @return Zero on string match, 1 if b1 is greater than b2, and -1 if b2 is + * greater than b1. + */ +int bstr_cmp_nocasenorzero(const bstr *b1, const bstr *b2); + +/** + * Create a new bstring by copying the provided bstring. + * + * @param[in] b + * @return New bstring, or NULL if memory allocation failed. + */ +bstr *bstr_dup(const bstr *b); + +/** + * Create a new bstring by copying the provided NUL-terminated string. + * + * @param[in] cstr + * @return New bstring, or NULL if memory allocation failed. + */ +bstr *bstr_dup_c(const char *cstr); + +/** + * Create a new bstring by copying a part of the provided bstring. + * + * @param[in] b + * @param[in] offset + * @param[in] len + * @return New bstring, or NULL if memory allocation failed. + */ +bstr *bstr_dup_ex(const bstr *b, size_t offset, size_t len); + +/** + * Create a copy of the provided bstring, then convert it to lowercase. + * + * @param[in] b + * @return New bstring, or NULL if memory allocation failed + */ +bstr *bstr_dup_lower(const bstr *b); + +/** + * Create a new bstring by copying the provided memory region. + * + * @param[in] data + * @param[in] len + * @return New bstring, or NULL if memory allocation failed + */ +bstr *bstr_dup_mem(const void *data, size_t len); + +/** + * Expand internal bstring storage to support at least newsize bytes. The storage + * is not expanded if the current size is equal or greater to newsize. Because + * realloc is used underneath, the old pointer to bstring may no longer be valid + * after this function completes successfully. + * + * @param[in] b + * @param[in] newsize + * @return Updated string instance, or NULL if memory allocation failed or if + * attempt was made to "expand" the bstring to a smaller size. + */ +bstr *bstr_expand(bstr *b, size_t newsize); + +/** + * Deallocate the supplied bstring instance and set it to NULL. Allows NULL on + * input. + * + * @param[in] b + */ +void bstr_free(bstr *b); + +/** + * Find the needle in the haystack. + * + * @param[in] bhaystack + * @param[in] bneedle + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of(const bstr *bhaystack, const bstr *bneedle); + +/** + * Find the needle in the haystack, ignoring case differences. + * + * @param[in] bhaystack + * @param[in] bneedle + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_nocase(const bstr *bhaystack, const bstr *bneedle); + +/** + * Find the needle in the haystack, with the needle being a NUL-terminated + * string. + * + * @param[in] bhaystack + * @param[in] cneedle + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_c(const bstr *bhaystack, const char *cneedle); + +/** + * Find the needle in the haystack, with the needle being a NUL-terminated + * string. Ignore case differences. + * + * @param[in] bhaystack + * @param[in] cneedle + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_c_nocase(const bstr *bhaystack, const char *cneedle); + +/** + * Find the needle in the haystack, with the needle being a NUL-terminated + * string. Ignore case differences. Skip zeroes in haystack + * + * @param[in] bhaystack + * @param[in] cneedle + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_c_nocasenorzero(const bstr *bhaystack, const char *cneedle); + +/** + * Find the needle in the haystack, with the needle being a memory region. + * + * @param[in] bhaystack + * @param[in] data + * @param[in] len + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_mem(const bstr *bhaystack, const void *data, size_t len); + +/** + * Find the needle in the haystack, with the needle being a memory region. + * Ignore case differences. + * + * @param[in] bhaystack + * @param[in] data + * @param[in] len + * @return Position of the match, or -1 if the needle could not be found. + */ +int bstr_index_of_mem_nocase(const bstr *bhaystack, const void *data, size_t len); + +/** + * Return the last position of a character (byte). + * + * @param[in] b + * @param[in] c + * @return The last position of the character, or -1 if it could not be found. + */ +int bstr_rchr(const bstr *b, int c); + +/** + * Convert bstring to lowercase. This function converts the supplied string, + * it does not create a new string. + * + * @param[in] b + * @return The same bstring received on input + */ +bstr *bstr_to_lowercase(bstr *b); + +/** + * Case-sensitive comparison of two memory regions. + * + * @param[in] data1 + * @param[in] len1 + * @param[in] data2 + * @param[in] len2 + * @return Zero if the memory regions are identical, 1 if data1 is greater than + * data2, and -1 if data2 is greater than data1. + */ +int bstr_util_cmp_mem(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Case-insensitive comparison of two memory regions. + * + * @param[in] data1 + * @param[in] len1 + * @param[in] data2 + * @param[in] len2 + * @return Zero if the memory regions are identical, 1 if data1 is greater than + * data2, and -1 if data2 is greater than data1. + */ + int bstr_util_cmp_mem_nocase(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Case-insensitive zero-skipping comparison of two memory regions. + * + * @param[in] data1 + * @param[in] len1 + * @param[in] data2 + * @param[in] len2 + * @return Zero if the memory regions are identical, 1 if data1 is greater than + * data2, and -1 if data2 is greater than data1. + */ + int bstr_util_cmp_mem_nocasenorzero(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Convert contents of a memory region to a positive integer. + * + * @param[in] data + * @param[in] len + * @param[in] base The desired number base. + * @param[in] lastlen Points to the first unused byte in the region + * @return If the conversion was successful, this function returns the + * number. When the conversion fails, -1 will be returned when not + * one valid digit was found, and -2 will be returned if an overflow + * occurred. + */ +int64_t bstr_util_mem_to_pint(const void *data, size_t len, int base, size_t *lastlen); + +/** + * Searches a memory block for the given NUL-terminated string. Case sensitive. + * + * @param[in] data + * @param[in] len + * @param[in] cstr + * @return Index of the first location of the needle on success, or -1 if the needle was not found. + */ +int bstr_util_mem_index_of_c(const void *data, size_t len, const char *cstr); + +/** + * Searches a memory block for the given NUL-terminated string. Case insensitive. + * + * @param[in] data + * @param[in] len + * @param[in] cstr + * @return Index of the first location of the needle on success, or -1 if the needle was not found. + */ +int bstr_util_mem_index_of_c_nocase(const void *data, size_t len, const char *cstr); + +/** + * Searches the haystack memory block for the needle memory block. Case sensitive. + * + * @param data1 + * @param len1 + * @param data2 + * @param len2 + * @return Index of the first location of the needle on success, or -1 if the needle was not found. + */ +int bstr_util_mem_index_of_mem(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Searches the haystack memory block for the needle memory block. Case sensitive. + * + * @param data1 + * @param len1 + * @param data2 + * @param len2 + * @return Index of the first location of the needle on success, or -1 if the needle was not found. + */ +int bstr_util_mem_index_of_mem_nocase(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Searches the haystack memory block for the needle memory block. Case sensitive. Skips zeroes in data1 + * + * @param data1 + * @param len1 + * @param data2 + * @param len2 + * @return Index of the first location of the needle on success, or -1 if the needle was not found. + */ +int bstr_util_mem_index_of_mem_nocasenorzero(const void *data1, size_t len1, const void *data2, size_t len2); + +/** + * Removes whitespace from the beginning and the end of a memory region. The data + * itself is not modified; this function only adjusts the provided pointers. + * + * @param[in,out] data + * @param[in,out] len + */ +void bstr_util_mem_trim(unsigned char **data, size_t *len); + +/** + * Take the provided memory region, allocate a new memory buffer, and construct + * a NUL-terminated string, replacing each NUL byte with "\0" (two bytes). The + * caller is responsible to keep track of the allocated memory area and free + * it once it is no longer needed. + * + * @param[in] data + * @param[in] len + * @return The newly created NUL-terminated string, or NULL in case of memory + * allocation failure. + */ +char *bstr_util_memdup_to_c(const void *data, size_t len); + +/** + * Create a new NUL-terminated string out of the provided bstring. If NUL bytes + * are contained in the bstring, each will be replaced with "\0" (two characters). + * The caller is responsible to keep track of the allocated memory area and free + * it once it is no longer needed. + * + * @param[in] b + * @return The newly created NUL-terminated string, or NULL in case of memory + * allocation failure. + */ +char *bstr_util_strdup_to_c(const bstr *b); + +/** + * Create a new bstring from the provided NUL-terminated string and without + * copying the data. The caller must ensure that the input string continues + * to point to a valid memory location for as long as the bstring is used. + * + * @param[in] cstr + * @return New bstring, or NULL on memory allocation failure. + */ +bstr *bstr_wrap_c(const char *cstr); + +/** + * Create a new bstring from the provided memory buffer without + * copying the data. The caller must ensure that the buffer remains + * valid for as long as the bstring is used. + * + * @param[in] data + * @param[in] len + * @return New bstring, or NULL on memory allocation failure. + */ +bstr *bstr_wrap_mem(const void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _BSTR_H */ diff --git a/htp/bstr_builder.c b/htp/bstr_builder.c new file mode 100644 index 0000000..89394f6 --- /dev/null +++ b/htp/bstr_builder.c @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "bstr.h" +#include "htp_list.h" + +htp_status_t bstr_builder_appendn(bstr_builder_t *bb, bstr *b) { + return htp_list_push(bb->pieces, b); +} + +htp_status_t bstr_builder_append_c(bstr_builder_t *bb, const char *cstr) { + bstr *b = bstr_dup_c(cstr); + if (b == NULL) return HTP_ERROR; + return htp_list_push(bb->pieces, b); +} + +htp_status_t bstr_builder_append_mem(bstr_builder_t *bb, const void *data, size_t len) { + bstr *b = bstr_dup_mem(data, len); + if (b == NULL) return HTP_ERROR; + return htp_list_push(bb->pieces, b); +} + +void bstr_builder_clear(bstr_builder_t *bb) { + // Do nothing if the list is empty + if (htp_list_size(bb->pieces) == 0) return; + + for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { + bstr *b = htp_list_get(bb->pieces, i); + bstr_free(b); + } + + htp_list_clear(bb->pieces); +} + +bstr_builder_t *bstr_builder_create(void) { + bstr_builder_t *bb = calloc(1, sizeof (bstr_builder_t)); + if (bb == NULL) return NULL; + + bb->pieces = htp_list_create(BSTR_BUILDER_DEFAULT_SIZE); + if (bb->pieces == NULL) { + free(bb); + return NULL; + } + + return bb; +} + +void bstr_builder_destroy(bstr_builder_t *bb) { + if (bb == NULL) return; + + // Destroy any pieces we might have + for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { + bstr *b = htp_list_get(bb->pieces, i); + bstr_free(b); + } + + htp_list_destroy(bb->pieces); + + free(bb); +} + +size_t bstr_builder_size(const bstr_builder_t *bb) { + return htp_list_size(bb->pieces); +} + +bstr *bstr_builder_to_str(const bstr_builder_t *bb) { + size_t len = 0; + + // Determine the size of the string + for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { + bstr *b = htp_list_get(bb->pieces, i); + len += bstr_len(b); + } + + // Allocate string + bstr *bnew = bstr_alloc(len); + if (bnew == NULL) return NULL; + + // Determine the size of the string + for (size_t i = 0, n = htp_list_size(bb->pieces); i < n; i++) { + bstr *b = htp_list_get(bb->pieces, i); + bstr_add_noex(bnew, b); + } + + return bnew; +} diff --git a/htp/bstr_builder.h b/htp/bstr_builder.h new file mode 100644 index 0000000..335a131 --- /dev/null +++ b/htp/bstr_builder.h @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _BSTR_BUILDER_H +#define _BSTR_BUILDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct bstr_builder_t bstr_builder_t; + +#include "htp_list.h" + +struct bstr_builder_t { + htp_list_t *pieces; +}; + +#define BSTR_BUILDER_DEFAULT_SIZE 16 + +/** + * Adds one new string to the builder. This function will adopt the + * string and destroy it when the builder itself is destroyed. + * + * @param[in] bb + * @param[in] b + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t bstr_builder_appendn(bstr_builder_t *bb, bstr *b); + +/** + * Adds one new piece, in the form of a NUL-terminated string, to + * the builder. This function will make a copy of the provided string. + * + * @param[in] bb + * @param[in] cstr + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t bstr_builder_append_c(bstr_builder_t *bb, const char *cstr); + +/** + * Adds one new piece, defined with the supplied pointer and + * length, to the builder. This function will make a copy of the + * provided data region. + * + * @param[in] bb + * @param[in] data + * @param[in] len + * @return @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t bstr_builder_append_mem(bstr_builder_t *bb, const void *data, size_t len); + +/** + * Clears this string builder, destroying all existing pieces. You may + * want to clear a builder once you've either read all the pieces and + * done something with them, or after you've converted the builder into + * a single string. + * + * @param[in] bb + */ +void bstr_builder_clear(bstr_builder_t *bb); + +/** + * Creates a new string builder. + * + * @return New string builder, or NULL on error. + */ +bstr_builder_t *bstr_builder_create(void); + +/** + * Destroys an existing string builder, also destroying all + * the pieces stored within. + * + * @param[in] bb + */ +void bstr_builder_destroy(bstr_builder_t *bb); + +/** + * Returns the size (the number of pieces) currently in a string builder. + * + * @param[in] bb + * @return size + */ +size_t bstr_builder_size(const bstr_builder_t *bb); + +/** + * Creates a single string out of all the pieces held in a + * string builder. This method will not destroy any of the pieces. + * + * @param[in] bb + * @return New string, or NULL on error. + */ +bstr *bstr_builder_to_str(const bstr_builder_t *bb); + + +#ifdef __cplusplus +} +#endif + +#endif /* _BSTR_BUILDER_H */ + diff --git a/htp/htp.h b/htp/htp.h new file mode 100644 index 0000000..36209ad --- /dev/null +++ b/htp/htp.h @@ -0,0 +1,678 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_H +#define _HTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/time.h> + +#include "htp_version.h" +#include "htp_core.h" + +#include "bstr.h" +#include "htp_base64.h" +#include "htp_config.h" +#include "htp_connection_parser.h" +#include "htp_decompressors.h" +#include "htp_hooks.h" +#include "htp_list.h" +#include "htp_multipart.h" +#include "htp_table.h" +#include "htp_transaction.h" +#include "htp_urlencoded.h" +#include "htp_utf8_decoder.h" + +/** + * Represents a single TCP connection. + */ +struct htp_conn_t { + /** Client IP address. */ + char *client_addr; + + /** Client port. */ + int client_port; + + /** Server IP address. */ + char *server_addr; + + /** Server port. */ + int server_port; + + /** + * Transactions carried out on this connection. The list may contain + * NULL elements when some of the transactions are deleted (and then + * removed from a connection by calling htp_conn_remove_tx(). + */ + htp_list_t *transactions; + + /** Log messages associated with this connection. */ + htp_list_t *messages; + + /** Parsing flags: HTP_CONN_PIPELINED. */ + uint8_t flags; + + /** When was this connection opened? Can be NULL. */ + htp_time_t open_timestamp; + + /** When was this connection closed? Can be NULL. */ + htp_time_t close_timestamp; + + /** Inbound data counter. */ + int64_t in_data_counter; + + /** Outbound data counter. */ + int64_t out_data_counter; +}; + +/** + * Used to represent files that are seen during the processing of HTTP traffic. Most + * commonly this refers to files seen in multipart/form-data payloads. In addition, PUT + * request bodies can be treated as files. + */ +struct htp_file_t { + /** Where did this file come from? Possible values: HTP_FILE_MULTIPART and HTP_FILE_PUT. */ + enum htp_file_source_t source; + + /** File name, as provided (e.g., in the Content-Disposition multipart part header. */ + bstr *filename; + + /** File length. */ + int64_t len; + + /** The unique filename in which this file is stored on the filesystem, when applicable.*/ + char *tmpname; + + /** The file descriptor used for external storage, or -1 if unused. */ + int fd; +}; + +/** + * Represents a chunk of file data. + */ +struct htp_file_data_t { + /** File information. */ + htp_file_t *file; + + /** Pointer to the data buffer. */ + const unsigned char *data; + + /** Buffer length. */ + size_t len; +}; + +/** + * Represents a single log entry. + */ +struct htp_log_t { + /** The connection parser associated with this log message. */ + htp_connp_t *connp; + + /** The transaction associated with this log message, if any. */ + htp_tx_t *tx; + + /** Log message. */ + const char *msg; + + /** Message level. */ + enum htp_log_level_t level; + + /** Message code. */ + int code; + + /** File in which the code that emitted the message resides. */ + const char *file; + + /** Line number on which the code that emitted the message resides. */ + unsigned int line; +}; + +/** + * Represents a single request or response header. + */ +struct htp_header_t { + /** Header name. */ + bstr *name; + + /** Header value. */ + bstr *value; + + /** Parsing flags; a combination of: HTP_FIELD_INVALID, HTP_FIELD_FOLDED, HTP_FIELD_REPEATED. */ + uint64_t flags; +}; + +/** + * Represents a single request parameter. + */ +struct htp_param_t { + /** Parameter name. */ + bstr *name; + + /** Parameter value. */ + bstr *value; + + /** Source of the parameter, for example HTP_SOURCE_QUERY_STRING. */ + enum htp_data_source_t source; + + /** Type of the data structure referenced below. */ + enum htp_parser_id_t parser_id; + + /** + * Pointer to the parser data structure that contains + * complete information about the parameter. Can be NULL. + */ + void *parser_data; +}; + +/** + * Represents a single HTTP transaction, which is a combination of a request and a response. + */ +struct htp_tx_t { + /** The connection parser associated with this transaction. */ + htp_connp_t *connp; + + /** The connection to which this transaction belongs. */ + htp_conn_t *conn; + + /** The configuration structure associated with this transaction. */ + htp_cfg_t *cfg; + + /** + * Is the configuration structure shared with other transactions or connections? If + * this field is set to HTP_CONFIG_PRIVATE, the transaction owns the configuration. + */ + int is_config_shared; + + /** The user data associated with this transaction. */ + void *user_data; + + + // Request fields + + /** Contains a count of how many empty lines were skipped before the request line. */ + unsigned int request_ignored_lines; + + /** The first line of this request. */ + bstr *request_line; + + /** Request method. */ + bstr *request_method; + + /** Request method, as number. Available only if we were able to recognize the request method. */ + enum htp_method_t request_method_number; + + /** + * Request URI, raw, as given to us on the request line. This field can take different forms, + * for example authority for CONNECT methods, absolute URIs for proxy requests, and the query + * string when one is provided. Use htp_tx_t::parsed_uri if you need to access to specific + * URI elements. Can be NULL if the request line contains only a request method (which is + * an extreme case of HTTP/0.9, but passes in practice. + */ + bstr *request_uri; + + /** Request protocol, as text. Can be NULL if no protocol was specified. */ + bstr *request_protocol; + + /** + * Protocol version as a number. Multiply the high version number by 100, then add the low + * version number. You should prefer to work the pre-defined HTP_PROTOCOL_* constants. + */ + int request_protocol_number; + + /** + * Is this request using HTTP/0.9? We need a separate field for this purpose because + * the protocol version alone is not sufficient to determine if HTTP/0.9 is used. For + * example, if you submit "GET / HTTP/0.9" to Apache, it will not treat the request + * as HTTP/0.9. + */ + int is_protocol_0_9; + + /** + * This structure holds the individual components parsed out of the request URI, with + * appropriate normalization and transformation applied, per configuration. No information + * is added. In extreme cases when no URI is provided on the request line, all fields + * will be NULL. (Well, except for port_number, which will be -1.) To inspect raw data, use + * htp_tx_t::request_uri or htp_tx_t::parsed_uri_raw. + */ + htp_uri_t *parsed_uri; + + /** + * This structure holds the individual components parsed out of the request URI, but + * without any modification. The purpose of this field is to allow you to look at the data as it + * was supplied on the request line. Fields can be NULL, depending on what data was supplied. + * The port_number field is always -1. + */ + htp_uri_t *parsed_uri_raw; + + /* HTTP 1.1 RFC + * + * 4.3 Message Body + * + * The message-body (if any) of an HTTP message is used to carry the + * entity-body associated with the request or response. The message-body + * differs from the entity-body only when a transfer-coding has been + * applied, as indicated by the Transfer-Encoding header field (section + * 14.41). + * + * message-body = entity-body + * | <entity-body encoded as per Transfer-Encoding> + */ + + /** + * The length of the request message-body. In most cases, this value + * will be the same as request_entity_len. The values will be different + * if request compression or chunking were applied. In that case, + * request_message_len contains the length of the request body as it + * has been seen over TCP; request_entity_len contains length after + * de-chunking and decompression. + */ + int64_t request_message_len; + + /** + * The length of the request entity-body. In most cases, this value + * will be the same as request_message_len. The values will be different + * if request compression or chunking were applied. In that case, + * request_message_len contains the length of the request body as it + * has been seen over TCP; request_entity_len contains length after + * de-chunking and decompression. + */ + int64_t request_entity_len; + + /** Parsed request headers. */ + htp_table_t *request_headers; + + /** + * Request transfer coding. Can be one of HTP_CODING_UNKNOWN (body presence not + * determined yet), HTP_CODING_IDENTITY, HTP_CODING_CHUNKED, HTP_CODING_NO_BODY, + * and HTP_CODING_UNRECOGNIZED. + */ + enum htp_transfer_coding_t request_transfer_coding; + + /** Request body compression. */ + enum htp_content_encoding_t request_content_encoding; + + /** + * This field contain the request content type when that information is + * available in request headers. The contents of the field will be converted + * to lowercase and any parameters (e.g., character set information) removed. + */ + bstr *request_content_type; + + /** + * Contains the value specified in the Content-Length header. The value of this + * field will be -1 from the beginning of the transaction and until request + * headers are processed. It will stay -1 if the C-L header was not provided, + * or if the value in it cannot be parsed. + */ + int64_t request_content_length; + + /** + * Transaction-specific REQUEST_BODY_DATA hook. Behaves as + * the configuration hook with the same name. + */ + htp_hook_t *hook_request_body_data; + + /** + * Transaction-specific RESPONSE_BODY_DATA hook. Behaves as + * the configuration hook with the same name. + */ + htp_hook_t *hook_response_body_data; + + /** + * Query string URLENCODED parser. Available only + * when the query string is not NULL and not empty. + */ + htp_urlenp_t *request_urlenp_query; + + /** + * Request body URLENCODED parser. Available only when the request body is in the + * application/x-www-form-urlencoded format and the parser was configured to run. + */ + htp_urlenp_t *request_urlenp_body; + + /** + * Request body MULTIPART parser. Available only when the body is in the + * multipart/form-data format and the parser was configured to run. + */ + htp_mpartp_t *request_mpartp; + + /** Request parameters. */ + htp_table_t *request_params; + + /** Request cookies */ + htp_table_t *request_cookies; + + /** Authentication type used in the request. */ + enum htp_auth_type_t request_auth_type; + + /** Authentication username. */ + bstr *request_auth_username; + + /** Authentication password. Available only when htp_tx_t::request_auth_type is HTP_AUTH_BASIC. */ + bstr *request_auth_password; + + /** + * Request hostname. Per the RFC, the hostname will be taken from the Host header + * when available. If the host information is also available in the URI, it is used + * instead of whatever might be in the Host header. Can be NULL. This field does + * not contain port information. + */ + bstr *request_hostname; + + /** + * Request port number, if presented. The rules for htp_tx_t::request_host apply. Set to + * -1 by default. + */ + int request_port_number; + + + // Response fields + + /** How many empty lines did we ignore before reaching the status line? */ + unsigned int response_ignored_lines; + + /** Response line. */ + bstr *response_line; + + /** Response protocol, as text. Can be NULL. */ + bstr *response_protocol; + + /** + * Response protocol as number. Available only if we were able to parse the protocol version, + * HTP_PROTOCOL_INVALID otherwise. HTP_PROTOCOL_UNKNOWN until parsing is attempted. + */ + int response_protocol_number; + + /** + * Response status code, as text. Starts as NULL and can remain NULL on + * an invalid response that does not specify status code. + */ + bstr *response_status; + + /** + * Response status code, available only if we were able to parse it, HTP_STATUS_INVALID + * otherwise. HTP_STATUS_UNKNOWN until parsing is attempted. + */ + int response_status_number; + + /** + * This field is set by the protocol decoder with it thinks that the + * backend server will reject a request with a particular status code. + */ + int response_status_expected_number; + + /** The message associated with the response status code. Can be NULL. */ + bstr *response_message; + + /** Have we seen the server respond with a 100 response? */ + int seen_100continue; + + /** Parsed response headers. Contains instances of htp_header_t. */ + htp_table_t *response_headers; + + /* HTTP 1.1 RFC + * + * 4.3 Message Body + * + * The message-body (if any) of an HTTP message is used to carry the + * entity-body associated with the request or response. The message-body + * differs from the entity-body only when a transfer-coding has been + * applied, as indicated by the Transfer-Encoding header field (section + * 14.41). + * + * message-body = entity-body + * | <entity-body encoded as per Transfer-Encoding> + */ + + /** + * The length of the response message-body. In most cases, this value + * will be the same as response_entity_len. The values will be different + * if response compression or chunking were applied. In that case, + * response_message_len contains the length of the response body as it + * has been seen over TCP; response_entity_len contains the length after + * de-chunking and decompression. + */ + int64_t response_message_len; + + /** + * The length of the response entity-body. In most cases, this value + * will be the same as response_message_len. The values will be different + * if request compression or chunking were applied. In that case, + * response_message_len contains the length of the response body as it + * has been seen over TCP; response_entity_len contains length after + * de-chunking and decompression. + */ + int64_t response_entity_len; + + /** + * Contains the value specified in the Content-Length header. The value of this + * field will be -1 from the beginning of the transaction and until response + * headers are processed. It will stay -1 if the C-L header was not provided, + * or if the value in it cannot be parsed. + */ + int64_t response_content_length; + + /** + * Response transfer coding, which indicates if there is a response body, + * and how it is transported (e.g., as-is, or chunked). + */ + enum htp_transfer_coding_t response_transfer_coding; + + /** + * Response body compression, which indicates if compression is used + * for the response body. This field is an interpretation of the information + * available in response headers. + */ + enum htp_content_encoding_t response_content_encoding; + + /** + * Response body compression processing information, which is related to how + * the library is going to process (or has processed) a response body. Changing + * this field mid-processing can influence library actions. For example, setting + * this field to HTP_COMPRESSION_NONE in a RESPONSE_HEADERS callback will prevent + * decompression. + */ + enum htp_content_encoding_t response_content_encoding_processing; + + /** + * This field will contain the response content type when that information + * is available in response headers. The contents of the field will be converted + * to lowercase and any parameters (e.g., character set information) removed. + */ + bstr *response_content_type; + + + // Common fields + + /** + * Parsing flags; a combination of: HTP_REQUEST_INVALID_T_E, HTP_INVALID_FOLDING, + * HTP_REQUEST_SMUGGLING, HTP_MULTI_PACKET_HEAD, and HTP_FIELD_UNPARSEABLE. + */ + uint64_t flags; + + /** Request progress. */ + enum htp_tx_req_progress_t request_progress; + + /** Response progress. */ + enum htp_tx_res_progress_t response_progress; + + /** Transaction index on the connection. */ + size_t index; + + /** Total repetitions for headers in request. */ + uint16_t req_header_repetitions; + + /** Total repetitions for headers in response. */ + uint16_t res_header_repetitions; +}; + +/** + * This structure is used to pass transaction data (for example + * request and response body buffers) to callbacks. + */ +struct htp_tx_data_t { + /** Transaction pointer. */ + htp_tx_t *tx; + + /** Pointer to the data buffer. */ + const unsigned char *data; + + /** Buffer length. */ + size_t len; + + /** + * Indicator if this chunk of data is the last in the series. Currently + * used only by REQUEST_HEADER_DATA, REQUEST_TRAILER_DATA, RESPONSE_HEADER_DATA, + * and RESPONSE_TRAILER_DATA callbacks. + */ + int is_last; +}; + +/** + * URI structure. Each of the fields provides access to a single + * URI element. Where an element is not present in a URI, the + * corresponding field will be set to NULL or -1, depending on the + * field type. + */ +struct htp_uri_t { + /** Scheme, e.g., "http". */ + bstr *scheme; + + /** Username. */ + bstr *username; + + /** Password. */ + bstr *password; + + /** Hostname. */ + bstr *hostname; + + /** Port, as string. */ + bstr *port; + + /** + * Port, as number. This field will contain HTP_PORT_NONE if there was + * no port information in the URI and HTP_PORT_INVALID if the port information + * was invalid (e.g., it's not a number or it falls out of range. + */ + int port_number; + + /** The path part of this URI. */ + bstr *path; + + /** Query string. */ + bstr *query; + + /** + * Fragment identifier. This field will rarely be available in a server-side + * setting, but it's not impossible to see it. */ + bstr *fragment; +}; + +/** + * Frees all data contained in the uri, and then the uri itself. + * + * @param[in] uri + */ +void htp_uri_free(htp_uri_t *uri); + +/** + * Allocates and initializes a new htp_uri_t structure. + * + * @return New structure, or NULL on memory allocation failure. + */ +htp_uri_t *htp_uri_alloc(void); + +/** + * Creates a new log entry and stores it with the connection. The file and line + * parameters are typically auto-generated using the HTP_LOG_MARK macro. +* + * @param[in] connp + * @param[in] file + * @param[in] line + * @param[in] level + * @param[in] code + * @param[in] fmt + * @param[in] ... + */ +void htp_log(htp_connp_t *connp, const char *file, int line, enum htp_log_level_t level, int code, const char *fmt, ...); + +/** + * Performs in-place decoding of the input string, according to the configuration specified + * by cfg and ctx. On output, various flags (HTP_URLEN_*) might be set. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] input + * @param[out] flags + * + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_urldecode_inplace(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags); + +/** + * Performs in-place decoding of the input string, according to the configuration specified + * by cfg and ctx. On output, various flags (HTP_URLEN_*) might be set. If something in the + * input would cause a particular server to respond with an error, the appropriate status + * code will be set. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] input + * @param[out] flags + * @param[out] expected_status_code 0 by default, or status code as necessary + * + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_urldecode_inplace_ex(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags, int *expected_status_code); + +/** + * Returns the LibHTP version string. + * + * @return LibHTP version, for example "LibHTP v0.5.x". + */ +char *htp_get_version(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_H */ diff --git a/htp/htp_base64.c b/htp/htp_base64.c new file mode 100644 index 0000000..75dc122 --- /dev/null +++ b/htp/htp_base64.c @@ -0,0 +1,196 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +/* Adapted from the libb64 project (http://sourceforge.net/projects/libb64), which is in public domain. */ + +#include "bstr.h" +#include "htp_base64.h" + +/** + * Decode single base64-encoded character. + * + * @param[in] value_in + * @return decoded character + */ +int htp_base64_decode_single(signed char value_in) { + static const signed char decoding[] = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + static const signed char decoding_size = sizeof (decoding); + + value_in -= 43; + + if ((value_in < 0) || (value_in > decoding_size - 1)) return -1; + + return decoding[(int) value_in]; +} + +/** + * Initialize base64 decoder. + * + * @param[in] decoder + */ +void htp_base64_decoder_init(htp_base64_decoder *decoder) { + decoder->step = step_a; + decoder->plainchar = 0; +} + +/** + * Feed the supplied memory range to the decoder. + * + * @param[in] decoder + * @param[in] _code_in + * @param[in] length_in + * @param[in] _plaintext_out + * @param[in] length_out + * @return how many bytes were placed into plaintext output + */ +int htp_base64_decode(htp_base64_decoder *decoder, const void *_code_in, int length_in, void *_plaintext_out, int length_out) { + const unsigned char *code_in = (const unsigned char *)_code_in; + unsigned char *plaintext_out = (unsigned char *)_plaintext_out; + const unsigned char *codechar = code_in; + unsigned char *plainchar = plaintext_out; + signed char fragment; + + if (length_out <= 0) return 0; + + *plainchar = decoder->plainchar; + + switch (decoder->step) { + while (1) { + case step_a: + do { + if (codechar == code_in + length_in) { + decoder->step = step_a; + decoder->plainchar = *plainchar; + return (int) (plainchar - plaintext_out); + } + fragment = (char) htp_base64_decode_single(*codechar++); + } while (fragment < 0); + *plainchar = (unsigned char) ((fragment & 0x03f) << 2); + /* fall through */ + + case step_b: + do { + if (codechar == code_in + length_in) { + decoder->step = step_b; + decoder->plainchar = *plainchar; + return (int) (plainchar - plaintext_out); + } + fragment = (char) htp_base64_decode_single(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (unsigned char) ((fragment & 0x00f) << 4); + if (--length_out == 0) { + return (int) (plainchar - plaintext_out); + } + /* fall through */ + + case step_c: + do { + if (codechar == code_in + length_in) { + decoder->step = step_c; + decoder->plainchar = *plainchar; + return (int) (plainchar - plaintext_out); + } + fragment = (char) htp_base64_decode_single(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (unsigned char) ((fragment & 0x003) << 6); + if (--length_out == 0) { + return (int) (plainchar - plaintext_out); + } + /* fall through */ + + case step_d: + do { + if (codechar == code_in + length_in) { + decoder->step = step_d; + decoder->plainchar = *plainchar; + return (int) (plainchar - plaintext_out); + } + fragment = (char) htp_base64_decode_single(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + if (--length_out == 0) { + return (int) (plainchar - plaintext_out); + } + /* fall through */ + } + } + + /* control should not reach here */ + return plainchar - plaintext_out; +} + +/** + * Base64-decode input, given as bstring. + * + * @param[in] input + * @return new base64-decoded bstring + */ +bstr *htp_base64_decode_bstr(bstr *input) { + return htp_base64_decode_mem(bstr_ptr(input), bstr_len(input)); +} + +/** + * Base64-decode input, given as memory range. + * + * @param[in] data + * @param[in] len + * @return new base64-decoded bstring + */ +bstr *htp_base64_decode_mem(const void *data, size_t len) { + htp_base64_decoder decoder; + bstr *r = NULL; + + htp_base64_decoder_init(&decoder); + + unsigned char *tmpstr = malloc(len); + if (tmpstr == NULL) return NULL; + + int resulting_len = htp_base64_decode(&decoder, data, (int) len, tmpstr, (int) len); + if (resulting_len > 0) { + r = bstr_dup_mem(tmpstr, resulting_len); + } + + free(tmpstr); + + return r; +} diff --git a/htp/htp_base64.h b/htp/htp_base64.h new file mode 100644 index 0000000..8978e7a --- /dev/null +++ b/htp/htp_base64.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +/* Adapted from the libb64 project (http://sourceforge.net/projects/libb64), which is in public domain. */ + +#ifndef _HTP_BASE64_H +#define _HTP_BASE64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bstr.h" + +typedef enum { + step_a, step_b, step_c, step_d +} htp_base64_decodestep; + +typedef struct { + htp_base64_decodestep step; + char plainchar; +} htp_base64_decoder; + +void htp_base64_decoder_init(htp_base64_decoder *state_in); + +int htp_base64_decode_single(signed char value_in); + +int htp_base64_decode(htp_base64_decoder *decoder, const void *code_in, int length_in, void *plaintext_out, int length_out); + +bstr *htp_base64_decode_bstr(bstr *input); + +bstr *htp_base64_decode_mem(const void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_BASE64_H */ + diff --git a/htp/htp_config.c b/htp/htp_config.c new file mode 100644 index 0000000..00ae853 --- /dev/null +++ b/htp/htp_config.c @@ -0,0 +1,954 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * This map is used by default for best-fit mapping from the Unicode + * values U+0100-FFFF. + */ +static unsigned char bestfit_1252[] = { + 0x01, 0x00, 0x41, 0x01, 0x01, 0x61, 0x01, 0x02, 0x41, 0x01, 0x03, 0x61, + 0x01, 0x04, 0x41, 0x01, 0x05, 0x61, 0x01, 0x06, 0x43, 0x01, 0x07, 0x63, + 0x01, 0x08, 0x43, 0x01, 0x09, 0x63, 0x01, 0x0a, 0x43, 0x01, 0x0b, 0x63, + 0x01, 0x0c, 0x43, 0x01, 0x0d, 0x63, 0x01, 0x0e, 0x44, 0x01, 0x0f, 0x64, + 0x01, 0x11, 0x64, 0x01, 0x12, 0x45, 0x01, 0x13, 0x65, 0x01, 0x14, 0x45, + 0x01, 0x15, 0x65, 0x01, 0x16, 0x45, 0x01, 0x17, 0x65, 0x01, 0x18, 0x45, + 0x01, 0x19, 0x65, 0x01, 0x1a, 0x45, 0x01, 0x1b, 0x65, 0x01, 0x1c, 0x47, + 0x01, 0x1d, 0x67, 0x01, 0x1e, 0x47, 0x01, 0x1f, 0x67, 0x01, 0x20, 0x47, + 0x01, 0x21, 0x67, 0x01, 0x22, 0x47, 0x01, 0x23, 0x67, 0x01, 0x24, 0x48, + 0x01, 0x25, 0x68, 0x01, 0x26, 0x48, 0x01, 0x27, 0x68, 0x01, 0x28, 0x49, + 0x01, 0x29, 0x69, 0x01, 0x2a, 0x49, 0x01, 0x2b, 0x69, 0x01, 0x2c, 0x49, + 0x01, 0x2d, 0x69, 0x01, 0x2e, 0x49, 0x01, 0x2f, 0x69, 0x01, 0x30, 0x49, + 0x01, 0x31, 0x69, 0x01, 0x34, 0x4a, 0x01, 0x35, 0x6a, 0x01, 0x36, 0x4b, + 0x01, 0x37, 0x6b, 0x01, 0x39, 0x4c, 0x01, 0x3a, 0x6c, 0x01, 0x3b, 0x4c, + 0x01, 0x3c, 0x6c, 0x01, 0x3d, 0x4c, 0x01, 0x3e, 0x6c, 0x01, 0x41, 0x4c, + 0x01, 0x42, 0x6c, 0x01, 0x43, 0x4e, 0x01, 0x44, 0x6e, 0x01, 0x45, 0x4e, + 0x01, 0x46, 0x6e, 0x01, 0x47, 0x4e, 0x01, 0x48, 0x6e, 0x01, 0x4c, 0x4f, + 0x01, 0x4d, 0x6f, 0x01, 0x4e, 0x4f, 0x01, 0x4f, 0x6f, 0x01, 0x50, 0x4f, + 0x01, 0x51, 0x6f, 0x01, 0x54, 0x52, 0x01, 0x55, 0x72, 0x01, 0x56, 0x52, + 0x01, 0x57, 0x72, 0x01, 0x58, 0x52, 0x01, 0x59, 0x72, 0x01, 0x5a, 0x53, + 0x01, 0x5b, 0x73, 0x01, 0x5c, 0x53, 0x01, 0x5d, 0x73, 0x01, 0x5e, 0x53, + 0x01, 0x5f, 0x73, 0x01, 0x62, 0x54, 0x01, 0x63, 0x74, 0x01, 0x64, 0x54, + 0x01, 0x65, 0x74, 0x01, 0x66, 0x54, 0x01, 0x67, 0x74, 0x01, 0x68, 0x55, + 0x01, 0x69, 0x75, 0x01, 0x6a, 0x55, 0x01, 0x6b, 0x75, 0x01, 0x6c, 0x55, + 0x01, 0x6d, 0x75, 0x01, 0x6e, 0x55, 0x01, 0x6f, 0x75, 0x01, 0x70, 0x55, + 0x01, 0x71, 0x75, 0x01, 0x72, 0x55, 0x01, 0x73, 0x75, 0x01, 0x74, 0x57, + 0x01, 0x75, 0x77, 0x01, 0x76, 0x59, 0x01, 0x77, 0x79, 0x01, 0x79, 0x5a, + 0x01, 0x7b, 0x5a, 0x01, 0x7c, 0x7a, 0x01, 0x80, 0x62, 0x01, 0x97, 0x49, + 0x01, 0x9a, 0x6c, 0x01, 0x9f, 0x4f, 0x01, 0xa0, 0x4f, 0x01, 0xa1, 0x6f, + 0x01, 0xab, 0x74, 0x01, 0xae, 0x54, 0x01, 0xaf, 0x55, 0x01, 0xb0, 0x75, + 0x01, 0xb6, 0x7a, 0x01, 0xc0, 0x7c, 0x01, 0xc3, 0x21, 0x01, 0xcd, 0x41, + 0x01, 0xce, 0x61, 0x01, 0xcf, 0x49, 0x01, 0xd0, 0x69, 0x01, 0xd1, 0x4f, + 0x01, 0xd2, 0x6f, 0x01, 0xd3, 0x55, 0x01, 0xd4, 0x75, 0x01, 0xd5, 0x55, + 0x01, 0xd6, 0x75, 0x01, 0xd7, 0x55, 0x01, 0xd8, 0x75, 0x01, 0xd9, 0x55, + 0x01, 0xda, 0x75, 0x01, 0xdb, 0x55, 0x01, 0xdc, 0x75, 0x01, 0xde, 0x41, + 0x01, 0xdf, 0x61, 0x01, 0xe4, 0x47, 0x01, 0xe5, 0x67, 0x01, 0xe6, 0x47, + 0x01, 0xe7, 0x67, 0x01, 0xe8, 0x4b, 0x01, 0xe9, 0x6b, 0x01, 0xea, 0x4f, + 0x01, 0xeb, 0x6f, 0x01, 0xec, 0x4f, 0x01, 0xed, 0x6f, 0x01, 0xf0, 0x6a, + 0x02, 0x61, 0x67, 0x02, 0xb9, 0x27, 0x02, 0xba, 0x22, 0x02, 0xbc, 0x27, + 0x02, 0xc4, 0x5e, 0x02, 0xc8, 0x27, 0x02, 0xcb, 0x60, 0x02, 0xcd, 0x5f, + 0x03, 0x00, 0x60, 0x03, 0x02, 0x5e, 0x03, 0x03, 0x7e, 0x03, 0x0e, 0x22, + 0x03, 0x31, 0x5f, 0x03, 0x32, 0x5f, 0x03, 0x7e, 0x3b, 0x03, 0x93, 0x47, + 0x03, 0x98, 0x54, 0x03, 0xa3, 0x53, 0x03, 0xa6, 0x46, 0x03, 0xa9, 0x4f, + 0x03, 0xb1, 0x61, 0x03, 0xb4, 0x64, 0x03, 0xb5, 0x65, 0x03, 0xc0, 0x70, + 0x03, 0xc3, 0x73, 0x03, 0xc4, 0x74, 0x03, 0xc6, 0x66, 0x04, 0xbb, 0x68, + 0x05, 0x89, 0x3a, 0x06, 0x6a, 0x25, 0x20, 0x00, 0x20, 0x20, 0x01, 0x20, + 0x20, 0x02, 0x20, 0x20, 0x03, 0x20, 0x20, 0x04, 0x20, 0x20, 0x05, 0x20, + 0x20, 0x06, 0x20, 0x20, 0x10, 0x2d, 0x20, 0x11, 0x2d, 0x20, 0x17, 0x3d, + 0x20, 0x32, 0x27, 0x20, 0x35, 0x60, 0x20, 0x44, 0x2f, 0x20, 0x74, 0x34, + 0x20, 0x75, 0x35, 0x20, 0x76, 0x36, 0x20, 0x77, 0x37, 0x20, 0x78, 0x38, + 0x20, 0x7f, 0x6e, 0x20, 0x80, 0x30, 0x20, 0x81, 0x31, 0x20, 0x82, 0x32, + 0x20, 0x83, 0x33, 0x20, 0x84, 0x34, 0x20, 0x85, 0x35, 0x20, 0x86, 0x36, + 0x20, 0x87, 0x37, 0x20, 0x88, 0x38, 0x20, 0x89, 0x39, 0x20, 0xa7, 0x50, + 0x21, 0x02, 0x43, 0x21, 0x07, 0x45, 0x21, 0x0a, 0x67, 0x21, 0x0b, 0x48, + 0x21, 0x0c, 0x48, 0x21, 0x0d, 0x48, 0x21, 0x0e, 0x68, 0x21, 0x10, 0x49, + 0x21, 0x11, 0x49, 0x21, 0x12, 0x4c, 0x21, 0x13, 0x6c, 0x21, 0x15, 0x4e, + 0x21, 0x18, 0x50, 0x21, 0x19, 0x50, 0x21, 0x1a, 0x51, 0x21, 0x1b, 0x52, + 0x21, 0x1c, 0x52, 0x21, 0x1d, 0x52, 0x21, 0x24, 0x5a, 0x21, 0x28, 0x5a, + 0x21, 0x2a, 0x4b, 0x21, 0x2c, 0x42, 0x21, 0x2d, 0x43, 0x21, 0x2e, 0x65, + 0x21, 0x2f, 0x65, 0x21, 0x30, 0x45, 0x21, 0x31, 0x46, 0x21, 0x33, 0x4d, + 0x21, 0x34, 0x6f, 0x22, 0x12, 0x2d, 0x22, 0x15, 0x2f, 0x22, 0x16, 0x5c, + 0x22, 0x17, 0x2a, 0x22, 0x1a, 0x76, 0x22, 0x1e, 0x38, 0x22, 0x23, 0x7c, + 0x22, 0x29, 0x6e, 0x22, 0x36, 0x3a, 0x22, 0x3c, 0x7e, 0x22, 0x61, 0x3d, + 0x22, 0x64, 0x3d, 0x22, 0x65, 0x3d, 0x23, 0x03, 0x5e, 0x23, 0x20, 0x28, + 0x23, 0x21, 0x29, 0x23, 0x29, 0x3c, 0x23, 0x2a, 0x3e, 0x25, 0x00, 0x2d, + 0x25, 0x0c, 0x2b, 0x25, 0x10, 0x2b, 0x25, 0x14, 0x2b, 0x25, 0x18, 0x2b, + 0x25, 0x1c, 0x2b, 0x25, 0x2c, 0x2d, 0x25, 0x34, 0x2d, 0x25, 0x3c, 0x2b, + 0x25, 0x50, 0x2d, 0x25, 0x52, 0x2b, 0x25, 0x53, 0x2b, 0x25, 0x54, 0x2b, + 0x25, 0x55, 0x2b, 0x25, 0x56, 0x2b, 0x25, 0x57, 0x2b, 0x25, 0x58, 0x2b, + 0x25, 0x59, 0x2b, 0x25, 0x5a, 0x2b, 0x25, 0x5b, 0x2b, 0x25, 0x5c, 0x2b, + 0x25, 0x5d, 0x2b, 0x25, 0x64, 0x2d, 0x25, 0x65, 0x2d, 0x25, 0x66, 0x2d, + 0x25, 0x67, 0x2d, 0x25, 0x68, 0x2d, 0x25, 0x69, 0x2d, 0x25, 0x6a, 0x2b, + 0x25, 0x6b, 0x2b, 0x25, 0x6c, 0x2b, 0x25, 0x84, 0x5f, 0x27, 0x58, 0x7c, + 0x30, 0x00, 0x20, 0x30, 0x08, 0x3c, 0x30, 0x09, 0x3e, 0x30, 0x1a, 0x5b, + 0x30, 0x1b, 0x5d, 0xff, 0x01, 0x21, 0xff, 0x02, 0x22, 0xff, 0x03, 0x23, + 0xff, 0x04, 0x24, 0xff, 0x05, 0x25, 0xff, 0x06, 0x26, 0xff, 0x07, 0x27, + 0xff, 0x08, 0x28, 0xff, 0x09, 0x29, 0xff, 0x0a, 0x2a, 0xff, 0x0b, 0x2b, + 0xff, 0x0c, 0x2c, 0xff, 0x0d, 0x2d, 0xff, 0x0e, 0x2e, 0xff, 0x0f, 0x2f, + 0xff, 0x10, 0x30, 0xff, 0x11, 0x31, 0xff, 0x12, 0x32, 0xff, 0x13, 0x33, + 0xff, 0x14, 0x34, 0xff, 0x15, 0x35, 0xff, 0x16, 0x36, 0xff, 0x17, 0x37, + 0xff, 0x18, 0x38, 0xff, 0x19, 0x39, 0xff, 0x1a, 0x3a, 0xff, 0x1b, 0x3b, + 0xff, 0x1c, 0x3c, 0xff, 0x1d, 0x3d, 0xff, 0x1e, 0x3e, 0xff, 0x20, 0x40, + 0xff, 0x21, 0x41, 0xff, 0x22, 0x42, 0xff, 0x23, 0x43, 0xff, 0x24, 0x44, + 0xff, 0x25, 0x45, 0xff, 0x26, 0x46, 0xff, 0x27, 0x47, 0xff, 0x28, 0x48, + 0xff, 0x29, 0x49, 0xff, 0x2a, 0x4a, 0xff, 0x2b, 0x4b, 0xff, 0x2c, 0x4c, + 0xff, 0x2d, 0x4d, 0xff, 0x2e, 0x4e, 0xff, 0x2f, 0x4f, 0xff, 0x30, 0x50, + 0xff, 0x31, 0x51, 0xff, 0x32, 0x52, 0xff, 0x33, 0x53, 0xff, 0x34, 0x54, + 0xff, 0x35, 0x55, 0xff, 0x36, 0x56, 0xff, 0x37, 0x57, 0xff, 0x38, 0x58, + 0xff, 0x39, 0x59, 0xff, 0x3a, 0x5a, 0xff, 0x3b, 0x5b, 0xff, 0x3c, 0x5c, + 0xff, 0x3d, 0x5d, 0xff, 0x3e, 0x5e, 0xff, 0x3f, 0x5f, 0xff, 0x40, 0x60, + 0xff, 0x41, 0x61, 0xff, 0x42, 0x62, 0xff, 0x43, 0x63, 0xff, 0x44, 0x64, + 0xff, 0x45, 0x65, 0xff, 0x46, 0x66, 0xff, 0x47, 0x67, 0xff, 0x48, 0x68, + 0xff, 0x49, 0x69, 0xff, 0x4a, 0x6a, 0xff, 0x4b, 0x6b, 0xff, 0x4c, 0x6c, + 0xff, 0x4d, 0x6d, 0xff, 0x4e, 0x6e, 0xff, 0x4f, 0x6f, 0xff, 0x50, 0x70, + 0xff, 0x51, 0x71, 0xff, 0x52, 0x72, 0xff, 0x53, 0x73, 0xff, 0x54, 0x74, + 0xff, 0x55, 0x75, 0xff, 0x56, 0x76, 0xff, 0x57, 0x77, 0xff, 0x58, 0x78, + 0xff, 0x59, 0x79, 0xff, 0x5a, 0x7a, 0xff, 0x5b, 0x7b, 0xff, 0x5c, 0x7c, + 0xff, 0x5d, 0x7d, 0xff, 0x5e, 0x7e, 0x00, 0x00, 0x00 +}; + +htp_cfg_t *htp_config_create(void) { + htp_cfg_t *cfg = calloc(1, sizeof (htp_cfg_t)); + if (cfg == NULL) return NULL; + + cfg->field_limit_hard = HTP_FIELD_LIMIT_HARD; + cfg->field_limit_soft = HTP_FIELD_LIMIT_SOFT; + cfg->log_level = HTP_LOG_NOTICE; + cfg->response_decompression_enabled = 1; + cfg->request_decompression_enabled = 0; // disabled by default + cfg->parse_request_cookies = 1; + cfg->parse_request_auth = 1; + cfg->extract_request_files = 0; + cfg->extract_request_files_limit = -1; // Use the parser default. + cfg->response_decompression_layer_limit = 2; // 2 layers seem fairly common + cfg->lzma_memlimit = HTP_LZMA_MEMLIMIT; + cfg->response_lzma_layer_limit = 1; // default is only one layer + cfg->compression_bomb_limit = HTP_COMPRESSION_BOMB_LIMIT; + cfg->compression_time_limit = HTP_COMPRESSION_TIME_LIMIT_USEC; + cfg->allow_space_uri = 0; + + // Default settings for URL-encoded data. + + htp_config_set_bestfit_map(cfg, HTP_DECODER_DEFAULTS, bestfit_1252); + htp_config_set_bestfit_replacement_byte(cfg, HTP_DECODER_DEFAULTS, '?'); + + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_config_set_nul_raw_terminates(cfg, HTP_DECODER_DEFAULTS, 0); + htp_config_set_nul_encoded_terminates(cfg, HTP_DECODER_DEFAULTS, 0); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 0); + + htp_config_set_plusspace_decode(cfg, HTP_DECODER_URLENCODED, 1); + + htp_config_set_server_personality(cfg, HTP_SERVER_MINIMAL); + + return cfg; +} + +htp_cfg_t *htp_config_copy(htp_cfg_t *cfg) { + if (cfg == NULL) return NULL; + + // Start by making a copy of the entire structure, + // which is essentially a shallow copy. + htp_cfg_t *copy = malloc(sizeof (htp_cfg_t)); + if (copy == NULL) return NULL; + memcpy(copy, cfg, sizeof (htp_cfg_t)); + + // Now create copies of the hooks' structures. + + if (cfg->hook_request_start != NULL) { + copy->hook_request_start = htp_hook_copy(cfg->hook_request_start); + if (copy->hook_request_start == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_line != NULL) { + copy->hook_request_line = htp_hook_copy(cfg->hook_request_line); + if (copy->hook_request_line == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_uri_normalize != NULL) { + copy->hook_request_uri_normalize = htp_hook_copy(cfg->hook_request_uri_normalize); + if (copy->hook_request_uri_normalize == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_header_data != NULL) { + copy->hook_request_header_data = htp_hook_copy(cfg->hook_request_header_data); + if (copy->hook_request_header_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_headers != NULL) { + copy->hook_request_headers = htp_hook_copy(cfg->hook_request_headers); + if (copy->hook_request_headers == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_body_data != NULL) { + copy->hook_request_body_data = htp_hook_copy(cfg->hook_request_body_data); + if (copy->hook_request_body_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_file_data != NULL) { + copy->hook_request_file_data = htp_hook_copy(cfg->hook_request_file_data); + if (copy->hook_request_file_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_trailer != NULL) { + copy->hook_request_trailer = htp_hook_copy(cfg->hook_request_trailer); + if (copy->hook_request_trailer == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_trailer_data != NULL) { + copy->hook_request_trailer_data = htp_hook_copy(cfg->hook_request_trailer_data); + if (copy->hook_request_trailer_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_request_complete != NULL) { + copy->hook_request_complete = htp_hook_copy(cfg->hook_request_complete); + if (copy->hook_request_complete == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_start != NULL) { + copy->hook_response_start = htp_hook_copy(cfg->hook_response_start); + if (copy->hook_response_start == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_line != NULL) { + copy->hook_response_line = htp_hook_copy(cfg->hook_response_line); + if (copy->hook_response_line == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_header_data != NULL) { + copy->hook_response_header_data = htp_hook_copy(cfg->hook_response_header_data); + if (copy->hook_response_header_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_headers != NULL) { + copy->hook_response_headers = htp_hook_copy(cfg->hook_response_headers); + if (copy->hook_response_headers == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_body_data != NULL) { + copy->hook_response_body_data = htp_hook_copy(cfg->hook_response_body_data); + if (copy->hook_response_body_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_trailer != NULL) { + copy->hook_response_trailer = htp_hook_copy(cfg->hook_response_trailer); + if (copy->hook_response_trailer == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_trailer_data != NULL) { + copy->hook_response_trailer_data = htp_hook_copy(cfg->hook_response_trailer_data); + if (copy->hook_response_trailer_data == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_response_complete != NULL) { + copy->hook_response_complete = htp_hook_copy(cfg->hook_response_complete); + if (copy->hook_response_complete == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_transaction_complete != NULL) { + copy->hook_transaction_complete = htp_hook_copy(cfg->hook_transaction_complete); + if (copy->hook_transaction_complete == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + if (cfg->hook_log != NULL) { + copy->hook_log = htp_hook_copy(cfg->hook_log); + if (copy->hook_log == NULL) { + htp_config_destroy(copy); + return NULL; + } + } + + return copy; +} + +void htp_config_destroy(htp_cfg_t *cfg) { + if (cfg == NULL) return; + + htp_hook_destroy(cfg->hook_request_start); + htp_hook_destroy(cfg->hook_request_line); + htp_hook_destroy(cfg->hook_request_uri_normalize); + htp_hook_destroy(cfg->hook_request_header_data); + htp_hook_destroy(cfg->hook_request_headers); + htp_hook_destroy(cfg->hook_request_body_data); + htp_hook_destroy(cfg->hook_request_file_data); + htp_hook_destroy(cfg->hook_request_trailer); + htp_hook_destroy(cfg->hook_request_trailer_data); + htp_hook_destroy(cfg->hook_request_complete); + htp_hook_destroy(cfg->hook_response_start); + htp_hook_destroy(cfg->hook_response_line); + htp_hook_destroy(cfg->hook_response_header_data); + htp_hook_destroy(cfg->hook_response_headers); + htp_hook_destroy(cfg->hook_response_body_data); + htp_hook_destroy(cfg->hook_response_trailer); + htp_hook_destroy(cfg->hook_response_trailer_data); + htp_hook_destroy(cfg->hook_response_complete); + htp_hook_destroy(cfg->hook_transaction_complete); + htp_hook_destroy(cfg->hook_log); + + free(cfg); +} + +void *htp_config_get_user_data(htp_cfg_t *cfg) { + if (cfg == NULL) return NULL; + return cfg->user_data; +} + +void htp_config_register_log(htp_cfg_t *cfg, int (*callback_fn)(htp_log_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_log, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_multipart_parser(htp_cfg_t *cfg) { + if (cfg == NULL) return; + htp_config_register_request_headers(cfg, htp_ch_multipart_callback_request_headers); +} + +void htp_config_register_request_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_complete, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_body_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_body_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_file_data(htp_cfg_t *cfg, int (*callback_fn)(htp_file_data_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_file_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_uri_normalize(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_uri_normalize, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_header_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_header_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_headers(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_headers, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_line(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_line, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_start(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_start, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_trailer(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_trailer, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_request_trailer_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *d)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_request_trailer_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_body_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_body_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_complete, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_header_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_header_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_headers(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_headers, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_line(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_line, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_start(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_start, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_trailer(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_trailer, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_response_trailer_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *d)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_response_trailer_data, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_transaction_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)) { + if (cfg == NULL) return; + htp_hook_register(&cfg->hook_transaction_complete, (htp_callback_fn_t) callback_fn); +} + +void htp_config_register_urlencoded_parser(htp_cfg_t *cfg) { + if (cfg == NULL) return; + htp_config_register_request_line(cfg, htp_ch_urlencoded_callback_request_line); + htp_config_register_request_headers(cfg, htp_ch_urlencoded_callback_request_headers); +} + +htp_status_t htp_config_set_extract_request_files(htp_cfg_t *cfg, int extract_request_files, int limit) { + if (cfg == NULL) return HTP_ERROR; + if (cfg->tmpdir == NULL) return HTP_ERROR; + cfg->extract_request_files = extract_request_files; + cfg->extract_request_files_limit = limit; + return HTP_OK; +} + +void htp_config_set_field_limits(htp_cfg_t *cfg, size_t soft_limit, size_t hard_limit) { + if (cfg == NULL) return; + cfg->field_limit_soft = soft_limit; + cfg->field_limit_hard = hard_limit; +} + +void htp_config_set_lzma_memlimit(htp_cfg_t *cfg, size_t memlimit) { + if (cfg == NULL) return; + cfg->lzma_memlimit = memlimit; +} + +void htp_config_set_lzma_layers(htp_cfg_t *cfg, int limit) { + if (cfg == NULL) return; + cfg->response_lzma_layer_limit = limit; +} + +void htp_config_set_max_tx(htp_cfg_t *cfg, uint32_t limit) { + if (cfg == NULL) return; + cfg->max_tx = limit; +} + +void htp_config_set_compression_bomb_limit(htp_cfg_t *cfg, size_t bomblimit) { + if (cfg == NULL) return; + if (bomblimit > INT32_MAX) { + cfg->compression_bomb_limit = INT32_MAX; + } else { + cfg->compression_bomb_limit = (int32_t) bomblimit; + } +} + +void htp_config_set_compression_time_limit(htp_cfg_t *cfg, size_t useclimit) { + if (cfg == NULL) return; + // max limit is one second + if (useclimit >= 1000000) { + cfg->compression_time_limit = 1000000; + } else { + cfg->compression_time_limit = (int32_t) useclimit; + } +} + +void htp_config_set_log_level(htp_cfg_t *cfg, enum htp_log_level_t log_level) { + if (cfg == NULL) return; + cfg->log_level = log_level; +} + +void htp_config_set_parse_request_auth(htp_cfg_t *cfg, int parse_request_auth) { + if (cfg == NULL) return; + cfg->parse_request_auth = parse_request_auth; +} + +void htp_config_set_parse_request_cookies(htp_cfg_t *cfg, int parse_request_cookies) { + if (cfg == NULL) return; + cfg->parse_request_cookies = parse_request_cookies; +} + +void htp_config_set_response_decompression(htp_cfg_t *cfg, int enabled) { + if (cfg == NULL) return; + cfg->response_decompression_enabled = enabled; +} + +void htp_config_set_request_decompression(htp_cfg_t *cfg, int enabled) { + if (cfg == NULL) return; + cfg->request_decompression_enabled = enabled; +} + +void htp_config_set_allow_space_uri(htp_cfg_t *cfg, int allow_space_uri) { + if (cfg == NULL) return; + cfg->allow_space_uri = allow_space_uri; +} + +int htp_config_set_server_personality(htp_cfg_t *cfg, enum htp_server_personality_t personality) { + if (cfg == NULL) return HTP_ERROR; + + switch (personality) { + + case HTP_SERVER_MINIMAL: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + break; + + case HTP_SERVER_GENERIC: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + break; + + case HTP_SERVER_IDS: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_convert_lowercase(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_requestline_leading_whitespace_unwanted(cfg, HTP_DECODER_DEFAULTS, HTP_UNWANTED_IGNORE); + break; + + case HTP_SERVER_APACHE_2: + cfg->parse_request_line = htp_parse_request_line_apache_2_2; + cfg->process_request_header = htp_process_request_header_apache_2_2; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 0); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 0); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URL_PATH, 0); + + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URL_PATH, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_config_set_url_encoding_invalid_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_400); + htp_config_set_control_chars_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_IGNORE); + htp_config_set_requestline_leading_whitespace_unwanted(cfg, HTP_DECODER_DEFAULTS, HTP_UNWANTED_400); + break; + + case HTP_SERVER_IIS_5_1: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URL_PATH, 0); + + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URL_PATH, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_config_set_control_chars_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_IGNORE); + htp_config_set_requestline_leading_whitespace_unwanted(cfg, HTP_DECODER_DEFAULTS, HTP_UNWANTED_IGNORE); + break; + + case HTP_SERVER_IIS_6_0: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URL_PATH, 1); + + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URL_PATH, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_config_set_u_encoding_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_400); + htp_config_set_control_chars_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_400); + htp_config_set_requestline_leading_whitespace_unwanted(cfg, HTP_DECODER_DEFAULTS, HTP_UNWANTED_IGNORE); + break; + + case HTP_SERVER_IIS_7_0: + case HTP_SERVER_IIS_7_5: + cfg->parse_request_line = htp_parse_request_line_generic; + cfg->process_request_header = htp_process_request_header_generic; + cfg->parse_response_line = htp_parse_response_line_generic; + cfg->process_response_header = htp_process_response_header_generic; + + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_path_separators_compress(cfg, HTP_DECODER_URL_PATH, 1); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URL_PATH, 1); + + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URL_PATH, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_config_set_url_encoding_invalid_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_400); + htp_config_set_control_chars_unwanted(cfg, HTP_DECODER_URL_PATH, HTP_UNWANTED_400); + htp_config_set_requestline_leading_whitespace_unwanted(cfg, HTP_DECODER_DEFAULTS, HTP_UNWANTED_IGNORE); + break; + + default: + return HTP_ERROR; + } + + // Remember the personality + cfg->server_personality = personality; + + return HTP_OK; +} + +void htp_config_set_tmpdir(htp_cfg_t *cfg, char *tmpdir) { + if (cfg == NULL) return; + cfg->tmpdir = tmpdir; +} + +void htp_config_set_tx_auto_destroy(htp_cfg_t *cfg, int tx_auto_destroy) { + if (cfg == NULL) return; + cfg->tx_auto_destroy = tx_auto_destroy; +} + +void htp_config_set_user_data(htp_cfg_t *cfg, void *user_data) { + if (cfg == NULL) return; + cfg->user_data = user_data; +} + + +static int convert_to_0_or_1(int b) { + if (b) return 1; + return 0; +} + +void htp_config_set_bestfit_map(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, void *map) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].bestfit_map = map; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].bestfit_map = map; + } + } +} + +void htp_config_set_bestfit_replacement_byte(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int b) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].bestfit_replacement_byte = (unsigned char) b; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].bestfit_replacement_byte = (unsigned char) b; + } + } +} + +void htp_config_set_url_encoding_invalid_handling(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_url_encoding_handling_t handling) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].url_encoding_invalid_handling = handling; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].url_encoding_invalid_handling = handling; + } + } +} + +void htp_config_set_nul_raw_terminates(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].nul_raw_terminates = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].nul_raw_terminates = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_nul_encoded_terminates(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].nul_encoded_terminates = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].nul_encoded_terminates = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_u_encoding_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].u_encoding_decode = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].u_encoding_decode = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_backslash_convert_slashes(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].backslash_convert_slashes = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].backslash_convert_slashes = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_path_separators_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].path_separators_decode = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].path_separators_decode = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_path_separators_compress(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].path_separators_compress = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].path_separators_compress = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_plusspace_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].plusspace_decode = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].plusspace_decode = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_convert_lowercase(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].convert_lowercase = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].convert_lowercase = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_utf8_convert_bestfit(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].utf8_convert_bestfit = convert_to_0_or_1(enabled); + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].utf8_convert_bestfit = convert_to_0_or_1(enabled); + } + } +} + +void htp_config_set_u_encoding_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].u_encoding_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].u_encoding_unwanted = unwanted; + } + } +} + +void htp_config_set_control_chars_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].u_encoding_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].u_encoding_unwanted = unwanted; + } + } +} + +void htp_config_set_url_encoding_invalid_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].url_encoding_invalid_unwanted = unwanted; + } + } +} + +void htp_config_set_nul_encoded_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].nul_encoded_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].nul_encoded_unwanted = unwanted; + } + } +} + +void htp_config_set_nul_raw_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].nul_raw_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].nul_raw_unwanted = unwanted; + } + } +} + +void htp_config_set_path_separators_encoded_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].path_separators_encoded_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].path_separators_encoded_unwanted = unwanted; + } + } +} + +void htp_config_set_utf8_invalid_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->decoder_cfgs[ctx].utf8_invalid_unwanted = unwanted; + + if (ctx == HTP_DECODER_DEFAULTS) { + for (size_t i = 0; i < HTP_DECODER_CONTEXTS_MAX; i++) { + cfg->decoder_cfgs[i].utf8_invalid_unwanted = unwanted; + } + } +} + +void htp_config_set_requestline_leading_whitespace_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted) { + if (ctx >= HTP_DECODER_CONTEXTS_MAX) return; + + cfg->requestline_leading_whitespace_unwanted = unwanted; +} + +void htp_config_set_response_decompression_layer_limit(htp_cfg_t *cfg, int limit) { + if (cfg == NULL) return; + cfg->response_decompression_layer_limit = limit; +} diff --git a/htp/htp_config.h b/htp/htp_config.h new file mode 100644 index 0000000..57544f7 --- /dev/null +++ b/htp/htp_config.h @@ -0,0 +1,719 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CONFIG_H +#define HTP_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp.h" + +/** + * Decoder contexts. + */ +enum htp_decoder_ctx_t { + /** Default settings. Settings applied to this context are propagated to all other contexts. */ + HTP_DECODER_DEFAULTS = 0, + + /** Urlencoded decoder settings. */ + HTP_DECODER_URLENCODED = 1, + + /** URL path decoder settings. */ + HTP_DECODER_URL_PATH = 2 +}; + +/** + * Enumerates the possible server personalities. + */ +enum htp_server_personality_t { + /** + * Minimal personality that performs at little work as possible. All optional + * features are disabled. This personality is a good starting point for customization. + */ + HTP_SERVER_MINIMAL = 0, + + /** A generic personality that aims to work reasonably well for all server types. */ + HTP_SERVER_GENERIC = 1, + + /** The IDS personality tries to perform as much decoding as possible. */ + HTP_SERVER_IDS = 2, + + /** Mimics the behavior of IIS 4.0, as shipped with Windows NT 4.0. */ + HTP_SERVER_IIS_4_0 = 3, + + /** Mimics the behavior of IIS 5.0, as shipped with Windows 2000. */ + HTP_SERVER_IIS_5_0 = 4, + + /** Mimics the behavior of IIS 5.1, as shipped with Windows XP Professional. */ + HTP_SERVER_IIS_5_1 = 5, + + /** Mimics the behavior of IIS 6.0, as shipped with Windows 2003. */ + HTP_SERVER_IIS_6_0 = 6, + + /** Mimics the behavior of IIS 7.0, as shipped with Windows 2008. */ + HTP_SERVER_IIS_7_0 = 7, + + /* Mimics the behavior of IIS 7.5, as shipped with Windows 7. */ + HTP_SERVER_IIS_7_5 = 8, + + /* Mimics the behavior of Apache 2.x. */ + HTP_SERVER_APACHE_2 = 9 +}; + +/** + * Enumerates the ways in which servers respond to malformed data. + */ +enum htp_unwanted_t { + + /** Ignores problem. */ + HTP_UNWANTED_IGNORE = 0, + + /** Responds with HTTP 400 status code. */ + HTP_UNWANTED_400 = 400, + + /** Responds with HTTP 404 status code. */ + HTP_UNWANTED_404 = 404 +}; + +/** + * Enumerates the possible approaches to handling invalid URL-encodings. + */ +enum htp_url_encoding_handling_t { + /** Ignore invalid URL encodings and leave the % in the data. */ + HTP_URL_DECODE_PRESERVE_PERCENT = 0, + + /** Ignore invalid URL encodings, but remove the % from the data. */ + HTP_URL_DECODE_REMOVE_PERCENT = 1, + + /** Decode invalid URL encodings. */ + HTP_URL_DECODE_PROCESS_INVALID = 2 +}; + +/** + * Creates a new configuration structure. Configuration structures created at + * configuration time must not be changed afterwards in order to support lock-less + * copying. + * + * @return New configuration structure. + */ +htp_cfg_t *htp_config_create(void); + +/** + * Creates a copy of the supplied configuration structure. The idea is to create + * one or more configuration objects at configuration-time, but to use this + * function to create per-connection copies. That way it will be possible to + * adjust per-connection configuration as necessary, without affecting the + * global configuration. Make sure no other thread changes the configuration + * object while this function is operating. + * + * @param[in] cfg + * @return A copy of the configuration structure. + */ +htp_cfg_t *htp_config_copy(htp_cfg_t *cfg); + +/** + * Destroy a configuration structure. + * + * @param[in] cfg + */ +void htp_config_destroy(htp_cfg_t *cfg); + +/** + * Retrieves user data associated with this configuration. + * + * @param[in] cfg + * @return User data pointer, or NULL if not set. + */ +void *htp_config_get_user_data(htp_cfg_t *cfg); + +/** + * Registers a callback that is invoked every time there is a log message with + * severity equal and higher than the configured log level. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_log(htp_cfg_t *cfg, int (*callback_fn)(htp_log_t *)); + +/** + * Adds the built-in Multipart parser to the configuration. This parser will extract information + * stored in request bodies, when they are in multipart/form-data format. + * + * @param[in] cfg + */ +void htp_config_register_multipart_parser(htp_cfg_t *cfg); + +/** + * Registers a REQUEST_START callback, which is invoked every time a new + * request begins and before any parsing is done. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_start(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a REQUEST_BODY_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_body_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Registers a REQUEST_COMPLETE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a REQUEST_FILE_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_file_data(htp_cfg_t *cfg, int (*callback_fn)(htp_file_data_t *)); + +/** + * Registers a REQUEST_HEADER_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_header_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Registers a REQUEST_HEADERS callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_headers(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a REQUEST_LINE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_line(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a REQUEST_URI_NORMALIZE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_uri_normalize(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a HTP_REQUEST_TRAILER callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_trailer(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a REQUEST_TRAILER_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_request_trailer_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *d)); + +/** + * Registers a RESPONSE_BODY_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_body_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Registers a RESPONSE_COMPLETE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a RESPONSE_HEADER_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_header_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Registers a RESPONSE_HEADERS callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_headers(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a RESPONSE_LINE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_line(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a RESPONSE_START callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_start(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a RESPONSE_TRAILER callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_trailer(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Registers a RESPONSE_TRAILER_DATA callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_response_trailer_data(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_data_t *d)); + +/** + * Registers a TRANSACTION_COMPLETE callback. + * + * @param[in] cfg + * @param[in] callback_fn + */ +void htp_config_register_transaction_complete(htp_cfg_t *cfg, int (*callback_fn)(htp_tx_t *)); + +/** + * Adds the built-in Urlencoded parser to the configuration. The parser will + * parse query strings and request bodies with the appropriate MIME type. + * + * @param[in] cfg + */ +void htp_config_register_urlencoded_parser(htp_cfg_t *cfg); + +/** + * Configures whether backslash characters are treated as path segment separators. They + * are not on Unix systems, but are on Windows systems. If this setting is enabled, a path + * such as "/one\two/three" will be converted to "/one/two/three". Implemented only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_backslash_convert_slashes(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures a best-fit map, which is used whenever characters longer than one byte + * need to be converted to a single-byte. By default a Windows 1252 best-fit map is used. + * The map is an list of triplets, the first 2 bytes being an UCS-2 character to map from, + * and the third byte being the single byte to map to. Make sure that your map contains + * the mappings to cover the full-width and half-width form characters (U+FF00-FFEF). The + * last triplet in the map must be all zeros (3 NUL bytes). + * + * @param[in] cfg + * @param[in] ctx + * @param[in] map + */ +void htp_config_set_bestfit_map(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, void *map); + +/** + * Sets the replacement character that will be used to in the lossy best-fit + * mapping from multi-byte to single-byte streams. The question mark character + * is used as the default replacement byte. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] replacement_byte + */ +void htp_config_set_bestfit_replacement_byte(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int replacement_byte); + +/** + * Controls reaction to raw control characters in the data. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_control_chars_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures whether input data will be converted to lowercase. Useful when set on the + * HTP_DECODER_URL_PATH context, in order to handle servers with case-insensitive filesystems. + * Implemented only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_convert_lowercase(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Enables or disables Multipart file extraction. This function can be invoked only + * after a previous htp_config_set_tmpdir() invocation. Otherwise, the configuration + * change will fail, and extraction will not be enabled. Disabled by default. Please + * note that the built-in file extraction implementation uses synchronous I/O, which + * means that it is not suitable for use in an event-driven container. There's an + * upper limit to how many files can be created on the filesystem during a single + * request. The limit exists in order to mitigate against a DoS attack with a + * Multipart payload that contains hundreds and thousands of files (it's cheap for the + * attacker to do this, but costly for the server to support it). The default limit + * may be pretty conservative. + * + * @param[in] cfg + * @param[in] extract_files 1 if you wish extraction to be enabled, 0 otherwise + * @param[in] limit the maximum number of files allowed; use -1 to use the parser default. + */ +htp_status_t htp_config_set_extract_request_files(htp_cfg_t *cfg, int extract_files, int limit); + +/** + * Configures the maximum size of the buffer LibHTP will use when all data is not available + * in the current buffer (e.g., a very long header line that might span several packets). This + * limit is controlled by the hard_limit parameter. The soft_limit parameter is not implemented. + * + * @param[in] cfg + * @param[in] soft_limit NOT IMPLEMENTED. + * @param[in] hard_limit + */ +void htp_config_set_field_limits(htp_cfg_t *cfg, size_t soft_limit, size_t hard_limit); + +/** + * Configures the maximum memlimit LibHTP will pass to liblzma. + * + * @param[in] cfg + * @param[in] memlimit + */ +void htp_config_set_lzma_memlimit(htp_cfg_t *cfg, size_t memlimit); + +/** + * Configures the maximum layers LibHTP will pass to liblzma. + * + * @param[in] cfg + * @param[in] limit + */ +void htp_config_set_lzma_layers(htp_cfg_t *cfg, int limit); + +/** + * Configures the maximum compression bomb size LibHTP will decompress. + * + * @param[in] cfg + * @param[in] bomblimit + */ +void htp_config_set_compression_bomb_limit(htp_cfg_t *cfg, size_t bomblimit); + +/** + * Configures the maximum compression bomb time LibHTP will decompress. + * + * @param[in] cfg + * @param[in] useclimit + */ +void htp_config_set_compression_time_limit(htp_cfg_t *cfg, size_t useclimit); + +/** + * Configures the maximum number of tx LibHTP will have per connection. + * + * @param[in] cfg + * @param[in] limit + */ +void htp_config_set_max_tx(htp_cfg_t *cfg, uint32_t limit); + +/** + * Configures the desired log level. + * + * @param[in] cfg + * @param[in] log_level + */ +void htp_config_set_log_level(htp_cfg_t *cfg, enum htp_log_level_t log_level); + +/** + * Configures how the server reacts to encoded NUL bytes. Some servers will stop at + * at NUL, while some will respond with 400 or 404. When the termination option is not + * used, the NUL byte will remain in the path. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_nul_encoded_terminates(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures reaction to encoded NUL bytes in input data. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_nul_encoded_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures the handling of raw NUL bytes. If enabled, raw NUL terminates strings. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_nul_raw_terminates(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures how the server reacts to raw NUL bytes. Some servers will terminate + * path at NUL, while some will respond with 400 or 404. When the termination option + * is not used, the NUL byte will remain in the data. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_nul_raw_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Enable or disable request HTTP Authentication parsing. Enabled by default. + * + * @param[in] cfg + * @param[in] parse_request_auth + */ +void htp_config_set_parse_request_auth(htp_cfg_t *cfg, int parse_request_auth); + +/** + * Enable or disable request cookie parsing. Enabled by default. + * + * @param[in] cfg + * @param[in] parse_request_cookies + */ +void htp_config_set_parse_request_cookies(htp_cfg_t *cfg, int parse_request_cookies); + +/** + * Enable or disable spaces in URIs. Disabled by default. + * + * @param[in] cfg + * @param[in] allow_space_uri + */ +void htp_config_set_allow_space_uri(htp_cfg_t *cfg, int allow_space_uri); + +/** + * Configures whether consecutive path segment separators will be compressed. When enabled, a path + * such as "/one//two" will be normalized to "/one/two". Backslash conversion and path segment separator + * decoding are carried out before compression. For example, the path "/one\\/two\/%5cthree/%2f//four" + * will be converted to "/one/two/three/four" (assuming all 3 options are enabled). Implemented only for + * HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_path_separators_compress(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures whether encoded path segment separators will be decoded. Apache does not do + * this by default, but IIS does. If enabled, a path such as "/one%2ftwo" will be normalized + * to "/one/two". If the backslash_separators option is also enabled, encoded backslash + * characters will be converted too (and subsequently normalized to forward slashes). Implemented + * only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_path_separators_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures reaction to encoded path separator characters (e.g., %2f). Implemented only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_path_separators_encoded_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures whether plus characters are converted to spaces when decoding URL-encoded strings. This + * is appropriate to do for parameters, but not for URLs. Only applies to contexts where decoding + * is taking place. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_plusspace_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Controls whether compressed response bodies will be automatically decompressed. + * + * @param[in] cfg + * @param[in] enabled set to 1 to enable decompression, 0 otherwise + */ +void htp_config_set_response_decompression(htp_cfg_t *cfg, int enabled); + +/** + * Controls whether compressed request bodies will be automatically decompressed. + * + * @param[in] cfg + * @param[in] enabled set to 1 to enable decompression, 0 otherwise + */ +void htp_config_set_request_decompression(htp_cfg_t *cfg, int enabled); + +/** + * Configure desired server personality. + * + * @param[in] cfg + * @param[in] personality + * @return HTP_OK if the personality is supported, HTP_ERROR if it isn't. + */ +htp_status_t htp_config_set_server_personality(htp_cfg_t *cfg, enum htp_server_personality_t personality); + +/** + * Configures the path where temporary files should be stored. Must be set + * in order to use the Multipart file extraction functionality. + * + * @param[in] cfg + * @param[in] tmpdir + */ +void htp_config_set_tmpdir(htp_cfg_t *cfg, char *tmpdir); + +/** + * Configures whether transactions will be automatically destroyed once they + * are processed and all callbacks invoked. This option is appropriate for + * programs that process transactions as they are processed. + * + * @param[in] cfg + * @param[in] tx_auto_destroy + */ +void htp_config_set_tx_auto_destroy(htp_cfg_t *cfg, int tx_auto_destroy); + +/** + * Associates provided opaque user data with the configuration. + * + * @param[in] cfg + * @param[in] user_data + */ +void htp_config_set_user_data(htp_cfg_t *cfg, void *user_data); + +/** + * Configures whether %u-encoded sequences are decoded. Such sequences + * will be treated as invalid URL encoding if decoding is not desirable. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_u_encoding_decode(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures reaction to %u-encoded sequences in input data. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_u_encoding_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures how the server handles to invalid URL encoding. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] handling + */ +void htp_config_set_url_encoding_invalid_handling(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_url_encoding_handling_t handling); + +/** + * Configures how the server reacts to invalid URL encoding. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_url_encoding_invalid_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Controls whether the data should be treated as UTF-8 and converted to a single-byte + * stream using best-fit mapping. Implemented only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] enabled + */ +void htp_config_set_utf8_convert_bestfit(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, int enabled); + +/** + * Configures how the server reacts to invalid UTF-8 characters. This setting does + * not affect path normalization; it only controls what response status will be expect for + * a request that contains invalid UTF-8 characters. Implemented only for HTP_DECODER_URL_PATH. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_utf8_invalid_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures how the server reacts to leading whitespace on the request line. + * + * @param[in] cfg + * @param[in] ctx + * @param[in] unwanted + */ +void htp_config_set_requestline_leading_whitespace_unwanted(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, enum htp_unwanted_t unwanted); + +/** + * Configures many layers of compression we try to decompress. + * + * @param[in] cfg + * @param[in] limit 0 disables limit + */ +void htp_config_set_response_decompression_layer_limit(htp_cfg_t *cfg, int limit); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CONFIG_H */ + diff --git a/htp/htp_config_auto.h b/htp/htp_config_auto.h new file mode 100644 index 0000000..7fbed4b --- /dev/null +++ b/htp/htp_config_auto.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * + * This file wraps the generated autoconf header to provide define + * blocks to prevent issue when included more than once. + * + * @warning Only include this in source files. + * + * @author Brian Rectanus <brectanus@qualys.com> + */ + +#ifndef _HTP_CONFIG_AUTO_H +#define _HTP_CONFIG_AUTO_H + +#ifdef HAVE_CONFIG_H +#include "htp_config_auto_gen.h" +#endif + +#endif /* _HTP_CONFIG_AUTO_H */ diff --git a/htp/htp_config_private.h b/htp/htp_config_private.h new file mode 100644 index 0000000..83ff8f6 --- /dev/null +++ b/htp/htp_config_private.h @@ -0,0 +1,373 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CONFIG_PRIVATE_H +#define HTP_CONFIG_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define HTP_DECODER_CONTEXTS_MAX 3 + +typedef struct htp_decoder_cfg_t { + + // Path-specific decoding options. + + /** Convert backslash characters to slashes. */ + int backslash_convert_slashes; + + /** Convert to lowercase. */ + int convert_lowercase; + + /** Compress slash characters. */ + int path_separators_compress; + + /** Should we URL-decode encoded path segment separators? */ + int path_separators_decode; + + /** Should we decode '+' characters to spaces? */ + int plusspace_decode; + + /** Reaction to encoded path separators. */ + enum htp_unwanted_t path_separators_encoded_unwanted; + + + // Special characters options. + + /** Controls how raw NUL bytes are handled. */ + int nul_raw_terminates; + + /** Determines server response to a raw NUL byte in the path. */ + enum htp_unwanted_t nul_raw_unwanted; + + /** Reaction to control characters. */ + enum htp_unwanted_t control_chars_unwanted; + + + // URL encoding options. + + /** Should we decode %u-encoded characters? */ + int u_encoding_decode; + + /** Reaction to %u encoding. */ + enum htp_unwanted_t u_encoding_unwanted; + + /** Handling of invalid URL encodings. */ + enum htp_url_encoding_handling_t url_encoding_invalid_handling; + + /** Reaction to invalid URL encoding. */ + enum htp_unwanted_t url_encoding_invalid_unwanted; + + /** Controls how encoded NUL bytes are handled. */ + int nul_encoded_terminates; + + /** How are we expected to react to an encoded NUL byte? */ + enum htp_unwanted_t nul_encoded_unwanted; + + + // UTF-8 options. + + /** Controls how invalid UTF-8 characters are handled. */ + enum htp_unwanted_t utf8_invalid_unwanted; + + /** Convert UTF-8 characters into bytes using best-fit mapping. */ + int utf8_convert_bestfit; + + + // Best-fit mapping options. + + /** The best-fit map to use to decode %u-encoded characters. */ + unsigned char *bestfit_map; + + /** The replacement byte used when there is no best-fit mapping. */ + unsigned char bestfit_replacement_byte; + +} htp_decoder_cfg_t; + +struct htp_cfg_t { + /** + * The maximum size of the buffer that is used when the current + * input chunk does not contain all the necessary data (e.g., a very header + * line that spans several packets). + */ + size_t field_limit_hard; + + /** + * Soft field limit length. If this limit is reached the parser will issue + * a warning but continue to run. NOT IMPLEMENTED. + */ + size_t field_limit_soft; + + /** + * Log level, which will be used when deciding whether to store or + * ignore the messages issued by the parser. + */ + enum htp_log_level_t log_level; + + /** + * Whether to delete each transaction after the last hook is invoked. This + * feature should be used when parsing traffic streams in real time. + */ + int tx_auto_destroy; + + /** + * Server personality identifier. + */ + enum htp_server_personality_t server_personality; + + /** The function used for request line parsing. Depends on the personality. */ + int (*parse_request_line)(htp_connp_t *connp); + + /** The function used for response line parsing. Depends on the personality. */ + int (*parse_response_line)(htp_connp_t *connp); + + /** The function used for request header parsing. Depends on the personality. */ + int (*process_request_header)(htp_connp_t *connp, unsigned char *data, size_t len); + + /** The function used for response header parsing. Depends on the personality. */ + int (*process_response_header)(htp_connp_t *connp, unsigned char *data, size_t len); + + /** The function to use to transform parameters after parsing. */ + int (*parameter_processor)(htp_param_t *param); + + /** Decoder configuration array, one per context. */ + htp_decoder_cfg_t decoder_cfgs[HTP_DECODER_CONTEXTS_MAX]; + + /** Whether to generate the request_uri_normalized field. */ + int generate_request_uri_normalized; + + /** Whether to decompress compressed response bodies. */ + int response_decompression_enabled; + + /** Not fully implemented at the moment. */ + char *request_encoding; + + /** Not fully implemented at the moment. */ + char *internal_encoding; + + /** Whether to parse request cookies. */ + int parse_request_cookies; + + /** Whether to parse HTTP Authentication headers. */ + int parse_request_auth; + + /** Whether to extract files from requests using Multipart encoding. */ + int extract_request_files; + + /** How many extracted files are allowed in a single Multipart request? */ + int extract_request_files_limit; + + /** Whether to allow spaces in URI. */ + int allow_space_uri; + + /** The location on disk where temporary files will be created. */ + char *tmpdir; + + // Hooks + + /** + * Request start hook, invoked when the parser receives the first byte of a new + * request. Because in HTTP a transaction always starts with a request, this hook + * doubles as a transaction start hook. + */ + htp_hook_t *hook_request_start; + + /** + * Request line hook, invoked after a request line has been parsed. + */ + htp_hook_t *hook_request_line; + + /** + * Request URI normalization hook, for overriding default normalization of URI. + */ + htp_hook_t *hook_request_uri_normalize; + + /** + * Receives raw request header data, starting immediately after the request line, + * including all headers as they are seen on the TCP connection, and including the + * terminating empty line. Not available on genuine HTTP/0.9 requests (because + * they don't use headers). + */ + htp_hook_t *hook_request_header_data; + + /** + * Request headers hook, invoked after all request headers are seen. + */ + htp_hook_t *hook_request_headers; + + /** + * Request body data hook, invoked every time body data is available. Each + * invocation will provide a htp_tx_data_t instance. Chunked data + * will be dechunked before the data is passed to this hook. Decompression + * is not currently implemented. At the end of the request body + * there will be a call with the data pointer set to NULL. + */ + htp_hook_t *hook_request_body_data; + + /** + * Request file data hook, which is invoked whenever request file data is + * available. Currently used only by the Multipart parser. + */ + htp_hook_t *hook_request_file_data; + + /** + * Receives raw request trailer data, which can be available on requests that have + * chunked bodies. The data starts immediately after the zero-length chunk + * and includes the terminating empty line. + */ + htp_hook_t *hook_request_trailer_data; + + /** + * Request trailer hook, invoked after all trailer headers are seen, + * and if they are seen (not invoked otherwise). + */ + htp_hook_t *hook_request_trailer; + + /** + * Request hook, invoked after a complete request is seen. + */ + htp_hook_t *hook_request_complete; + + /** + * Response startup hook, invoked when a response transaction is found and + * processing started. + */ + htp_hook_t *hook_response_start; + + /** + * Response line hook, invoked after a response line has been parsed. + */ + htp_hook_t *hook_response_line; + + /** + * Receives raw response header data, starting immediately after the status line + * and including all headers as they are seen on the TCP connection, and including the + * terminating empty line. Not available on genuine HTTP/0.9 responses (because + * they don't have response headers). + */ + htp_hook_t *hook_response_header_data; + + /** + * Response headers book, invoked after all response headers have been seen. + */ + htp_hook_t *hook_response_headers; + + /** + * Response body data hook, invoked every time body data is available. Each + * invocation will provide a htp_tx_data_t instance. Chunked data + * will be dechunked before the data is passed to this hook. By default, + * compressed data will be decompressed, but decompression can be disabled + * in configuration. At the end of the response body there will be a call + * with the data pointer set to NULL. + */ + htp_hook_t *hook_response_body_data; + + /** + * Receives raw response trailer data, which can be available on responses that have + * chunked bodies. The data starts immediately after the zero-length chunk + * and includes the terminating empty line. + */ + htp_hook_t *hook_response_trailer_data; + + /** + * Response trailer hook, invoked after all trailer headers have been processed, + * and only if the trailer exists. + */ + htp_hook_t *hook_response_trailer; + + /** + * Response hook, invoked after a response has been seen. Because sometimes servers + * respond before receiving complete requests, a response_complete callback may be + * invoked prior to a request_complete callback. + */ + htp_hook_t *hook_response_complete; + + /** + * Transaction complete hook, which is invoked once the entire transaction is + * considered complete (request and response are both complete). This is always + * the last hook to be invoked. + */ + htp_hook_t *hook_transaction_complete; + + /** + * Log hook, invoked every time the library wants to log. + */ + htp_hook_t *hook_log; + + /** + * Opaque user data associated with this configuration structure. + */ + void *user_data; + + // Request Line parsing options. + + // TODO this was added here to maintain a stable ABI, once we can break that + // we may want to move this into htp_decoder_cfg_t (VJ) + + /** Reaction to leading whitespace on the request line */ + enum htp_unwanted_t requestline_leading_whitespace_unwanted; + + /** How many layers of compression we will decompress (0 => no limit). */ + int response_decompression_layer_limit; + + /** max memory use by a the lzma decompressor. */ + size_t lzma_memlimit; + + /** max output size for a compression bomb. */ + int32_t compression_bomb_limit; + + /** max time for a decompression bomb. */ + int32_t compression_time_limit; + + /** How many layers of compression we will decompress (0 => no lzma). */ + int response_lzma_layer_limit; + + /** Whether to decompress compressed request bodies. */ + int request_decompression_enabled; + + /** Maximum number of transactions. */ + uint32_t max_tx; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CONFIG_PRIVATE H */ + diff --git a/htp/htp_connection.c b/htp/htp_connection.c new file mode 100644 index 0000000..3fe7c89 --- /dev/null +++ b/htp/htp_connection.c @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +htp_conn_t *htp_conn_create(void) { + htp_conn_t *conn = calloc(1, sizeof (htp_conn_t)); + if (conn == NULL) return NULL; + + conn->transactions = htp_list_create(16); + if (conn->transactions == NULL) { + free(conn); + return NULL; + } + + conn->messages = htp_list_create(8); + if (conn->messages == NULL) { + htp_list_destroy(conn->transactions); + conn->transactions = NULL; + free(conn); + return NULL; + } + + return conn; +} + +void htp_conn_close(htp_conn_t *conn, const htp_time_t *timestamp) { + if (conn == NULL) return; + + // Update timestamp. + if (timestamp != NULL) { + memcpy(&(conn->close_timestamp), timestamp, sizeof(htp_time_t)); + } +} + +void htp_conn_destroy(htp_conn_t *conn) { + if (conn == NULL) return; + + if (conn->transactions != NULL) { + // Destroy individual transactions. Do note that iterating + // using the iterator does not work here because some of the + // list element may be NULL (and with the iterator it is impossible + // to distinguish a NULL element from the end of the list). + for (size_t i = 0, n = htp_list_size(conn->transactions); i < n; i++) { + htp_tx_t *tx = htp_list_get(conn->transactions, i); + if (tx != NULL) { + htp_tx_destroy_incomplete(tx); + } + } + + htp_list_destroy(conn->transactions); + conn->transactions = NULL; + } + + if (conn->messages != NULL) { + // Destroy individual messages. + for (size_t i = 0, n = htp_list_size(conn->messages); i < n; i++) { + htp_log_t *l = htp_list_get(conn->messages, i); + free((void *) l->msg); + free(l); + } + + htp_list_destroy(conn->messages); + conn->messages = NULL; + } + + if (conn->server_addr != NULL) { + free(conn->server_addr); + } + + if (conn->client_addr != NULL) { + free(conn->client_addr); + } + + free(conn); +} + +htp_status_t htp_conn_open(htp_conn_t *conn, const char *client_addr, int client_port, + const char *server_addr, int server_port, const htp_time_t *timestamp) +{ + if (conn == NULL) return HTP_ERROR; + + if (client_addr != NULL) { + conn->client_addr = strdup(client_addr); + if (conn->client_addr == NULL) return HTP_ERROR; + } + + conn->client_port = client_port; + + if (server_addr != NULL) { + conn->server_addr = strdup(server_addr); + if (conn->server_addr == NULL) { + if (conn->client_addr != NULL) { + free(conn->client_addr); + } + + return HTP_ERROR; + } + } + + conn->server_port = server_port; + + // Remember when the connection was opened. + if (timestamp != NULL) { + memcpy(&(conn->open_timestamp), timestamp, sizeof(*timestamp)); + } + + return HTP_OK; +} + +htp_status_t htp_conn_remove_tx(htp_conn_t *conn, const htp_tx_t *tx) { + if ((tx == NULL) || (conn == NULL)) return HTP_ERROR; + if (conn->transactions == NULL) return HTP_ERROR; + for (size_t i = 0, n = htp_list_size(conn->transactions); i < n; i++) { + htp_tx_t *tx2 = htp_list_get(conn->transactions, i); + if (tx2 == tx) { + return htp_list_replace(conn->transactions, i, NULL); + } + } + return HTP_DECLINED; +} + +void htp_conn_track_inbound_data(htp_conn_t *conn, size_t len, const htp_time_t *timestamp) { + if (conn == NULL) return; + conn->in_data_counter += len; +} + +void htp_conn_track_outbound_data(htp_conn_t *conn, size_t len, const htp_time_t *timestamp) { + if (conn == NULL) return; + conn->out_data_counter += len; +} diff --git a/htp/htp_connection_parser.c b/htp/htp_connection_parser.c new file mode 100644 index 0000000..8fd4ed6 --- /dev/null +++ b/htp/htp_connection_parser.c @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +void htp_connp_clear_error(htp_connp_t *connp) { + connp->last_error = NULL; +} + +void htp_connp_req_close(htp_connp_t *connp, const htp_time_t *timestamp) { + if (connp == NULL) return; + + // Update internal flags + if (connp->in_status != HTP_STREAM_ERROR) + connp->in_status = HTP_STREAM_CLOSED; + + // Call the parsers one last time, which will allow them + // to process the events that depend on stream closure + htp_connp_req_data(connp, timestamp, NULL, 0); +} + +void htp_connp_close(htp_connp_t *connp, const htp_time_t *timestamp) { + if (connp == NULL) return; + + // Close the underlying connection. + htp_conn_close(connp->conn, timestamp); + + // Update internal flags + if (connp->in_status != HTP_STREAM_ERROR) + connp->in_status = HTP_STREAM_CLOSED; + if (connp->out_status != HTP_STREAM_ERROR) + connp->out_status = HTP_STREAM_CLOSED; + + // Call the parsers one last time, which will allow them + // to process the events that depend on stream closure + htp_connp_req_data(connp, timestamp, NULL, 0); + htp_connp_res_data(connp, timestamp, NULL, 0); +} + +htp_connp_t *htp_connp_create(htp_cfg_t *cfg) { + htp_connp_t *connp = calloc(1, sizeof (htp_connp_t)); + if (connp == NULL) return NULL; + + // Use the supplied configuration structure + connp->cfg = cfg; + + // Create a new connection. + connp->conn = htp_conn_create(); + if (connp->conn == NULL) { + free(connp); + return NULL; + } + + // Request parsing + connp->in_state = htp_connp_REQ_IDLE; + connp->in_status = HTP_STREAM_NEW; + + // Response parsing + connp->out_state = htp_connp_RES_IDLE; + connp->out_status = HTP_STREAM_NEW; + + return connp; +} + +void htp_connp_destroy(htp_connp_t *connp) { + if (connp == NULL) return; + + if (connp->in_buf != NULL) { + free(connp->in_buf); + } + + if (connp->out_buf != NULL) { + free(connp->out_buf); + } + + htp_connp_destroy_decompressors(connp); + + if (connp->put_file != NULL) { + bstr_free(connp->put_file->filename); + free(connp->put_file); + } + + if (connp->in_header) { + bstr_free(connp->in_header); + connp->in_header = NULL; + } + if (connp->out_header) { + bstr_free(connp->out_header); + connp->out_header = NULL; + } + free(connp); +} + +void htp_connp_destroy_all(htp_connp_t *connp) { + if (connp == NULL) return; + + // Destroy connection + htp_conn_destroy(connp->conn); + connp->conn = NULL; + + // Destroy everything else + htp_connp_destroy(connp); +} + +htp_conn_t *htp_connp_get_connection(const htp_connp_t *connp) { + if (connp == NULL) return NULL; + return connp->conn; +} + +htp_tx_t *htp_connp_get_in_tx(const htp_connp_t *connp) { + if (connp == NULL) return NULL; + return connp->in_tx; +} + +htp_log_t *htp_connp_get_last_error(const htp_connp_t *connp) { + if (connp == NULL) return NULL; + return connp->last_error; +} + +htp_tx_t *htp_connp_get_out_tx(const htp_connp_t *connp) { + if (connp == NULL) return NULL; + return connp->out_tx; +} + +void *htp_connp_get_user_data(const htp_connp_t *connp) { + if (connp == NULL) return NULL; + return (void *)connp->user_data; +} + +void htp_connp_in_reset(htp_connp_t *connp) { + if (connp == NULL) return; + connp->in_content_length = -1; + connp->in_body_data_left = -1; + connp->in_chunk_request_index = connp->in_chunk_count; +} + +void htp_connp_open(htp_connp_t *connp, const char *client_addr, int client_port, const char *server_addr, + int server_port, htp_time_t *timestamp) +{ + if (connp == NULL) return; + + // Check connection parser state first. + if ((connp->in_status != HTP_STREAM_NEW) || (connp->out_status != HTP_STREAM_NEW)) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Connection is already open"); + return; + } + + if (htp_conn_open(connp->conn, client_addr, client_port, server_addr, server_port, timestamp) != HTP_OK) { + return; + } + + connp->in_status = HTP_STREAM_OPEN; + connp->out_status = HTP_STREAM_OPEN; +} + +void htp_connp_set_user_data(htp_connp_t *connp, const void *user_data) { + if (connp == NULL) return; + connp->user_data = user_data; +} + +htp_tx_t *htp_connp_tx_create(htp_connp_t *connp) { + if (connp == NULL) return NULL; + + // Detect pipelining. + if (htp_list_size(connp->conn->transactions) > connp->out_next_tx_index) { + connp->conn->flags |= HTP_CONN_PIPELINED; + } + if (connp->cfg->max_tx > 0 && + htp_list_size(connp->conn->transactions) > connp->cfg->max_tx) { + return NULL; + } + + htp_tx_t *tx = htp_tx_create(connp); + if (tx == NULL) return NULL; + + connp->in_tx = tx; + + htp_connp_in_reset(connp); + + return tx; +} + +/** + * Removes references to the supplied transaction. + * + * @param[in] connp + * @param[in] tx + */ +void htp_connp_tx_remove(htp_connp_t *connp, htp_tx_t *tx) { + if (connp == NULL) return; + + if (connp->in_tx == tx) { + connp->in_tx = NULL; + } + + if (connp->out_tx == tx) { + connp->out_tx = NULL; + } +} + +/** + * Removes all front NULL-ed transactions + * + * @param[in] connp + * @return numbers of removed NULL transactions + */ +size_t htp_connp_tx_freed(htp_connp_t *connp) { + // Transactions first got freed and NULLed + // Now, we can recycle the space that hold them by shifting the list + size_t r = 0; + size_t nb = htp_list_size(connp->conn->transactions); + for (size_t i = 0; i < nb; i++) { + // 0 and not i because at next iteration, we have removed the first + void * tx = htp_list_get(connp->conn->transactions, 0); + if (tx != NULL) { + break; + } + htp_list_shift(connp->conn->transactions); + r++; + connp->out_next_tx_index--; + } + return r; +} diff --git a/htp/htp_connection_parser.h b/htp/htp_connection_parser.h new file mode 100644 index 0000000..b2c8d3a --- /dev/null +++ b/htp/htp_connection_parser.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CONNECTION_PARSER_H +#define HTP_CONNECTION_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Clears the most recent error, if any. + * + * @param[in] connp + */ +void htp_connp_clear_error(htp_connp_t *connp); + +/** + * Closes the connection associated with the supplied parser. + * + * @param[in] connp + * @param[in] timestamp Optional. + */ +void htp_connp_close(htp_connp_t *connp, const htp_time_t *timestamp); +void htp_connp_req_close(htp_connp_t *connp, const htp_time_t *timestamp); + +/** + * Creates a new connection parser using the provided configuration. Because + * the configuration structure is used directly, in a multithreaded environment + * you are not allowed to change the structure, ever. If you have a need to + * change configuration on per-connection basis, make a copy of the configuration + * structure to go along with every connection parser. + * + * @param[in] cfg + * @return New connection parser instance, or NULL on error. + */ +htp_connp_t *htp_connp_create(htp_cfg_t *cfg); + +/** + * Destroys the connection parser and its data structures, leaving + * all the data (connection, transactions, etc) intact. + * + * @param[in] connp + */ +void htp_connp_destroy(htp_connp_t *connp); + +/** + * Destroys the connection parser, its data structures, as well + * as the connection and its transactions. + * + * @param[in] connp + */ +void htp_connp_destroy_all(htp_connp_t *connp); + +/** + * Returns the connection associated with the connection parser. + * + * @param[in] connp + * @return htp_conn_t instance, or NULL if one is not available. + */ +htp_conn_t *htp_connp_get_connection(const htp_connp_t *connp); + +/** + * Retrieves the pointer to the active inbound transaction. In connection + * parsing mode there can be many open transactions, and up to 2 active + * transactions at any one time. This is due to HTTP pipelining. Can be NULL. + * + * @param[in] connp + * @return Active inbound transaction, or NULL if there isn't one. + */ +htp_tx_t *htp_connp_get_in_tx(const htp_connp_t *connp); + +/** + * Returns the last error that occurred with this connection parser. Do note, however, + * that the value in this field will only be valid immediately after an error condition, + * but it is not guaranteed to remain valid if the parser is invoked again. + * + * @param[in] connp + * @return A pointer to an htp_log_t instance if there is an error, or NULL + * if there isn't. + */ +htp_log_t *htp_connp_get_last_error(const htp_connp_t *connp); + +/** + * Retrieves the pointer to the active outbound transaction. In connection + * parsing mode there can be many open transactions, and up to 2 active + * transactions at any one time. This is due to HTTP pipelining. Can be NULL. + * + * @param[in] connp + * @return Active outbound transaction, or NULL if there isn't one. + */ +htp_tx_t *htp_connp_get_out_tx(const htp_connp_t *connp); + +/** + * Retrieve the user data associated with this connection parser. + * + * @param[in] connp + * @return User data, or NULL if there isn't any. + */ +void *htp_connp_get_user_data(const htp_connp_t *connp); + +/** + * Opens connection. + * + * @param[in] connp + * @param[in] client_addr Client address + * @param[in] client_port Client port + * @param[in] server_addr Server address + * @param[in] server_port Server port + * @param[in] timestamp Optional. + */ +void htp_connp_open(htp_connp_t *connp, const char *client_addr, int client_port, const char *server_addr, + int server_port, htp_time_t *timestamp); + +/** + * Associate user data with the supplied parser. + * + * @param[in] connp + * @param[in] user_data + */ +void htp_connp_set_user_data(htp_connp_t *connp, const void *user_data); + +/** + * + * @param[in] connp + * @param[in] timestamp + * @param[in] data + * @param[in] len + * @return HTP_STREAM_DATA, HTP_STREAM_ERROR or STEAM_STATE_DATA_OTHER (see QUICK_START). + * HTP_STREAM_CLOSED and HTP_STREAM_TUNNEL are also possible. + */ +int htp_connp_req_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len); + +/** + * Returns the number of bytes consumed from the most recent inbound data chunk. Normally, an invocation + * of htp_connp_req_data() will consume all data from the supplied buffer, but there are circumstances + * where only partial consumption is possible. In such cases HTP_STREAM_DATA_OTHER will be returned. + * Consumed bytes are no longer necessary, but the remainder of the buffer will be need to be saved + * for later. + * + * @param[in] connp + * @return The number of bytes consumed from the last data chunk sent for inbound processing. + */ +size_t htp_connp_req_data_consumed(htp_connp_t *connp); + +/** + * Process a chunk of outbound (server or response) data. + * + * @param[in] connp + * @param[in] timestamp Optional. + * @param[in] data + * @param[in] len + * @return HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed + */ +int htp_connp_res_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len); + +/** + * Returns the number of bytes consumed from the most recent outbound data chunk. Normally, an invocation + * of htp_connp_res_data() will consume all data from the supplied buffer, but there are circumstances + * where only partial consumption is possible. In such cases HTP_STREAM_DATA_OTHER will be returned. + * Consumed bytes are no longer necessary, but the remainder of the buffer will be need to be saved + * for later. + * + * @param[in] connp + * @return The number of bytes consumed from the last data chunk sent for outbound processing. + */ +size_t htp_connp_res_data_consumed(htp_connp_t *connp); + +/** + * Create a new transaction using the connection parser provided. + * + * @param[in] connp + * @return Transaction instance on success, NULL on failure. + */ +htp_tx_t *htp_connp_tx_create(htp_connp_t *connp); + +size_t htp_connp_tx_freed(htp_connp_t *connp); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CONNECTION_PARSER_H */ diff --git a/htp/htp_connection_parser_private.h b/htp/htp_connection_parser_private.h new file mode 100644 index 0000000..a055aa8 --- /dev/null +++ b/htp/htp_connection_parser_private.h @@ -0,0 +1,275 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CONNECTION_PARSER_PRIVATE_H +#define HTP_CONNECTION_PARSER_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp_core.h" + +/** + * Connection parser structure. + */ +struct htp_connp_t { + + // General fields + + /** Current parser configuration structure. */ + htp_cfg_t *cfg; + + /** The connection structure associated with this parser. */ + htp_conn_t *conn; + + /** Opaque user data associated with this parser. */ + const void *user_data; + + /** + * On parser failure, this field will contain the error information. Do note, however, + * that the value in this field will only be valid immediately after an error condition, + * but it is not guaranteed to remain valid if the parser is invoked again. + */ + htp_log_t *last_error; + + + // Request parser fields + + /** Parser inbound status. Starts as HTP_OK, but may turn into HTP_ERROR. */ + enum htp_stream_state_t in_status; + + /** Parser output status. Starts as HTP_OK, but may turn into HTP_ERROR. */ + enum htp_stream_state_t out_status; + + /** + * When true, this field indicates that there is unprocessed inbound data, and + * that the response parsing code should stop at the end of the current request + * in order to allow more requests to be produced. + */ + unsigned int out_data_other_at_tx_end; + + /** + * The time when the last request data chunk was received. Can be NULL if + * the upstream code is not providing the timestamps when calling us. + */ + htp_time_t in_timestamp; + + /** Pointer to the current request data chunk. */ + unsigned char *in_current_data; + + /** The length of the current request data chunk. */ + int64_t in_current_len; + + /** The offset of the next byte in the request data chunk to read. */ + int64_t in_current_read_offset; + + /** + * The starting point of the data waiting to be consumed. This field is used + * in the states where reading data is not the same as consumption. + */ + int64_t in_current_consume_offset; + + /** + * Marks the starting point of raw data within the inbound data chunk. Raw + * data (e.g., complete headers) is sent to appropriate callbacks (e.g., + * REQUEST_HEADER_DATA). + */ + int64_t in_current_receiver_offset; + + /** How many data chunks does the inbound connection stream consist of? */ + size_t in_chunk_count; + + /** The index of the first chunk used in the current request. */ + size_t in_chunk_request_index; + + /** The offset, in the entire connection stream, of the next request byte. */ + int64_t in_stream_offset; + + /** + * The value of the request byte currently being processed. This field is + * populated when the IN_NEXT_* or IN_PEEK_* macros are invoked. + */ + int in_next_byte; + + /** Used to buffer a line of inbound data when buffering cannot be avoided. */ + unsigned char *in_buf; + + /** Stores the size of the buffer. Valid only when htp_tx_t::in_buf is not NULL. */ + size_t in_buf_size; + + /** + * Stores the current value of a folded request header. Such headers span + * multiple lines, and are processed only when all data is available. + */ + bstr *in_header; + + /** Ongoing inbound transaction. */ + htp_tx_t *in_tx; + + /** + * The request body length declared in a valid request header. The key here + * is "valid". This field will not be populated if the request contains both + * a Transfer-Encoding header and a Content-Length header. + */ + int64_t in_content_length; + + /** + * Holds the remaining request body length that we expect to read. This + * field will be available only when the length of a request body is known + * in advance, i.e. when request headers contain a Content-Length header. + */ + int64_t in_body_data_left; + + /** + * Holds the amount of data that needs to be read from the + * current data chunk. Only used with chunked request bodies. + */ + int64_t in_chunked_length; + + /** Current request parser state. */ + int (*in_state)(htp_connp_t *); + + /** Previous request parser state. Used to detect state changes. */ + int (*in_state_previous)(htp_connp_t *); + + /** The hook that should be receiving raw connection data. */ + htp_hook_t *in_data_receiver_hook; + + // Response parser fields + + /** + * Response counter, incremented with every new response. This field is + * used to match responses to requests. The expectation is that for every + * response there will already be a transaction (request) waiting. + */ + size_t out_next_tx_index; + + /** The time when the last response data chunk was received. Can be NULL. */ + htp_time_t out_timestamp; + + /** Pointer to the current response data chunk. */ + unsigned char *out_current_data; + + /** The length of the current response data chunk. */ + int64_t out_current_len; + + /** The offset of the next byte in the response data chunk to consume. */ + int64_t out_current_read_offset; + + /** + * The starting point of the data waiting to be consumed. This field is used + * in the states where reading data is not the same as consumption. + */ + int64_t out_current_consume_offset; + + /** + * Marks the starting point of raw data within the outbound data chunk. Raw + * data (e.g., complete headers) is sent to appropriate callbacks (e.g., + * RESPONSE_HEADER_DATA). + */ + int64_t out_current_receiver_offset; + + /** The offset, in the entire connection stream, of the next response byte. */ + int64_t out_stream_offset; + + /** The value of the response byte currently being processed. */ + int out_next_byte; + + /** Used to buffer a line of outbound data when buffering cannot be avoided. */ + unsigned char *out_buf; + + /** Stores the size of the buffer. Valid only when htp_tx_t::out_buf is not NULL. */ + size_t out_buf_size; + + /** + * Stores the current value of a folded response header. Such headers span + * multiple lines, and are processed only when all data is available. + */ + bstr *out_header; + + /** Ongoing outbound transaction */ + htp_tx_t *out_tx; + + /** + * The length of the current response body as presented in the + * Content-Length response header. + */ + int64_t out_content_length; + + /** The remaining length of the current response body, if known. Set to -1 otherwise. */ + int64_t out_body_data_left; + + /** + * Holds the amount of data that needs to be read from the + * current response data chunk. Only used with chunked response bodies. + */ + int64_t out_chunked_length; + + /** Current response parser state. */ + int (*out_state)(htp_connp_t *); + + /** Previous response parser state. */ + int (*out_state_previous)(htp_connp_t *); + + /** The hook that should be receiving raw connection data. */ + htp_hook_t *out_data_receiver_hook; + + /** Response decompressor used to decompress response body data. */ + htp_decompressor_t *out_decompressor; + + /** On a PUT request, this field contains additional file data. */ + htp_file_t *put_file; + + /** Request decompressor used to decompress request body data. */ + htp_decompressor_t *req_decompressor; +}; + +/** + * This function is most likely not used and/or not needed. + * + * @param[in] connp + */ +void htp_connp_in_reset(htp_connp_t *connp); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CONNECTION_PARSER_PRIVATE_H */ + diff --git a/htp/htp_connection_private.h b/htp/htp_connection_private.h new file mode 100644 index 0000000..e4beccc --- /dev/null +++ b/htp/htp_connection_private.h @@ -0,0 +1,121 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CONNECTION_H +#define HTP_CONNECTION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a new connection structure. + * + * @return A new connection structure on success, NULL on memory allocation failure. + */ +htp_conn_t *htp_conn_create(void); + +/** + * Closes the connection. + * + * @param[in] conn + * @param[in] timestamp + */ +void htp_conn_close(htp_conn_t *conn, const htp_time_t *timestamp); + +/** + * Destroys a connection, as well as all the transactions it contains. It is + * not possible to destroy a connection structure yet leave any of its + * transactions intact. This is because transactions need its connection and + * connection structures hold little data anyway. The opposite is true, though + * it is possible to delete a transaction but leave its connection alive. + * + * @param[in] conn + */ +void htp_conn_destroy(htp_conn_t *conn); + +/** + * Opens a connection. This function will essentially only store the provided data + * for future reference. The timestamp parameter is optional. + * + * @param[in] conn + * @param[in] remote_addr + * @param[in] remote_port + * @param[in] local_addr + * @param[in] local_port + * @param[in] timestamp + * @return + */ +htp_status_t htp_conn_open(htp_conn_t *conn, const char *remote_addr, int remote_port, + const char *local_addr, int local_port, const htp_time_t *timestamp); + +/** + * Removes the given transaction structure, which makes it possible to + * safely destroy it. It is safe to destroy transactions in this way + * because the index of the transactions (in a connection) is preserved. + * + * @param[in] conn + * @param[in] tx + * @return HTP_OK if transaction was removed (replaced with NULL) or HTP_ERROR if it wasn't found. + */ +htp_status_t htp_conn_remove_tx(htp_conn_t *conn, const htp_tx_t *tx); + +/** + * Keeps track of inbound packets and data. + * + * @param[in] conn + * @param[in] len + * @param[in] timestamp + */ +void htp_conn_track_inbound_data(htp_conn_t *conn, size_t len, const htp_time_t *timestamp); + +/** + * Keeps track of outbound packets and data. + * + * @param[in] conn + * @param[in] len + * @param[in] timestamp + */ +void htp_conn_track_outbound_data(htp_conn_t *conn, size_t len, const htp_time_t *timestamp); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CONNECTION_H */ + diff --git a/htp/htp_content_handlers.c b/htp/htp_content_handlers.c new file mode 100644 index 0000000..183a0f2 --- /dev/null +++ b/htp/htp_content_handlers.c @@ -0,0 +1,299 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * This callback function feeds request body data to a Urlencoded parser + * and, later, feeds the parsed parameters to the correct structures. + * + * @param[in] d + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_ch_urlencoded_callback_request_body_data(htp_tx_data_t *d) { + htp_tx_t *tx = d->tx; + + // Check that we were not invoked again after the finalization. + if (tx->request_urlenp_body->params == NULL) return HTP_ERROR; + + if (d->data != NULL) { + // Process one chunk of data. + htp_urlenp_parse_partial(tx->request_urlenp_body, d->data, d->len); + } else { + // Finalize parsing. + htp_urlenp_finalize(tx->request_urlenp_body); + + // Add all parameters to the transaction. + bstr *name = NULL; + bstr *value = NULL; + + for (size_t i = 0, n = htp_table_size(tx->request_urlenp_body->params); i < n; i++) { + value = htp_table_get_index(tx->request_urlenp_body->params, i, &name); + + htp_param_t *param = calloc(1, sizeof (htp_param_t)); + if (param == NULL) return HTP_ERROR; + + param->name = name; + param->value = value; + param->source = HTP_SOURCE_BODY; + param->parser_id = HTP_PARSER_URLENCODED; + param->parser_data = NULL; + + if (htp_tx_req_add_param(tx, param) != HTP_OK) { + free(param); + return HTP_ERROR; + } + } + + // All the parameter data is now owned by the transaction, and + // the parser table used to store it is no longer needed. The + // line below will destroy just the table, leaving keys intact. + htp_table_destroy_ex(tx->request_urlenp_body->params); + tx->request_urlenp_body->params = NULL; + } + + return HTP_OK; +} + +/** + * Determine if the request has a Urlencoded body, and, if it does, create and + * attach an instance of the Urlencoded parser to the transaction. + * + * @param[in] connp + * @return HTP_OK if a new parser has been setup, HTP_DECLINED if the MIME type + * is not appropriate for this parser, and HTP_ERROR on failure. + */ +htp_status_t htp_ch_urlencoded_callback_request_headers(htp_tx_t *tx) { + // Check the request content type to see if it matches our MIME type. + if ((tx->request_content_type == NULL) || (!bstr_begins_with_c(tx->request_content_type, HTP_URLENCODED_MIME_TYPE))) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_ch_urlencoded_callback_request_headers: Body not URLENCODED\n"); + #endif + + return HTP_DECLINED; + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_ch_urlencoded_callback_request_headers: Parsing URLENCODED body\n"); + #endif + + // Create parser instance. + tx->request_urlenp_body = htp_urlenp_create(tx); + if (tx->request_urlenp_body == NULL) return HTP_ERROR; + + // Register a request body data callback. + htp_tx_register_request_body_data(tx, htp_ch_urlencoded_callback_request_body_data); + + return HTP_OK; +} + +/** + * Parses request query string, if present. + * + * @param[in] connp + * @param[in] raw_data + * @param[in] raw_len + * @return HTP_OK if query string was parsed, HTP_DECLINED if there was no query + * string, and HTP_ERROR on failure. + */ +htp_status_t htp_ch_urlencoded_callback_request_line(htp_tx_t *tx) { + // Proceed only if there's something for us to parse. + if ((tx->parsed_uri->query == NULL) || (bstr_len(tx->parsed_uri->query) == 0)) { + return HTP_DECLINED; + } + + // We have a non-zero length query string. + + tx->request_urlenp_query = htp_urlenp_create(tx); + if (tx->request_urlenp_query == NULL) return HTP_ERROR; + + if (htp_urlenp_parse_complete(tx->request_urlenp_query, bstr_ptr(tx->parsed_uri->query), + bstr_len(tx->parsed_uri->query)) != HTP_OK) { + htp_urlenp_destroy(tx->request_urlenp_query); + return HTP_ERROR; + } + + // Add all parameters to the transaction. + + bstr *name = NULL; + bstr *value = NULL; + for (size_t i = 0, n = htp_table_size(tx->request_urlenp_query->params); i < n; i++) { + value = htp_table_get_index(tx->request_urlenp_query->params, i, &name); + + htp_param_t *param = calloc(1, sizeof (htp_param_t)); + if (param == NULL) return HTP_ERROR; + + param->name = name; + param->value = value; + param->source = HTP_SOURCE_QUERY_STRING; + param->parser_id = HTP_PARSER_URLENCODED; + param->parser_data = NULL; + + if (htp_tx_req_add_param(tx, param) != HTP_OK) { + free(param); + return HTP_ERROR; + } + } + + // All the parameter data is now owned by the transaction, and + // the parser table used to store it is no longer needed. The + // line below will destroy just the table, leaving keys intact. + htp_table_destroy_ex(tx->request_urlenp_query->params); + tx->request_urlenp_query->params = NULL; + + htp_urlenp_destroy(tx->request_urlenp_query); + tx->request_urlenp_query = NULL; + + return HTP_OK; +} + +/** + * Finalize Multipart processing. + * + * @param[in] d + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_ch_multipart_callback_request_body_data(htp_tx_data_t *d) { + htp_tx_t *tx = d->tx; + + // Check that we were not invoked again after the finalization. + if (tx->request_mpartp->gave_up_data == 1) return HTP_ERROR; + + if (d->data != NULL) { + // Process one chunk of data. + htp_mpartp_parse(tx->request_mpartp, d->data, d->len); + } else { + // Finalize parsing. + htp_mpartp_finalize(tx->request_mpartp); + + htp_multipart_t *body = htp_mpartp_get_multipart(tx->request_mpartp); + + for (size_t i = 0, n = htp_list_size(body->parts); i < n; i++) { + htp_multipart_part_t *part = htp_list_get(body->parts, i); + + // Use text parameters. + if (part->type == MULTIPART_PART_TEXT) { + htp_param_t *param = calloc(1, sizeof (htp_param_t)); + if (param == NULL) return HTP_ERROR; + param->name = part->name; + param->value = part->value; + param->source = HTP_SOURCE_BODY; + param->parser_id = HTP_PARSER_MULTIPART; + param->parser_data = part; + + if (htp_tx_req_add_param(tx, param) != HTP_OK) { + free(param); + return HTP_ERROR; + } + } + } + + // Tell the parser that it no longer owns names + // and values of MULTIPART_PART_TEXT parts. + tx->request_mpartp->gave_up_data = 1; + } + + return HTP_OK; +} + +/** + * Inspect request headers and register the Multipart request data hook + * if it contains a multipart/form-data body. + * + * @param[in] connp + * @return HTP_OK if a new parser has been setup, HTP_DECLINED if the MIME type + * is not appropriate for this parser, and HTP_ERROR on failure. + */ +htp_status_t htp_ch_multipart_callback_request_headers(htp_tx_t *tx) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_ch_multipart_callback_request_headers: Need to determine if multipart body is present\n"); + #endif + + // The field tx->request_content_type does not contain the entire C-T + // value and so we cannot use it to look for a boundary, but we can + // use it for a quick check to determine if the C-T header exists. + if (tx->request_content_type == NULL) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_ch_multipart_callback_request_headers: Not multipart body (no C-T header)\n"); + #endif + + return HTP_DECLINED; + } + + // Look for a boundary. + + htp_header_t *ct = htp_table_get_c(tx->request_headers, "content-type"); + if (ct == NULL) return HTP_ERROR; + + bstr *boundary = NULL; + uint64_t flags = 0; + + htp_status_t rc = htp_mpartp_find_boundary(ct->value, &boundary, &flags); + if (rc != HTP_OK) { + #ifdef HTP_DEBUG + if (rc == HTP_DECLINED) { + fprintf(stderr, "htp_ch_multipart_callback_request_headers: Not multipart body\n"); + } + #endif + + // No boundary (HTP_DECLINED) or error (HTP_ERROR). + return rc; + } + + if (boundary == NULL) return HTP_ERROR; + + // Create a Multipart parser instance. + tx->request_mpartp = htp_mpartp_create(tx->connp->cfg, boundary, flags); + if (tx->request_mpartp == NULL) { + bstr_free(boundary); + return HTP_ERROR; + } + + // Configure file extraction. + if (tx->cfg->extract_request_files) { + tx->request_mpartp->extract_files = 1; + tx->request_mpartp->extract_dir = tx->connp->cfg->tmpdir; + } + + // Register a request body data callback. + htp_tx_register_request_body_data(tx, htp_ch_multipart_callback_request_body_data); + + return HTP_OK; +} diff --git a/htp/htp_cookies.c b/htp/htp_cookies.c new file mode 100644 index 0000000..bb26822 --- /dev/null +++ b/htp/htp_cookies.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Parses a single v0 request cookie and places the results into tx->request_cookies. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return HTP_OK on success, HTP_ERROR on error. + */ +int htp_parse_single_cookie_v0(htp_connp_t *connp, unsigned char *data, size_t len) { + if (len == 0) return HTP_OK; + + size_t pos = 0; + + // Look for '='. + while ((pos < len) && (data[pos] != '=')) pos++; + if (pos == 0) return HTP_OK; // Ignore a nameless cookie. + + bstr *name = bstr_dup_mem(data, pos); + if (name == NULL) return HTP_ERROR; + + bstr *value = NULL; + if (pos == len) { + // The cookie is empty. + value = bstr_dup_c(""); + } else { + // The cookie is not empty. + value = bstr_dup_mem(data + pos + 1, len - pos - 1); + } + + if (value == NULL) { + bstr_free(name); + return HTP_ERROR; + } + + htp_table_addn(connp->in_tx->request_cookies, name, value); + + return HTP_OK; +} + +/** + * Parses the Cookie request header in v0 format. + * + * @param[in] connp + * @return HTP_OK on success, HTP_ERROR on error + */ +htp_status_t htp_parse_cookies_v0(htp_connp_t *connp) { + htp_header_t *cookie_header = htp_table_get_c(connp->in_tx->request_headers, "cookie"); + if (cookie_header == NULL) return HTP_OK; + + // Create a new table to store cookies. + connp->in_tx->request_cookies = htp_table_create(4); + if (connp->in_tx->request_cookies == NULL) return HTP_ERROR; + + unsigned char *data = bstr_ptr(cookie_header->value); + size_t len = bstr_len(cookie_header->value); + size_t pos = 0; + + while (pos < len) { + // Ignore whitespace at the beginning. + while ((pos < len) && (isspace((int)data[pos]))) pos++; + if (pos == len) return HTP_OK; + + size_t start = pos; + + // Find the end of the cookie. + while ((pos < len) && (data[pos] != ';')) pos++; + + if (htp_parse_single_cookie_v0(connp, data + start, pos - start) != HTP_OK) { + return HTP_ERROR; + } + + // Go over the semicolon. + if (pos < len) pos++; + } + + return HTP_OK; +} diff --git a/htp/htp_core.h b/htp/htp_core.h new file mode 100644 index 0000000..e4c933e --- /dev/null +++ b/htp/htp_core.h @@ -0,0 +1,353 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_CORE_H +#define HTP_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int htp_status_t; + +typedef struct htp_cfg_t htp_cfg_t; +typedef struct htp_conn_t htp_conn_t; +typedef struct htp_connp_t htp_connp_t; +typedef struct htp_file_t htp_file_t; +typedef struct htp_file_data_t htp_file_data_t; +typedef struct htp_header_t htp_header_t; +typedef struct htp_header_line_t htp_header_line_t; +typedef struct htp_log_t htp_log_t; +typedef struct htp_param_t htp_param_t; +typedef struct htp_tx_data_t htp_tx_data_t; +typedef struct htp_tx_t htp_tx_t; +typedef struct htp_uri_t htp_uri_t; +typedef struct timeval htp_time_t; + +// Below are all htp_status_t return codes used by LibHTP. Enum is not +// used here to allow applications to define their own codes. + +/** + * The lowest htp_status_t value LibHTP will use internally. + */ +#define HTP_ERROR_RESERVED -1000 + +/** General-purpose error code. */ +#define HTP_ERROR -1 + +/** + * No processing or work was done. This is typically used by callbacks + * to indicate that they were not interested in doing any work in the + * given context. + */ +#define HTP_DECLINED 0 + +/** Returned by a function when its work was successfully completed. */ +#define HTP_OK 1 + +/** + * Returned when processing a connection stream, after consuming all + * provided data. The caller should call again with more data. + */ +#define HTP_DATA 2 + +/** + * Returned when processing a connection stream, after encountering + * a situation where processing needs to continue on the alternate + * stream (e.g., the inbound parser needs to observe some outbound + * data). The data provided was not completely consumed. On the next + * invocation the caller should supply only the data that has not + * been processed already. Use htp_connp_req_data_consumed() and + * htp_connp_res_data_consumed() to determine how much of the most + * recent data chunk was consumed. + */ +#define HTP_DATA_OTHER 3 + +/** + * Used by callbacks to indicate that the processing should stop. For example, + * returning HTP_STOP from a connection callback indicates that LibHTP should + * stop following that particular connection. + */ +#define HTP_STOP 4 + +/** + * Same as HTP_DATA, but indicates that any non-consumed part of the + * data chunk should be preserved (buffered) for later. + */ +#define HTP_DATA_BUFFER 5 + +/** + * The highest htp_status_t value LibHTP will use internally. + */ +#define HTP_STATUS_RESERVED 1000 + +/** + * Enumerates the possible values for authentication type. + */ +enum htp_auth_type_t { + /** + * This is the default value that is used before + * the presence of authentication is determined (e.g., + * before request headers are seen). + */ + HTP_AUTH_UNKNOWN = 0, + + /** No authentication. */ + HTP_AUTH_NONE = 1, + + /** HTTP Basic authentication used. */ + HTP_AUTH_BASIC = 2, + + /** HTTP Digest authentication used. */ + HTP_AUTH_DIGEST = 3, + + /** HTTP Digest authentication used. */ + HTP_AUTH_BEARER = 4, + + /** Unrecognized authentication method. */ + HTP_AUTH_UNRECOGNIZED = 9 +}; + +enum htp_content_encoding_t { + /** + * This is the default value, which is used until the presence + * of content encoding is determined (e.g., before request headers + * are seen. + */ + HTP_COMPRESSION_UNKNOWN = 0, + + /** No compression. */ + HTP_COMPRESSION_NONE = 1, + + /** Gzip compression. */ + HTP_COMPRESSION_GZIP = 2, + + /** Deflate compression. */ + HTP_COMPRESSION_DEFLATE = 3, + + /** LZMA compression. */ + HTP_COMPRESSION_LZMA = 4 +}; + +/** + * Enumerates the possible request and response body codings. + */ +enum htp_transfer_coding_t { + /** Body coding not determined yet. */ + HTP_CODING_UNKNOWN = 0, + + /** No body. */ + HTP_CODING_NO_BODY = 1, + + /** Identity coding is used, which means that the body was sent as is. */ + HTP_CODING_IDENTITY = 2, + + /** Chunked encoding. */ + HTP_CODING_CHUNKED = 3, + + /** We could not recognize the encoding. */ + HTP_CODING_INVALID = 4 +}; + +enum htp_file_source_t { + + HTP_FILE_MULTIPART = 1, + + HTP_FILE_PUT = 2 +}; + +// Various flag bits. Even though we have a flag field in several places +// (header, transaction, connection), these fields are all in the same namespace +// because we may want to set the same flag in several locations. For example, we +// may set HTP_FIELD_FOLDED on the actual folded header, but also on the transaction +// that contains the header. Both uses are useful. + +// Connection flags are 8 bits wide. +#define HTP_CONN_PIPELINED 0x000000001ULL +#define HTP_CONN_HTTP_0_9_EXTRA 0x000000002ULL + +// All other flags are 64 bits wide. +#define HTP_FIELD_UNPARSEABLE 0x000000004ULL +#define HTP_FIELD_INVALID 0x000000008ULL +#define HTP_FIELD_FOLDED 0x000000010ULL +#define HTP_FIELD_REPEATED 0x000000020ULL +#define HTP_FIELD_LONG 0x000000040ULL +#define HTP_FIELD_RAW_NUL 0x000000080ULL +#define HTP_REQUEST_SMUGGLING 0x000000100ULL +#define HTP_INVALID_FOLDING 0x000000200ULL +#define HTP_REQUEST_INVALID_T_E 0x000000400ULL +#define HTP_MULTI_PACKET_HEAD 0x000000800ULL +#define HTP_HOST_MISSING 0x000001000ULL +#define HTP_HOST_AMBIGUOUS 0x000002000ULL +#define HTP_PATH_ENCODED_NUL 0x000004000ULL +#define HTP_PATH_RAW_NUL 0x000008000ULL +#define HTP_PATH_INVALID_ENCODING 0x000010000ULL +#define HTP_PATH_INVALID 0x000020000ULL +#define HTP_PATH_OVERLONG_U 0x000040000ULL +#define HTP_PATH_ENCODED_SEPARATOR 0x000080000ULL +#define HTP_PATH_UTF8_VALID 0x000100000ULL /* At least one valid UTF-8 character and no invalid ones. */ +#define HTP_PATH_UTF8_INVALID 0x000200000ULL +#define HTP_PATH_UTF8_OVERLONG 0x000400000ULL +#define HTP_PATH_HALF_FULL_RANGE 0x000800000ULL /* Range U+FF00 - U+FFEF detected. */ +#define HTP_STATUS_LINE_INVALID 0x001000000ULL +#define HTP_HOSTU_INVALID 0x002000000ULL /* Host in the URI. */ +#define HTP_HOSTH_INVALID 0x004000000ULL /* Host in the Host header. */ +#define HTP_URLEN_ENCODED_NUL 0x008000000ULL +#define HTP_URLEN_INVALID_ENCODING 0x010000000ULL +#define HTP_URLEN_OVERLONG_U 0x020000000ULL +#define HTP_URLEN_HALF_FULL_RANGE 0x040000000ULL /* Range U+FF00 - U+FFEF detected. */ +#define HTP_URLEN_RAW_NUL 0x080000000ULL +#define HTP_REQUEST_INVALID 0x100000000ULL +#define HTP_REQUEST_INVALID_C_L 0x200000000ULL +#define HTP_AUTH_INVALID 0x400000000ULL + +#define HTP_MAX_HEADERS_REPETITIONS 64 + +#define HTP_HOST_INVALID ( HTP_HOSTU_INVALID | HTP_HOSTH_INVALID ) + +// Logging-related constants. +#define HTP_LOG_MARK __FILE__,__LINE__ + +/** + * Enumerates all log levels. + */ +enum htp_log_level_t { + HTP_LOG_NONE = 0, + HTP_LOG_ERROR = 1, + HTP_LOG_WARNING = 2, + HTP_LOG_NOTICE = 3, + HTP_LOG_INFO = 4, + HTP_LOG_DEBUG = 5, + HTP_LOG_DEBUG2 = 6 +}; + +/** + * HTTP methods. + */ +enum htp_method_t { + /** + * Used by default, until the method is determined (e.g., before + * the request line is processed. + */ + HTP_M_UNKNOWN = 0, + HTP_M_HEAD = 1, + HTP_M_GET = 2, + HTP_M_PUT = 3, + HTP_M_POST = 4, + HTP_M_DELETE = 5, + HTP_M_CONNECT = 6, + HTP_M_OPTIONS = 7, + HTP_M_TRACE = 8, + HTP_M_PATCH = 9, + HTP_M_PROPFIND = 10, + HTP_M_PROPPATCH = 11, + HTP_M_MKCOL = 12, + HTP_M_COPY = 13, + HTP_M_MOVE = 14, + HTP_M_LOCK = 15, + HTP_M_UNLOCK = 16, + HTP_M_VERSION_CONTROL = 17, + HTP_M_CHECKOUT = 18, + HTP_M_UNCHECKOUT = 19, + HTP_M_CHECKIN = 20, + HTP_M_UPDATE = 21, + HTP_M_LABEL = 22, + HTP_M_REPORT = 23, + HTP_M_MKWORKSPACE = 24, + HTP_M_MKACTIVITY = 25, + HTP_M_BASELINE_CONTROL = 26, + HTP_M_MERGE = 27, + HTP_M_INVALID = 28 +}; + +// A collection of unique parser IDs. +enum htp_parser_id_t { + /** application/x-www-form-urlencoded parser. */ + HTP_PARSER_URLENCODED = 0, + + /** multipart/form-data parser. */ + HTP_PARSER_MULTIPART = 1 +}; + +// Protocol version constants; an enum cannot be +// used here because we allow any properly-formatted protocol +// version (e.g., 1.3), even those that do not actually exist. +#define HTP_PROTOCOL_INVALID -2 +#define HTP_PROTOCOL_UNKNOWN -1 +#define HTP_PROTOCOL_0_9 9 +#define HTP_PROTOCOL_1_0 100 +#define HTP_PROTOCOL_1_1 101 + +// A collection of possible data sources. +enum htp_data_source_t { + /** Embedded in the URL. */ + HTP_SOURCE_URL = 0, + + /** Transported in the query string. */ + HTP_SOURCE_QUERY_STRING = 1, + + /** Cookies. */ + HTP_SOURCE_COOKIE = 2, + + /** Transported in the request body. */ + HTP_SOURCE_BODY = 3 +}; + +#define HTP_STATUS_INVALID -1 +#define HTP_STATUS_UNKNOWN 0 + +/** + * Enumerates all stream states. Each connection has two streams, one + * inbound and one outbound. Their states are tracked separately. + */ +enum htp_stream_state_t { + HTP_STREAM_NEW = 0, + HTP_STREAM_OPEN = 1, + HTP_STREAM_CLOSED = 2, + HTP_STREAM_ERROR = 3, + HTP_STREAM_TUNNEL = 4, + HTP_STREAM_DATA_OTHER = 5, + HTP_STREAM_STOP = 6, + HTP_STREAM_DATA = 9 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_CORE_H */ diff --git a/htp/htp_decompressors.c b/htp/htp_decompressors.c new file mode 100644 index 0000000..fe12833 --- /dev/null +++ b/htp/htp_decompressors.c @@ -0,0 +1,490 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + + +static void *SzAlloc(ISzAllocPtr p, size_t size) { return malloc(size); } +static void SzFree(ISzAllocPtr p, void *address) { free(address); } +const ISzAlloc lzma_Alloc = { SzAlloc, SzFree }; + + +/** + * @brief See if the header has extensions + * @return number of bytes to skip + */ +static size_t htp_gzip_decompressor_probe(const unsigned char *data, + size_t data_len) +{ + if (data_len < 4) + return 0; + + size_t consumed = 0; + + if (data[0] == 0x1f && data[1] == 0x8b && data[3] != 0) { + if (data[3] & (1 << 3) || data[3] & (1 << 4)) { + /* skip past + * - FNAME extension, which is a name ended in a NUL terminator + * or + * - FCOMMENT extension, which is a commend ended in a NULL terminator + */ + + size_t len; + for (len = 10; len < data_len && data[len] != '\0'; len++); + consumed = len + 1; + + //printf("skipped %u bytes for FNAME/FCOMMENT header (GZIP)\n", (uint)consumed); + + } else if (data[3] & (1 << 1)) { + consumed = 12; + //printf("skipped %u bytes for FHCRC header (GZIP)\n", 12); + + } else { + //printf("GZIP unknown/unsupported flags %02X\n", data[3]); + consumed = 10; + } + } + + if (consumed > data_len) + return 0; + + return consumed; +} + +/** + * @brief restart the decompressor + * @return 1 if it restarted, 0 otherwise + */ +static int htp_gzip_decompressor_restart(htp_decompressor_gzip_t *drec, + const unsigned char *data, + size_t data_len, size_t *consumed_back) +{ + size_t consumed = 0; + int rc = 0; + + if (drec->restart < 3) { + + // first retry with the existing type, but now consider the + // extensions + if (drec->restart == 0) { + consumed = htp_gzip_decompressor_probe(data, data_len); + + if (drec->zlib_initialized == HTP_COMPRESSION_GZIP) { + //printf("GZIP restart, consumed %u\n", (uint)consumed); + rc = inflateInit2(&drec->stream, 15 + 32); + } else { + //printf("DEFLATE restart, consumed %u\n", (uint)consumed); + rc = inflateInit2(&drec->stream, -15); + } + if (rc != Z_OK) + return 0; + + goto restart; + + // if that still fails, try the other method we support + + } else if (drec->zlib_initialized == HTP_COMPRESSION_DEFLATE) { + rc = inflateInit2(&drec->stream, 15 + 32); + if (rc != Z_OK) + return 0; + + drec->zlib_initialized = HTP_COMPRESSION_GZIP; + consumed = htp_gzip_decompressor_probe(data, data_len); +#if 0 + printf("DEFLATE -> GZIP consumed %u\n", (uint)consumed); +#endif + goto restart; + + } else if (drec->zlib_initialized == HTP_COMPRESSION_GZIP) { + rc = inflateInit2(&drec->stream, -15); + if (rc != Z_OK) + return 0; + + drec->zlib_initialized = HTP_COMPRESSION_DEFLATE; + consumed = htp_gzip_decompressor_probe(data, data_len); +#if 0 + printf("GZIP -> DEFLATE consumed %u\n", (uint)consumed); +#endif + goto restart; + } + } + return 0; + +restart: +#if 0 + gz_header y; + gz_headerp x = &y; + int res = inflateGetHeader(&drec->stream, x); + printf("HEADER res %d x.os %d x.done %d\n", res, x->os, x->done); +#endif + *consumed_back = consumed; + drec->restart++; + return 1; +} + +/** + * Ends decompressor. + * + * @param[in] drec + */ +static void htp_gzip_decompressor_end(htp_decompressor_gzip_t *drec) { + if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) { + LzmaDec_Free(&drec->state, &lzma_Alloc); + drec->zlib_initialized = 0; + } else if (drec->zlib_initialized) { + inflateEnd(&drec->stream); + drec->zlib_initialized = 0; + } +} + +/** + * Decompress a chunk of gzip-compressed data. + * If we have more than one decompressor, call this function recursively. + * + * @param[in] drec + * @param[in] d + * @return HTP_OK on success, HTP_ERROR or some other negative integer on failure. + */ +htp_status_t htp_gzip_decompressor_decompress(htp_decompressor_t *drec1, htp_tx_data_t *d) { + size_t consumed = 0; + int rc = 0; + htp_status_t callback_rc; + htp_decompressor_gzip_t *drec = (htp_decompressor_gzip_t*) drec1; + + // Pass-through the NULL chunk, which indicates the end of the stream. + + if (drec->super.passthrough) { + htp_tx_data_t d2; + d2.tx = d->tx; + d2.data = d->data; + d2.len = d->len; + d2.is_last = d->is_last; + + callback_rc = drec->super.callback(&d2); + if (callback_rc != HTP_OK) { + return HTP_ERROR; + } + + return HTP_OK; + } + + if (d->data == NULL) { + // Prepare data for callback. + htp_tx_data_t dout; + dout.tx = d->tx; + // This is last call, so output uncompressed data so far + dout.len = GZIP_BUF_SIZE - drec->stream.avail_out; + if (dout.len > 0) { + dout.data = drec->buffer; + } else { + dout.data = NULL; + } + dout.is_last = d->is_last; + if (drec->super.next != NULL && drec->zlib_initialized) { + return htp_gzip_decompressor_decompress(drec->super.next, &dout); + } else { + // Send decompressed data to the callback. + callback_rc = drec->super.callback(&dout); + if (callback_rc != HTP_OK) { + htp_gzip_decompressor_end(drec); + return callback_rc; + } + } + + return HTP_OK; + } + +restart: + if (consumed > d->len || d->len > UINT32_MAX ) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "GZip decompressor: consumed > d->len"); + return HTP_ERROR; + } + drec->stream.next_in = (unsigned char *) (d->data + consumed); + drec->stream.avail_in = (uint32_t) (d->len - consumed); + + while (drec->stream.avail_in != 0) { + // If there's no more data left in the + // buffer, send that information out. + if (drec->stream.avail_out == 0) { + drec->crc = crc32(drec->crc, drec->buffer, GZIP_BUF_SIZE); + + // Prepare data for callback. + htp_tx_data_t d2; + d2.tx = d->tx; + d2.data = drec->buffer; + d2.len = GZIP_BUF_SIZE; + d2.is_last = d->is_last; + + if (drec->super.next != NULL && drec->zlib_initialized) { + callback_rc = htp_gzip_decompressor_decompress(drec->super.next, &d2); + } else { + // Send decompressed data to callback. + callback_rc = drec->super.callback(&d2); + } + if (callback_rc != HTP_OK) { + htp_gzip_decompressor_end(drec); + return callback_rc; + } + + drec->stream.next_out = drec->buffer; + drec->stream.avail_out = GZIP_BUF_SIZE; + } + + if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) { + if (drec->header_len < LZMA_PROPS_SIZE + 8) { + consumed = LZMA_PROPS_SIZE + 8 - drec->header_len; + if (consumed > drec->stream.avail_in) { + consumed = drec->stream.avail_in; + } + memcpy(drec->header + drec->header_len, drec->stream.next_in, consumed); + drec->stream.next_in = (unsigned char *) (d->data + consumed); + drec->stream.avail_in = (uint32_t) (d->len - consumed); + drec->header_len += consumed; + } + if (drec->header_len == LZMA_PROPS_SIZE + 8) { + rc = LzmaDec_Allocate(&drec->state, drec->header, LZMA_PROPS_SIZE, &lzma_Alloc); + if (rc != SZ_OK) + return rc; + LzmaDec_Init(&drec->state); + // hacky to get to next step end retry allocate in case of failure + drec->header_len++; + } + if (drec->header_len > LZMA_PROPS_SIZE + 8) { + size_t inprocessed = drec->stream.avail_in; + size_t outprocessed = drec->stream.avail_out; + ELzmaStatus status; + rc = LzmaDec_DecodeToBuf(&drec->state, drec->stream.next_out, &outprocessed, + drec->stream.next_in, &inprocessed, LZMA_FINISH_ANY, &status, d->tx->cfg->lzma_memlimit); + drec->stream.avail_in -= inprocessed; + drec->stream.next_in += inprocessed; + drec->stream.avail_out -= outprocessed; + drec->stream.next_out += outprocessed; + switch (rc) { + case SZ_OK: + rc = Z_OK; + if (status == LZMA_STATUS_FINISHED_WITH_MARK) { + rc = Z_STREAM_END; + } + break; + case SZ_ERROR_MEM: + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "LZMA decompressor: memory limit reached"); + // fall through + default: + rc = Z_DATA_ERROR; + } + } + } else if (drec->zlib_initialized) { + rc = inflate(&drec->stream, Z_NO_FLUSH); + } else { + // no initialization means previous error on stream + return HTP_ERROR; + } + if (GZIP_BUF_SIZE > drec->stream.avail_out) { + if (rc == Z_DATA_ERROR) { + // There is data even if there is an error + // So use this data and log a warning + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "GZip decompressor: inflate failed with %d", rc); + rc = Z_STREAM_END; + } + } + if (rc == Z_STREAM_END) { + // How many bytes do we have? + size_t len = GZIP_BUF_SIZE - drec->stream.avail_out; + + // Update CRC + + // Prepare data for the callback. + htp_tx_data_t d2; + d2.tx = d->tx; + d2.data = drec->buffer; + d2.len = len; + d2.is_last = d->is_last; + + if (drec->super.next != NULL && drec->zlib_initialized) { + callback_rc = htp_gzip_decompressor_decompress(drec->super.next, &d2); + } else { + // Send decompressed data to the callback. + callback_rc = drec->super.callback(&d2); + } + if (callback_rc != HTP_OK) { + htp_gzip_decompressor_end(drec); + return callback_rc; + } + drec->stream.avail_out = GZIP_BUF_SIZE; + drec->stream.next_out = drec->buffer; + // TODO Handle trailer. + + return HTP_OK; + } + else if (rc != Z_OK) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "GZip decompressor: inflate failed with %d", rc); + if (drec->zlib_initialized == HTP_COMPRESSION_LZMA) { + LzmaDec_Free(&drec->state, &lzma_Alloc); + // so as to clean zlib ressources after restart + drec->zlib_initialized = HTP_COMPRESSION_NONE; + } else { + inflateEnd(&drec->stream); + } + + // see if we want to restart the decompressor + if (htp_gzip_decompressor_restart(drec, + d->data, d->len, &consumed) == 1) + { + // we'll be restarting the compressor + goto restart; + } + + drec->zlib_initialized = 0; + + // all our inflate attempts have failed, simply + // pass the raw data on to the callback in case + // it's not compressed at all + + htp_tx_data_t d2; + d2.tx = d->tx; + d2.data = d->data; + d2.len = d->len; + d2.is_last = d->is_last; + + callback_rc = drec->super.callback(&d2); + if (callback_rc != HTP_OK) { + return HTP_ERROR; + } + + drec->stream.avail_out = GZIP_BUF_SIZE; + drec->stream.next_out = drec->buffer; + + /* successfully passed through, lets continue doing that */ + drec->super.passthrough = 1; + return HTP_OK; + } + } + + return HTP_OK; +} + +/** + * Shut down gzip decompressor. + * + * @param[in] drec + */ +void htp_gzip_decompressor_destroy(htp_decompressor_t *drec1) { + htp_decompressor_gzip_t *drec = (htp_decompressor_gzip_t*) drec1; + if (drec == NULL) return; + + htp_gzip_decompressor_end(drec); + + free(drec->buffer); + free(drec); +} + +/** + * Create a new decompressor instance. + * + * @param[in] connp + * @param[in] format + * @return New htp_decompressor_t instance on success, or NULL on failure. + */ +htp_decompressor_t *htp_gzip_decompressor_create(htp_connp_t *connp, enum htp_content_encoding_t format) { + htp_decompressor_gzip_t *drec = calloc(1, sizeof (htp_decompressor_gzip_t)); + if (drec == NULL) return NULL; + + drec->super.decompress = NULL; + drec->super.destroy = NULL; + drec->super.next = NULL; + + drec->buffer = malloc(GZIP_BUF_SIZE); + if (drec->buffer == NULL) { + free(drec); + return NULL; + } + + // Initialize zlib. + int rc; + + switch (format) { + case HTP_COMPRESSION_LZMA: + if (connp->cfg->lzma_memlimit > 0 && + connp->cfg->response_lzma_layer_limit > 0) { + LzmaDec_Construct(&drec->state); + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "LZMA decompression disabled"); + drec->super.passthrough = 1; + } + rc = Z_OK; + break; + case HTP_COMPRESSION_DEFLATE: + // Negative values activate raw processing, + // which is what we need for deflate. + rc = inflateInit2(&drec->stream, -15); + break; + case HTP_COMPRESSION_GZIP: + // Increased windows size activates gzip header processing. + rc = inflateInit2(&drec->stream, 15 + 32); + break; + default: + // do nothing + rc = Z_DATA_ERROR; + } + + if (rc != Z_OK) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "GZip decompressor: inflateInit2 failed with code %d", rc); + + if (format == HTP_COMPRESSION_DEFLATE || format == HTP_COMPRESSION_GZIP) { + inflateEnd(&drec->stream); + } + free(drec->buffer); + free(drec); + + return NULL; + } + + drec->zlib_initialized = format; + drec->stream.avail_out = GZIP_BUF_SIZE; + drec->stream.next_out = drec->buffer; + + #if 0 + if (format == COMPRESSION_DEFLATE) { + drec->initialized = 1; + } + #endif + + return (htp_decompressor_t *) drec; +} diff --git a/htp/htp_decompressors.h b/htp/htp_decompressors.h new file mode 100644 index 0000000..a357de1 --- /dev/null +++ b/htp/htp_decompressors.h @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_DECOMPRESSORS_H +#define _HTP_DECOMPRESSORS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <zlib.h> +#include "lzma/LzmaDec.h" + +typedef struct htp_decompressor_gzip_t htp_decompressor_gzip_t; +typedef struct htp_decompressor_t htp_decompressor_t; + +#define GZIP_BUF_SIZE 8192 + +#define DEFLATE_MAGIC_1 0x1f +#define DEFLATE_MAGIC_2 0x8b + +struct htp_decompressor_t { + // no longer used + htp_status_t (*decompress)(htp_decompressor_t *, htp_tx_data_t *); + htp_status_t (*callback)(htp_tx_data_t *); + // no longer used + void (*destroy)(htp_decompressor_t *); + struct htp_decompressor_t *next; + struct timeval time_before; + int32_t time_spent; + uint32_t nb_callbacks; + uint8_t passthrough; /**< decompression failed, pass through raw data */ +}; + +struct htp_decompressor_gzip_t { + htp_decompressor_t super; + #if 0 + int initialized; + #endif + int zlib_initialized; + uint8_t restart; /**< deflate restarted to try rfc1950 instead of 1951 */ + z_stream stream; + uint8_t header[LZMA_PROPS_SIZE + 8]; + uint8_t header_len; + CLzmaDec state; + unsigned char *buffer; + unsigned long crc; +}; + +htp_decompressor_t *htp_gzip_decompressor_create(htp_connp_t *connp, enum htp_content_encoding_t format); +htp_status_t htp_gzip_decompressor_decompress(htp_decompressor_t *drec, htp_tx_data_t *d); +void htp_gzip_decompressor_destroy(htp_decompressor_t *drec); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_DECOMPRESSORS_H */ + diff --git a/htp/htp_hooks.c b/htp/htp_hooks.c new file mode 100644 index 0000000..37d0fd4 --- /dev/null +++ b/htp/htp_hooks.c @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +htp_hook_t *htp_hook_copy(const htp_hook_t *hook) { + if (hook == NULL) return NULL; + + htp_hook_t *copy = htp_hook_create(); + if (copy == NULL) return NULL; + + for (size_t i = 0, n = htp_list_size(hook->callbacks); i < n; i++) { + htp_callback_t *callback = htp_list_get(hook->callbacks, i); + if (htp_hook_register(©, callback->fn) != HTP_OK) { + htp_hook_destroy(copy); + return NULL; + } + } + + return copy; +} + +htp_hook_t *htp_hook_create(void) { + htp_hook_t *hook = calloc(1, sizeof (htp_hook_t)); + if (hook == NULL) return NULL; + + hook->callbacks = (htp_list_array_t *) htp_list_array_create(4); + if (hook->callbacks == NULL) { + free(hook); + return NULL; + } + + return hook; +} + +void htp_hook_destroy(htp_hook_t *hook) { + if (hook == NULL) return; + + for (size_t i = 0, n = htp_list_size(hook->callbacks); i < n; i++) { + free((htp_callback_t *) htp_list_get(hook->callbacks, i)); + } + + htp_list_array_destroy(hook->callbacks); + + free(hook); +} + +htp_status_t htp_hook_register(htp_hook_t **hook, const htp_callback_fn_t callback_fn) { + if (hook == NULL) return HTP_ERROR; + + htp_callback_t *callback = calloc(1, sizeof (htp_callback_t)); + if (callback == NULL) return HTP_ERROR; + + callback->fn = callback_fn; + + // Create a new hook if one does not exist + int hook_created = 0; + + if (*hook == NULL) { + hook_created = 1; + + *hook = htp_hook_create(); + if (*hook == NULL) { + free(callback); + return HTP_ERROR; + } + } + + // Add callback + if (htp_list_array_push((*hook)->callbacks, callback) != HTP_OK) { + if (hook_created) { + free(*hook); + } + + free(callback); + + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_hook_run_all(htp_hook_t *hook, void *user_data) { + if (hook == NULL) return HTP_OK; + + // Loop through the registered callbacks, giving each a chance to run. + for (size_t i = 0, n = htp_list_size(hook->callbacks); i < n; i++) { + htp_callback_t *callback = htp_list_get(hook->callbacks, i); + + htp_status_t rc = callback->fn(user_data); + + // A hook can return HTP_OK to say that it did some work, + // or HTP_DECLINED to say that it did no work. Anything else + // is treated as an error. + if ((rc != HTP_OK) && (rc != HTP_DECLINED)) { + return rc; + } + } + + return HTP_OK; +} + +htp_status_t htp_hook_run_one(htp_hook_t *hook, void *user_data) { + if (hook == NULL) return HTP_DECLINED; + + for (size_t i = 0, n = htp_list_size(hook->callbacks); i < n; i++) { + htp_callback_t *callback = htp_list_get(hook->callbacks, i); + + htp_status_t rc = callback->fn(user_data); + + // A hook can return HTP_DECLINED to say that it did no work, + // and we'll ignore that. If we see HTP_OK or anything else, + // we stop processing (because it was either a successful + // handling or an error). + if (rc != HTP_DECLINED) { + // Return HTP_OK or an error. + return rc; + } + } + + // No hook wanted to process the callback. + return HTP_DECLINED; +} diff --git a/htp/htp_hooks.h b/htp/htp_hooks.h new file mode 100644 index 0000000..902a7d4 --- /dev/null +++ b/htp/htp_hooks.h @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HOOKS_H +#define _HOOKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct htp_hook_t htp_hook_t; +typedef struct htp_callback_t htp_callback_t; +typedef int (*htp_callback_fn_t) (void *); + +#include "htp.h" + +struct htp_hook_t { + htp_list_array_t *callbacks; +}; + +struct htp_callback_t { + htp_callback_fn_t fn; +}; + +/** + * Creates a copy of the provided hook. The hook is allowed to be NULL, + * in which case this function simply returns a NULL. + * + * @param[in] hook + * @return A copy of the hook, or NULL (if the provided hook was NULL + * or, if it wasn't, if there was a memory allocation problem while + * constructing a copy). + */ +htp_hook_t *htp_hook_copy(const htp_hook_t *hook); + +/** + * Creates a new hook. + * + * @return New htp_hook_t structure on success, NULL on failure. + */ +htp_hook_t *htp_hook_create(void); + +/** + * Destroys an existing hook. It is all right to send a NULL + * to this method because it will simply return straight away. + * + * @param[in] hook + */ +void htp_hook_destroy(htp_hook_t *hook); + +/** + * Registers a new callback with the hook. + * + * @param[in] hook + * @param[in] callback_fn + * @return HTP_OK on success, HTP_ERROR on memory allocation error. + */ +htp_status_t htp_hook_register(htp_hook_t **hook, const htp_callback_fn_t callback_fn); + +/** + * Runs all the callbacks associated with a given hook. Only stops if + * one of the callbacks returns an error (HTP_ERROR) or stop (HTP_STOP). + * + * @param[in] hook + * @param[in] user_data + * @return HTP_OK if at least one hook ran successfully, HTP_STOP if there was + * no error but processing should stop, and HTP_ERROR or any other value + * less than zero on error. + */ +htp_status_t htp_hook_run_all(htp_hook_t *hook, void *user_data); + +/** + * Run callbacks one by one until one of them accepts to service the hook. + * + * @param[in] hook + * @param[in] user_data + * @return HTP_OK if a hook was found to process the callback, HTP_DECLINED if + * no hook could be found, HTP_STOP if a hook signalled the processing + * to stop, and HTP_ERROR or any other value less than zero on error. + */ +htp_status_t htp_hook_run_one(htp_hook_t *hook, void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* _HOOKS_H */ diff --git a/htp/htp_list.c b/htp/htp_list.c new file mode 100644 index 0000000..b7c42bf --- /dev/null +++ b/htp/htp_list.c @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +// Array-backed list + +htp_status_t htp_list_array_init(htp_list_t *l, size_t size) { + // Allocate the initial batch of elements. + l->elements = malloc(size * sizeof (void *)); + if (l->elements == NULL) { + return HTP_ERROR; + } + + // Initialize the structure. + l->first = 0; + l->last = 0; + l->current_size = 0; + l->max_size = size; + + return HTP_OK; +} + +htp_list_t *htp_list_array_create(size_t size) { + // It makes no sense to create a zero-size list. + if (size == 0) return NULL; + + // Allocate the list structure. + htp_list_array_t *l = calloc(1, sizeof (htp_list_array_t)); + if (l == NULL) return NULL; + + if (htp_list_array_init(l, size) == HTP_ERROR) { + free(l); + return NULL; + } + + return (htp_list_t *) l; +} + +void htp_list_array_clear(htp_list_array_t *l) { + if (l == NULL) return; + + // Continue using already allocated memory; just reset the fields. + l->first = 0; + l->last = 0; + l->current_size = 0; +} + +void htp_list_array_destroy(htp_list_array_t *l) { + if (l == NULL) return; + + free(l->elements); + free(l); +} + +void htp_list_array_release(htp_list_array_t *l) { + if (l == NULL) return; + + free(l->elements); +} + +void *htp_list_array_get(const htp_list_array_t *l, size_t idx) { + if (l == NULL) return NULL; + if (idx >= l->current_size) return NULL; + + if (l->first + idx < l->max_size) { + return (void *) l->elements[l->first + idx]; + } else { + return (void *) l->elements[idx - (l->max_size - l->first)]; + } +} + +void *htp_list_array_pop(htp_list_array_t *l) { + if (l == NULL) return NULL; + + const void *r = NULL; + + if (l->current_size == 0) { + return NULL; + } + + size_t pos = l->first + l->current_size - 1; + if (pos > l->max_size - 1) pos -= l->max_size; + + r = l->elements[pos]; + l->last = pos; + + l->current_size--; + + return (void *) r; +} + +htp_status_t htp_list_array_push(htp_list_array_t *l, void *e) { + if (l == NULL) return HTP_ERROR; + + // Check whether we're full + if (l->current_size >= l->max_size) { + size_t new_size = l->max_size * 2; + void *newblock = NULL; + + if (l->first == 0) { + // The simple case of expansion is when the first + // element in the list resides in the first slot. In + // that case we just add some new space to the end, + // adjust the max_size and that's that. + newblock = realloc(l->elements, new_size * sizeof (void *)); + if (newblock == NULL) return HTP_ERROR; + } else { + // When the first element is not in the first + // memory slot, we need to rearrange the order + // of the elements in order to expand the storage area. + /* coverity[suspicious_sizeof] */ + newblock = malloc((size_t) (new_size * sizeof (void *))); + if (newblock == NULL) return HTP_ERROR; + + // Copy the beginning of the list to the beginning of the new memory block + /* coverity[suspicious_sizeof] */ + memcpy(newblock, + (void *) ((char *) l->elements + l->first * sizeof (void *)), + (size_t) ((l->max_size - l->first) * sizeof (void *))); + + // Append the second part of the list to the end + memcpy((void *) ((char *) newblock + (l->max_size - l->first) * sizeof (void *)), + (void *) l->elements, + (size_t) (l->first * sizeof (void *))); + + free(l->elements); + } + + l->first = 0; + l->last = l->current_size; + l->max_size = new_size; + l->elements = newblock; + } + + l->elements[l->last] = e; + l->current_size++; + + l->last++; + if (l->last == l->max_size) { + l->last = 0; + } + + return HTP_OK; +} + +htp_status_t htp_list_array_replace(htp_list_array_t *l, size_t idx, void *e) { + if (l == NULL) return HTP_ERROR; + + if (idx + 1 > l->current_size) return HTP_DECLINED; + + l->elements[(l->first + idx) % l->max_size] = e; + + return HTP_OK; +} + +size_t htp_list_array_size(const htp_list_array_t *l) { + if (l == NULL) return HTP_ERROR; + + return l->current_size; +} + +void *htp_list_array_shift(htp_list_array_t *l) { + if (l == NULL) return NULL; + + void *r = NULL; + + if (l->current_size == 0) { + return NULL; + } + + r = l->elements[l->first]; + l->first++; + if (l->first == l->max_size) { + l->first = 0; + } + + l->current_size--; + + return r; +} + +#if 0 +// Linked list + +htp_list_linked_t *htp_list_linked_create(void) { + htp_list_linked_t *l = calloc(1, sizeof (htp_list_linked_t)); + if (l == NULL) return NULL; + + return l; +} + +void htp_list_linked_destroy(htp_list_linked_t *l) { + if (l == NULL) return; + + // Free the list structures + htp_list_linked_element_t *temp = l->first; + htp_list_linked_element_t *prev = NULL; + while (temp != NULL) { + free(temp->data); + prev = temp; + temp = temp->next; + free(prev); + } + + // Free the list itself + free(l); +} + +int htp_list_linked_empty(const htp_list_linked_t *l) { + if (!l->first) { + return 1; + } else { + return 0; + } +} + +void *htp_list_linked_pop(htp_list_linked_t *l) { + void *r = NULL; + + if (!l->first) { + return NULL; + } + + // Find the last element + htp_list_linked_element_t *qprev = NULL; + htp_list_linked_element_t *qe = l->first; + while (qe->next != NULL) { + qprev = qe; + qe = qe->next; + } + + r = qe->data; + free(qe); + + if (qprev != NULL) { + qprev->next = NULL; + l->last = qprev; + } else { + l->first = NULL; + l->last = NULL; + } + + return r; +} + +int htp_list_linked_push(htp_list_linked_t *l, void *e) { + htp_list_linked_element_t *le = calloc(1, sizeof (htp_list_linked_element_t)); + if (le == NULL) return -1; + + // Remember the element + le->data = e; + + // If the queue is empty, make this element first + if (!l->first) { + l->first = le; + } + + if (l->last) { + l->last->next = le; + } + + l->last = le; + + return 1; +} + +void *htp_list_linked_shift(htp_list_linked_t *l) { + void *r = NULL; + + if (!l->first) { + return NULL; + } + + htp_list_linked_element_t *le = l->first; + l->first = le->next; + r = le->data; + + if (!l->first) { + l->last = NULL; + } + + free(le); + + return r; +} +#endif + +#if 0 + +int main(int argc, char **argv) { + htp_list_t *q = htp_list_array_create(4); + + htp_list_push(q, "1"); + htp_list_push(q, "2"); + htp_list_push(q, "3"); + htp_list_push(q, "4"); + + htp_list_shift(q); + htp_list_push(q, "5"); + htp_list_push(q, "6"); + + char *s = NULL; + while ((s = (char *) htp_list_pop(q)) != NULL) { + printf("Got: %s\n", s); + } + + printf("---\n"); + + htp_list_push(q, "1"); + htp_list_push(q, "2"); + htp_list_push(q, "3"); + htp_list_push(q, "4"); + + while ((s = (char *) htp_list_shift(q)) != NULL) { + printf("Got: %s\n", s); + } + + free(q); + + return 0; +} +#endif diff --git a/htp/htp_list.h b/htp/htp_list.h new file mode 100644 index 0000000..8a2bd63 --- /dev/null +++ b/htp/htp_list.h @@ -0,0 +1,227 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_LIST_H +#define HTP_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +// The default list implementation is array-based. The +// linked list version is not fully implemented yet. +#define htp_list_t htp_list_array_t +#define htp_list_add htp_list_array_push +#define htp_list_create htp_list_array_create +#define htp_list_init htp_list_array_init +#define htp_list_clear htp_list_array_clear +#define htp_list_destroy htp_list_array_destroy +#define htp_list_release htp_list_array_release +#define htp_list_get htp_list_array_get +#define htp_list_pop htp_list_array_pop +#define htp_list_push htp_list_array_push +#define htp_list_replace htp_list_array_replace +#define htp_list_size htp_list_array_size +#define htp_list_shift htp_list_array_shift + +// Data structures + +typedef struct htp_list_array_t htp_list_array_t; +typedef struct htp_list_linked_t htp_list_linked_t; + +#include "htp_core.h" +#include "bstr.h" + +// Functions + +/** + * Create new array-backed list. + * + * @param[in] size + * @return Newly created list. + */ +htp_list_array_t *htp_list_array_create(size_t size); + +/** + * Initialize an array-backed list. + * + * @param[in] l + * @param[in] size + * @return HTP_OK or HTP_ERROR if allocation failed + */ +htp_status_t htp_list_array_init(htp_list_array_t *l, size_t size); + +/** + * Remove all elements from the list. It is the responsibility of the caller + * to iterate over list elements and deallocate them if necessary, prior to + * invoking this function. + * + * @param[in] l + */ +void htp_list_array_clear(htp_list_array_t *l); + +/** + * Free the memory occupied by this list. This function assumes + * the elements held by the list were freed beforehand. + * + * @param[in] l + */ +void htp_list_array_destroy(htp_list_array_t *l); + +/** + * Free the memory occupied by this list, except itself. + * This function assumes the elements held by the list + * were freed beforehand. + * + * @param[in] l + */ +void htp_list_array_release(htp_list_array_t *l); + +/** + * Find the element at the given index. + * + * @param[in] l + * @param[in] idx + * @return the desired element, or NULL if the list is too small, or + * if the element at that position carries a NULL + */ +void *htp_list_array_get(const htp_list_array_t *l, size_t idx); + +/** + * Remove one element from the end of the list. + * + * @param[in] l + * @return The removed element, or NULL if the list is empty. + */ +void *htp_list_array_pop(htp_list_array_t *l); + +/** + * Add new element to the end of the list, expanding the list as necessary. + * + * @param[in] l + * @param[in] e + * @return HTP_OK on success or HTP_ERROR on failure. + * + */ +htp_status_t htp_list_array_push(htp_list_array_t *l, void *e); + +/** + * Replace the element at the given index with the provided element. + * + * @param[in] l + * @param[in] idx + * @param[in] e + * + * @return HTP_OK if an element with the given index was replaced; HTP_ERROR + * if the desired index does not exist. + */ +htp_status_t htp_list_array_replace(htp_list_array_t *l, size_t idx, void *e); + +/** + * Returns the size of the list. + * + * @param[in] l + * @return List size. + */ +size_t htp_list_array_size(const htp_list_array_t *l); + +/** + * Remove one element from the beginning of the list. + * + * @param[in] l + * @return The removed element, or NULL if the list is empty. + */ +void *htp_list_array_shift(htp_list_array_t *l); + + +// Linked list + +/** + * Create a new linked list. + * + * @return The newly created list, or NULL on memory allocation failure + */ +htp_list_linked_t *htp_list_linked_create(void); + +/** + * Destroy list. This function will not destroy any of the + * data stored in it. You'll have to do that manually beforehand. + * + * @param[in] l + */ +void htp_list_linked_destroy(htp_list_linked_t *l); + +/** + * Is the list empty? + * + * @param[in] l + * @return 1 if the list is empty, 0 if it is not + */ +int htp_list_linked_empty(const htp_list_linked_t *l); + +/** + * Remove one element from the end of the list. + * + * @param[in] l + * @return Pointer to the removed element, or NULL if the list is empty. + */ +void *htp_list_linked_pop(htp_list_linked_t *l); + +/** + * Add element to list. + * + * @param[in] l + * @param[in] e + * @return HTP_OK on success, HTP_ERROR on error. + */ +htp_status_t htp_list_linked_push(htp_list_linked_t *l, void *e); + +/** + * Remove one element from the beginning of the list. + * + * @param[in] l + * @return Pointer to the removed element, or NULL if the list is empty. + */ +void *htp_list_linked_shift(htp_list_linked_t *l); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_LIST_H */ + diff --git a/htp/htp_list_private.h b/htp/htp_list_private.h new file mode 100644 index 0000000..6f462c0 --- /dev/null +++ b/htp/htp_list_private.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_LIST_PRIVATE_H +#define HTP_LIST_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp_list.h" + +typedef struct htp_list_linked_element_t htp_list_linked_element_t; + +struct htp_list_array_t { + size_t first; + size_t last; + size_t max_size; + size_t current_size; + void **elements; +}; + +struct htp_list_linked_element_t { + void *data; + htp_list_linked_element_t *next; +}; + +struct htp_list_linked_t { + htp_list_linked_element_t *first; + htp_list_linked_element_t *last; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_LIST_PRIVATE_H */ + diff --git a/htp/htp_multipart.c b/htp/htp_multipart.c new file mode 100644 index 0000000..ea73072 --- /dev/null +++ b/htp/htp_multipart.c @@ -0,0 +1,1615 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Determines the type of a Content-Disposition parameter. + * + * @param[in] data + * @param[in] startpos + * @param[in] pos + * @return CD_PARAM_OTHER, CD_PARAM_NAME or CD_PARAM_FILENAME. + */ +static int htp_mpartp_cd_param_type(unsigned char *data, size_t startpos, size_t endpos) { + if ((endpos - startpos) == 4) { + if (memcmp(data + startpos, "name", 4) == 0) return CD_PARAM_NAME; + } else if ((endpos - startpos) == 8) { + if (memcmp(data + startpos, "filename", 8) == 0) return CD_PARAM_FILENAME; + } + + return CD_PARAM_OTHER; +} + +htp_multipart_t *htp_mpartp_get_multipart(htp_mpartp_t *parser) { + return &(parser->multipart); +} + +/** + * Decodes a C-D header value. This is impossible to do correctly without a + * parsing personality because most browsers are broken: + * - Firefox encodes " as \", and \ is not encoded. + * - Chrome encodes " as %22. + * - IE encodes " as \", and \ is not encoded. + * - Opera encodes " as \" and \ as \\. + * @param[in] b + */ +static void htp_mpart_decode_quoted_cd_value_inplace(bstr *b) { + unsigned char *s = bstr_ptr(b); + unsigned char *d = bstr_ptr(b); + size_t len = bstr_len(b); + size_t pos = 0; + + while (pos < len) { + // Ignore \ when before \ or ". + if ((*s == '\\')&&(pos + 1 < len)&&((*(s + 1) == '"')||(*(s + 1) == '\\'))) { + s++; + pos++; + } + + *d++ = *s++; + pos++; + } + + bstr_adjust_len(b, len - (s - d)); +} + +/** + * Parses the Content-Disposition part header. + * + * @param[in] part + * @return HTP_OK on success (header found and parsed), HTP_DECLINED if there is no C-D header or if + * it could not be processed, and HTP_ERROR on fatal error. + */ +htp_status_t htp_mpart_part_parse_c_d(htp_multipart_part_t *part) { + // Find the C-D header. + htp_header_t *h = htp_table_get_c(part->headers, "content-disposition"); + if (h == NULL) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_UNKNOWN; + return HTP_DECLINED; + } + + // Require "form-data" at the beginning of the header. + if (bstr_index_of_c(h->value, "form-data") != 0) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // The parsing starts here. + unsigned char *data = bstr_ptr(h->value); + size_t len = bstr_len(h->value); + size_t pos = 9; // Start after "form-data" + + // Main parameter parsing loop (once per parameter). + while (pos < len) { + // Ignore whitespace. + while ((pos < len) && isspace(data[pos])) pos++; + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Expecting a semicolon. + if (data[pos] != ';') { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + pos++; + + // Go over the whitespace before parameter name. + while ((pos < len) && isspace(data[pos])) pos++; + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Found the starting position of the parameter name. + size_t start = pos; + + // Look for the ending position. + while ((pos < len) && (!isspace(data[pos]) && (data[pos] != '='))) pos++; + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Ending position is in "pos" now. + + // Determine parameter type ("name", "filename", or other). + int param_type = htp_mpartp_cd_param_type(data, start, pos); + + // Ignore whitespace after parameter name, if any. + while ((pos < len) && isspace(data[pos])) pos++; + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Equals. + if (data[pos] != '=') { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + pos++; + + // Go over the whitespace before the parameter value. + while ((pos < len) && isspace(data[pos])) pos++; + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Expecting a double quote. + if (data[pos] != '"') { + // Bare string or non-standard quoting, which we don't like. + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + pos++; // Over the double quote. + + // We have the starting position of the value. + start = pos; + + // Find the end of the value. + while ((pos < len) && (data[pos] != '"')) { + // Check for escaping. + if (data[pos] == '\\') { + if (pos + 1 >= len) { + // A backslash as the last character in the C-D header. + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Allow " and \ to be escaped. + if ((data[pos + 1] == '"')||(data[pos + 1] == '\\')) { + // Go over the quoted character. + pos++; + } + } + + pos++; + } + + // If we've reached the end of the string that means the + // value was not terminated properly (the second double quote is missing). + if (pos == len) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + // Expecting the terminating double quote. + if (data[pos] != '"') { + part->parser->multipart.flags |= HTP_MULTIPART_CD_SYNTAX_INVALID; + return HTP_DECLINED; + } + + pos++; // Over the terminating double quote. + + // Finally, process the parameter value. + + switch (param_type) { + case CD_PARAM_NAME: + // Check that we have not seen the name parameter already. + if (part->name != NULL) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_PARAM_REPEATED; + return HTP_DECLINED; + } + + part->name = bstr_dup_mem(data + start, pos - start - 1); + if (part->name == NULL) return HTP_ERROR; + + htp_mpart_decode_quoted_cd_value_inplace(part->name); + + break; + + case CD_PARAM_FILENAME: + // Check that we have not seen the filename parameter already. + if (part->file != NULL) { + part->parser->multipart.flags |= HTP_MULTIPART_CD_PARAM_REPEATED; + return HTP_DECLINED; + } + + part->file = calloc(1, sizeof (htp_file_t)); + if (part->file == NULL) return HTP_ERROR; + + part->file->fd = -1; + part->file->source = HTP_FILE_MULTIPART; + + part->file->filename = bstr_dup_mem(data + start, pos - start - 1); + if (part->file->filename == NULL) { + free(part->file); + return HTP_ERROR; + } + + htp_mpart_decode_quoted_cd_value_inplace(part->file->filename); + + break; + + default: + // Unknown parameter. + part->parser->multipart.flags |= HTP_MULTIPART_CD_PARAM_UNKNOWN; + return HTP_DECLINED; + break; + } + + // Continue to parse the next parameter, if any. + } + + return HTP_OK; +} + +/** + * Parses the Content-Type part header, if present. + * + * @param[in] part + * @return HTP_OK on success, HTP_DECLINED if the C-T header is not present, and HTP_ERROR on failure. + */ +static htp_status_t htp_mpart_part_parse_c_t(htp_multipart_part_t *part) { + htp_header_t *h = (htp_header_t *) htp_table_get_c(part->headers, "content-type"); + if (h == NULL) return HTP_DECLINED; + return htp_parse_ct_header(h->value, &part->content_type); +} + +/** + * Processes part headers. + * + * @param[in] part + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_mpart_part_process_headers(htp_multipart_part_t *part) { + if (htp_mpart_part_parse_c_d(part) == HTP_ERROR) return HTP_ERROR; + if (htp_mpart_part_parse_c_t(part) == HTP_ERROR) return HTP_ERROR; + + return HTP_OK; +} + +/** + * Parses one part header. + * + * @param[in] part + * @param[in] data + * @param[in] len + * @return HTP_OK on success, HTP_DECLINED on parsing error, HTP_ERROR on fatal error. + */ +htp_status_t htp_mpartp_parse_header(htp_multipart_part_t *part, const unsigned char *data, size_t len) { + size_t name_start, name_end; + size_t value_start, value_end; + + // We do not allow NUL bytes here. + if (memchr(data, '\0', len) != NULL) { + part->parser->multipart.flags |= HTP_MULTIPART_NUL_BYTE; + return HTP_DECLINED; + } + + name_start = 0; + + // Look for the starting position of the name first. + size_t colon_pos = 0; + + while ((colon_pos < len)&&(htp_is_space(data[colon_pos]))) colon_pos++; + if (colon_pos != 0) { + // Whitespace before header name. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + // Now look for the colon. + while ((colon_pos < len) && (data[colon_pos] != ':')) colon_pos++; + + if (colon_pos == len) { + // Missing colon. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + if (colon_pos == 0) { + // Empty header name. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + name_end = colon_pos; + + // Ignore LWS after header name. + size_t prev = name_end; + while ((prev > name_start) && (htp_is_lws(data[prev - 1]))) { + prev--; + name_end--; + + // LWS after field name. Not allowing for now. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + // Header value. + + value_start = colon_pos + 1; + + // Ignore LWS before value. + while ((value_start < len) && (htp_is_lws(data[value_start]))) value_start++; + + if (value_start == len) { + // No header value. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + // Assume the value is at the end. + value_end = len; + + // Check that the header name is a token. + size_t i = name_start; + while (i < name_end) { + if (!htp_is_token(data[i])) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_INVALID; + return HTP_DECLINED; + } + + i++; + } + + // Now extract the name and the value. + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return HTP_ERROR; + + h->name = bstr_dup_mem(data + name_start, name_end - name_start); + if (h->name == NULL) { + free(h); + return HTP_ERROR; + } + + h->value = bstr_dup_mem(data + value_start, value_end - value_start); + if (h->value == NULL) { + bstr_free(h->name); + free(h); + return HTP_ERROR; + } + + if ((bstr_cmp_c_nocase(h->name, "content-disposition") != 0) && (bstr_cmp_c_nocase(h->name, "content-type") != 0)) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_UNKNOWN; + } + + // Check if the header already exists. + htp_header_t * h_existing = htp_table_get(part->headers, h->name); + if (h_existing != NULL) { + // Add to the existing header. + bstr *new_value = bstr_expand(h_existing->value, bstr_len(h_existing->value) + + 2 + bstr_len(h->value)); + if (new_value == NULL) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + + h_existing->value = new_value; + bstr_add_mem_noex(h_existing->value, ", ", 2); + bstr_add_noex(h_existing->value, h->value); + + // The header is no longer needed. + bstr_free(h->name); + bstr_free(h->value); + free(h); + + // Keep track of same-name headers. + h_existing->flags |= HTP_MULTIPART_PART_HEADER_REPEATED; + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_REPEATED; + } else { + // Add as a new header. + if (htp_table_add(part->headers, h->name, h) != HTP_OK) { + bstr_free(h->value); + bstr_free(h->name); + free(h); + return HTP_ERROR; + } + } + + return HTP_OK; +} + +/** + * Creates a new Multipart part. + * + * @param[in] parser + * @return New part instance, or NULL on memory allocation failure. + */ +htp_multipart_part_t *htp_mpart_part_create(htp_mpartp_t *parser) { + htp_multipart_part_t * part = calloc(1, sizeof (htp_multipart_part_t)); + if (part == NULL) return NULL; + + part->headers = htp_table_create(4); + if (part->headers == NULL) { + free(part); + return NULL; + } + + part->parser = parser; + bstr_builder_clear(parser->part_data_pieces); + bstr_builder_clear(parser->part_header_pieces); + + return part; +} + +/** + * Destroys a part. + * + * @param[in] part + * @param[in] gave_up_data + */ +void htp_mpart_part_destroy(htp_multipart_part_t *part, int gave_up_data) { + if (part == NULL) return; + + if (part->file != NULL) { + bstr_free(part->file->filename); + + if (part->file->tmpname != NULL) { + unlink(part->file->tmpname); + free(part->file->tmpname); + } + + free(part->file); + part->file = NULL; + } + + if ((!gave_up_data) || (part->type != MULTIPART_PART_TEXT)) { + bstr_free(part->name); + bstr_free(part->value); + } + + bstr_free(part->content_type); + + if (part->headers != NULL) { + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(part->headers); i < n; i++) { + h = htp_table_get_index(part->headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_destroy(part->headers); + } + + free(part); +} + +/** + * Finalizes part processing. + * + * @param[in] part + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_mpart_part_finalize_data(htp_multipart_part_t *part) { + // Determine if this part is the epilogue. + + if (part->parser->multipart.flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY) { + if (part->type == MULTIPART_PART_UNKNOWN) { + // Assume that the unknown part after the last boundary is the epilogue. + part->parser->current_part->type = MULTIPART_PART_EPILOGUE; + + // But if we've already seen a part we thought was the epilogue, + // raise HTP_MULTIPART_PART_UNKNOWN. Multiple epilogues are not allowed. + if (part->parser->multipart.flags & HTP_MULTIPART_HAS_EPILOGUE) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_UNKNOWN; + } + + part->parser->multipart.flags |= HTP_MULTIPART_HAS_EPILOGUE; + } else { + part->parser->multipart.flags |= HTP_MULTIPART_PART_AFTER_LAST_BOUNDARY; + } + } + + // Sanity checks. + + // Have we seen complete part headers? If we have not, that means that the part ended prematurely. + if ((part->parser->current_part->type != MULTIPART_PART_EPILOGUE) && (part->parser->current_part_mode != MODE_DATA)) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_INCOMPLETE; + } + + // Have we been able to determine the part type? If not, this means + // that the part did not contain the C-D header. + if (part->type == MULTIPART_PART_UNKNOWN) { + part->parser->multipart.flags |= HTP_MULTIPART_PART_UNKNOWN; + } + + // Finalize part value. + + if (part->type == MULTIPART_PART_FILE) { + // Notify callbacks about the end of the file. + htp_mpartp_run_request_file_data_hook(part, NULL, 0); + + // If we are storing the file to disk, close the file descriptor. + if (part->file->fd != -1) { + close(part->file->fd); + } + } else { + // Combine value pieces into a single buffer. + if (bstr_builder_size(part->parser->part_data_pieces) > 0) { + part->value = bstr_builder_to_str(part->parser->part_data_pieces); + bstr_builder_clear(part->parser->part_data_pieces); + } + } + + return HTP_OK; +} + +htp_status_t htp_mpartp_run_request_file_data_hook(htp_multipart_part_t *part, const unsigned char *data, size_t len) { + if (part->parser->cfg == NULL) return HTP_OK; + + // Keep track of the file length. + part->file->len += len; + + // Package data for the callbacks. + htp_file_data_t file_data; + file_data.file = part->file; + file_data.data = data; + file_data.len = (const size_t) len; + + // Send data to callbacks + htp_status_t rc = htp_hook_run_all(part->parser->cfg->hook_request_file_data, &file_data); + if (rc != HTP_OK) return rc; + + return HTP_OK; +} + +/** + * Handles part data. + * + * @param[in] part + * @param[in] data + * @param[in] len + * @param[in] is_line + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_mpart_part_handle_data(htp_multipart_part_t *part, const unsigned char *data, size_t len, int is_line) { + #if HTP_DEBUG + fprintf(stderr, "Part type %d mode %d is_line %d\n", part->type, part->parser->current_part_mode, is_line); + fprint_raw_data(stderr, "htp_mpart_part_handle_data: data chunk", data, len); + #endif + + // Keep track of raw part length. + part->len += len; + + // If we're processing a part that came after the last boundary, then we're not sure if it + // is the epilogue part or some other part (in case of evasion attempt). For that reason we + // will keep all its data in the part_data_pieces structure. If it ends up not being the + // epilogue, this structure will be cleared. + if ((part->parser->multipart.flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY) && (part->type == MULTIPART_PART_UNKNOWN)) { + bstr_builder_append_mem(part->parser->part_data_pieces, data, len); + } + + if (part->parser->current_part_mode == MODE_LINE) { + // Line mode. + + if (is_line) { + // End of the line. + + bstr *line = NULL; + + // If this line came to us in pieces, combine them now into a single buffer. + if (bstr_builder_size(part->parser->part_header_pieces) > 0) { + bstr_builder_append_mem(part->parser->part_header_pieces, data, len); + line = bstr_builder_to_str(part->parser->part_header_pieces); + if (line == NULL) return HTP_ERROR; + bstr_builder_clear(part->parser->part_header_pieces); + + data = bstr_ptr(line); + len = bstr_len(line); + } + + // Ignore the line endings. + if (len > 1) { + if (data[len - 1] == LF) len--; + if (data[len - 1] == CR) len--; + } else if (len > 0) { + if (data[len - 1] == LF) len--; + } + + // Is it an empty line? + if (len == 0) { + // Empty line; process headers and switch to data mode. + + // Process the pending header, if any. + if (part->parser->pending_header_line != NULL) { + if (htp_mpartp_parse_header(part, bstr_ptr(part->parser->pending_header_line), + bstr_len(part->parser->pending_header_line)) == HTP_ERROR) + { + bstr_free(line); + return HTP_ERROR; + } + + bstr_free(part->parser->pending_header_line); + part->parser->pending_header_line = NULL; + } + + if (htp_mpart_part_process_headers(part) == HTP_ERROR) { + bstr_free(line); + return HTP_ERROR; + } + + part->parser->current_part_mode = MODE_DATA; + bstr_builder_clear(part->parser->part_header_pieces); + + if (part->file != NULL) { + // Changing part type because we have a filename. + part->type = MULTIPART_PART_FILE; + + if ((part->parser->extract_files) && (part->parser->file_count < part->parser->extract_limit)) { + char buf[255]; + + strncpy(buf, part->parser->extract_dir, 254); + strncat(buf, "/libhtp-multipart-file-XXXXXX", 254 - strlen(buf)); + + part->file->tmpname = strdup(buf); + if (part->file->tmpname == NULL) { + bstr_free(line); + return HTP_ERROR; + } + + mode_t previous_mask = umask(S_IXUSR | S_IRWXG | S_IRWXO); + part->file->fd = mkstemp(part->file->tmpname); + umask(previous_mask); + + if (part->file->fd < 0) { + bstr_free(line); + return HTP_ERROR; + } + + part->parser->file_count++; + } + } else if (part->name != NULL) { + // Changing part type because we have a name. + part->type = MULTIPART_PART_TEXT; + bstr_builder_clear(part->parser->part_data_pieces); + } else { + // Do nothing; the type stays MULTIPART_PART_UNKNOWN. + } + } else { + // Not an empty line. + + // Is there a pending header? + if (part->parser->pending_header_line == NULL) { + if (line != NULL) { + part->parser->pending_header_line = line; + line = NULL; + } else { + part->parser->pending_header_line = bstr_dup_mem(data, len); + if (part->parser->pending_header_line == NULL) return HTP_ERROR; + } + } else { + // Is this a folded line? + if (isspace(data[0])) { + // Folding; add to the existing line. + part->parser->multipart.flags |= HTP_MULTIPART_PART_HEADER_FOLDING; + part->parser->pending_header_line = bstr_add_mem(part->parser->pending_header_line, data, len); + if (part->parser->pending_header_line == NULL) { + bstr_free(line); + return HTP_ERROR; + } + } else { + // Process the pending header line. + if (htp_mpartp_parse_header(part, bstr_ptr(part->parser->pending_header_line), + bstr_len(part->parser->pending_header_line)) == HTP_ERROR) + { + bstr_free(line); + return HTP_ERROR; + } + + bstr_free(part->parser->pending_header_line); + + if (line != NULL) { + part->parser->pending_header_line = line; + line = NULL; + } else { + part->parser->pending_header_line = bstr_dup_mem(data, len); + if (part->parser->pending_header_line == NULL) return HTP_ERROR; + } + } + } + } + + bstr_free(line); + line = NULL; + } else { + // Not end of line; keep the data chunk for later. + bstr_builder_append_mem(part->parser->part_header_pieces, data, len); + } + } else { + // Data mode; keep the data chunk for later (but not if it is a file). + switch (part->type) { + case MULTIPART_PART_EPILOGUE: + case MULTIPART_PART_PREAMBLE: + case MULTIPART_PART_TEXT: + case MULTIPART_PART_UNKNOWN: + // Make a copy of the data in RAM. + bstr_builder_append_mem(part->parser->part_data_pieces, data, len); + break; + + case MULTIPART_PART_FILE: + // Invoke file data callbacks. + htp_mpartp_run_request_file_data_hook(part, data, len); + + // Optionally, store the data in a file. + if (part->file->fd != -1) { + if (write(part->file->fd, data, len) < 0) { + return HTP_ERROR; + } + } + break; + + default: + // Internal error. + return HTP_ERROR; + break; + } + } + + return HTP_OK; +} + +/** + * Handles data, creating new parts as necessary. + * + * @param[in] mpartp + * @param[in] data + * @param[in] len + * @param[in] is_line + * @return HTP_OK on success, HTP_ERROR on failure. + */ +static htp_status_t htp_mpartp_handle_data(htp_mpartp_t *parser, const unsigned char *data, size_t len, int is_line) { + if (len == 0) return HTP_OK; + + // Do we have a part already? + if (parser->current_part == NULL) { + // Create a new part. + parser->current_part = htp_mpart_part_create(parser); + if (parser->current_part == NULL) return HTP_ERROR; + + if (parser->multipart.boundary_count == 0) { + // We haven't seen a boundary yet, so this must be the preamble part. + parser->current_part->type = MULTIPART_PART_PREAMBLE; + parser->multipart.flags |= HTP_MULTIPART_HAS_PREAMBLE; + parser->current_part_mode = MODE_DATA; + } else { + // Part after preamble. + parser->current_part_mode = MODE_LINE; + } + + // Add part to the list. + htp_list_push(parser->multipart.parts, parser->current_part); + + #ifdef HTP_DEBUG + fprintf(stderr, "Created new part type %d\n", parser->current_part->type); + #endif + } + + // Send data to the part. + return htp_mpart_part_handle_data(parser->current_part, data, len, is_line); +} + +/** + * Handles a boundary event, which means that it will finalize a part if one exists. + * + * @param[in] mpartp + * @return HTP_OK on success, HTP_ERROR on failure. + */ +static htp_status_t htp_mpartp_handle_boundary(htp_mpartp_t *parser) { + #if HTP_DEBUG + fprintf(stderr, "htp_mpartp_handle_boundary\n"); + #endif + + if (parser->current_part != NULL) { + if (htp_mpart_part_finalize_data(parser->current_part) != HTP_OK) { + return HTP_ERROR; + } + + // We're done with this part + parser->current_part = NULL; + + // Revert to line mode + parser->current_part_mode = MODE_LINE; + } + + return HTP_OK; +} + +static htp_status_t htp_mpartp_init_boundary(htp_mpartp_t *parser, unsigned char *data, size_t len) { + if ((parser == NULL) || (data == NULL)) return HTP_ERROR; + + // Copy the boundary and convert it to lowercase. + + parser->multipart.boundary_len = len + 4; + parser->multipart.boundary = malloc(parser->multipart.boundary_len + 1); + if (parser->multipart.boundary == NULL) return HTP_ERROR; + + parser->multipart.boundary[0] = CR; + parser->multipart.boundary[1] = LF; + parser->multipart.boundary[2] = '-'; + parser->multipart.boundary[3] = '-'; + + for (size_t i = 0; i < len; i++) { + parser->multipart.boundary[i + 4] = data[i]; + } + + parser->multipart.boundary[parser->multipart.boundary_len] = '\0'; + + // We're starting in boundary-matching mode. The first boundary can appear without the + // CRLF, and our starting state expects that. If we encounter non-boundary data, the + // state will switch to data mode. Then, if the data is CRLF or LF, we will go back + // to boundary matching. Thus, we handle all the possibilities. + + parser->parser_state = STATE_BOUNDARY; + parser->boundary_match_pos = 2; + + return HTP_OK; +} + +htp_mpartp_t *htp_mpartp_create(htp_cfg_t *cfg, bstr *boundary, uint64_t flags) { + if ((cfg == NULL) || (boundary == NULL)) return NULL; + + htp_mpartp_t *parser = calloc(1, sizeof (htp_mpartp_t)); + if (parser == NULL) return NULL; + + parser->cfg = cfg; + + parser->boundary_pieces = bstr_builder_create(); + if (parser->boundary_pieces == NULL) { + htp_mpartp_destroy(parser); + return NULL; + } + + parser->part_data_pieces = bstr_builder_create(); + if (parser->part_data_pieces == NULL) { + htp_mpartp_destroy(parser); + return NULL; + } + + parser->part_header_pieces = bstr_builder_create(); + if (parser->part_header_pieces == NULL) { + htp_mpartp_destroy(parser); + return NULL; + } + + parser->multipart.parts = htp_list_create(64); + if (parser->multipart.parts == NULL) { + htp_mpartp_destroy(parser); + return NULL; + } + + parser->multipart.flags = flags; + parser->parser_state = STATE_INIT; + parser->extract_files = cfg->extract_request_files; + parser->extract_dir = cfg->tmpdir; + if (cfg->extract_request_files_limit >= 0) { + parser->extract_limit = cfg->extract_request_files_limit; + } else { + parser->extract_limit = DEFAULT_FILE_EXTRACT_LIMIT; + } + parser->handle_data = htp_mpartp_handle_data; + parser->handle_boundary = htp_mpartp_handle_boundary; + + // Initialize the boundary. + htp_status_t rc = htp_mpartp_init_boundary(parser, bstr_ptr(boundary), bstr_len(boundary)); + if (rc != HTP_OK) { + htp_mpartp_destroy(parser); + return NULL; + } + + // On success, the ownership of the boundary parameter + // is transferred to us. We made a copy, and so we + // don't need it any more. + bstr_free(boundary); + + return parser; +} + +void htp_mpartp_destroy(htp_mpartp_t *parser) { + if (parser == NULL) return; + + if (parser->multipart.boundary != NULL) { + free(parser->multipart.boundary); + } + + bstr_builder_destroy(parser->boundary_pieces); + bstr_builder_destroy(parser->part_header_pieces); + bstr_free(parser->pending_header_line); + bstr_builder_destroy(parser->part_data_pieces); + + // Free the parts. + if (parser->multipart.parts != NULL) { + for (size_t i = 0, n = htp_list_size(parser->multipart.parts); i < n; i++) { + htp_multipart_part_t * part = htp_list_get(parser->multipart.parts, i); + htp_mpart_part_destroy(part, parser->gave_up_data); + } + + htp_list_destroy(parser->multipart.parts); + } + + free(parser); +} + +/** + * Processes set-aside data. + * + * @param[in] mpartp + * @param[in] data + * @param[in] pos + * @param[in] startpos + * @param[in] return_pos + * @param[in] matched + * @return HTP_OK on success, HTP_ERROR on failure. + */ +static htp_status_t htp_martp_process_aside(htp_mpartp_t *parser, int matched) { + // The stored data pieces can contain up to one line. If we're in data mode and there + // was no boundary match, things are straightforward -- we process everything as data. + // If there was a match, we need to take care to not send the line ending as data, nor + // anything that follows (because it's going to be a part of the boundary). Similarly, + // when we are in line mode, we need to split the first data chunk, processing the first + // part as line and the second part as data. + + #ifdef HTP_DEBUG + fprintf(stderr, "mpartp_process_aside matched %d current_part_mode %d\n", matched, parser->current_part_mode); + #endif + + // Do we need to do any chunk splitting? + if (matched || (parser->current_part_mode == MODE_LINE)) { + // Line mode or boundary match + + // Process the CR byte, if set aside. + if ((!matched) && (parser->cr_aside)) { + // Treat as part data, when there is not a match. + parser->handle_data(parser, (unsigned char *) &"\r", 1, /* not a line */ 0); + parser->cr_aside = 0; + } else { + // Treat as boundary, when there is a match. + parser->cr_aside = 0; + } + + // We know that we went to match a boundary because + // we saw a new line. Now we have to find that line and + // process it. It's either going to be in the current chunk, + // or in the first stored chunk. + if (bstr_builder_size(parser->boundary_pieces) > 0) { + int first = 1; + for (size_t i = 0, n = htp_list_size(parser->boundary_pieces->pieces); i < n; i++) { + bstr *b = htp_list_get(parser->boundary_pieces->pieces, i); + + if (first) { + first = 0; + + // Split the first chunk. + + if (!matched) { + // In line mode, we are OK with line endings. + parser->handle_data(parser, bstr_ptr(b), parser->boundary_candidate_pos, /* line */ 1); + } else { + // But if there was a match, the line ending belongs to the boundary. + unsigned char *dx = bstr_ptr(b); + size_t lx = parser->boundary_candidate_pos; + + // Remove LF or CRLF. + if ((lx > 0) && (dx[lx - 1] == LF)) { + lx--; + // Remove CR. + if ((lx > 0) && (dx[lx - 1] == CR)) { + lx--; + } + } + + parser->handle_data(parser, dx, lx, /* not a line */ 0); + } + + // The second part of the split chunks belongs to the boundary + // when matched, data otherwise. + if (!matched) { + parser->handle_data(parser, bstr_ptr(b) + parser->boundary_candidate_pos, + bstr_len(b) - parser->boundary_candidate_pos, /* not a line */ 0); + } + } else { + // Do not send data if there was a boundary match. The stored + // data belongs to the boundary. + if (!matched) { + parser->handle_data(parser, bstr_ptr(b), bstr_len(b), /* not a line */ 0); + } + } + } + + bstr_builder_clear(parser->boundary_pieces); + } + } else { + // Data mode and no match. + + // In data mode, we process the lone CR byte as data. + if (parser->cr_aside) { + parser->handle_data(parser, (const unsigned char *)&"\r", 1, /* not a line */ 0); + parser->cr_aside = 0; + } + + // We then process any pieces that we might have stored, also as data. + if (bstr_builder_size(parser->boundary_pieces) > 0) { + for (size_t i = 0, n = htp_list_size(parser->boundary_pieces->pieces); i < n; i++) { + bstr *b = htp_list_get(parser->boundary_pieces->pieces, i); + parser->handle_data(parser, bstr_ptr(b), bstr_len(b), /* not a line */ 0); + } + + bstr_builder_clear(parser->boundary_pieces); + } + } + + return HTP_OK; +} + +htp_status_t htp_mpartp_finalize(htp_mpartp_t *parser) { + if (parser->current_part != NULL) { + // Process buffered data, if any. + htp_martp_process_aside(parser, 0); + + // Finalize the last part. + if (htp_mpart_part_finalize_data(parser->current_part) != HTP_OK) return HTP_ERROR; + + // It is OK to end abruptly in the epilogue part, but not in any other. + if (parser->current_part->type != MULTIPART_PART_EPILOGUE) { + parser->multipart.flags |= HTP_MULTIPART_INCOMPLETE; + } + } + + bstr_builder_clear(parser->boundary_pieces); + + return HTP_OK; +} + +htp_status_t htp_mpartp_parse(htp_mpartp_t *parser, const void *_data, size_t len) { + unsigned char *data = (unsigned char *) _data; + + // The current position in the entire input buffer. + size_t pos = 0; + + // The position of the first unprocessed byte of data. We split the + // input buffer into smaller chunks, according to their purpose. Once + // an entire such smaller chunk is processed, we move to the next + // and update startpos. + size_t startpos = 0; + + // The position of the (possible) boundary. We investigate for possible + // boundaries whenever we encounter CRLF or just LF. If we don't find a + // boundary we need to go back, and this is what data_return_pos helps with. + size_t data_return_pos = 0; + + #if HTP_DEBUG + fprint_raw_data(stderr, "htp_mpartp_parse: data chunk", data, len); + #endif + + // While there's data in the input buffer. + + while (pos < len) { + +STATE_SWITCH: + #if HTP_DEBUG + fprintf(stderr, "htp_mpartp_parse: state %d pos %zd startpos %zd\n", parser->parser_state, pos, startpos); + #endif + + switch (parser->parser_state) { + + case STATE_INIT: + // Incomplete initialization. + return HTP_ERROR; + break; + + case STATE_DATA: // Handle part data. + + // While there's data in the input buffer. + + while (pos < len) { + // Check for a CRLF-terminated line. + if (data[pos] == CR) { + // We have a CR byte. + + // Is this CR the last byte in the input buffer? + if (pos + 1 == len) { + // We have CR as the last byte in input. We are going to process + // what we have in the buffer as data, except for the CR byte, + // which we're going to leave for later. If it happens that a + // CR is followed by a LF and then a boundary, the CR is going + // to be discarded. + pos++; // Advance over CR. + parser->cr_aside = 1; + } else { + // We have CR and at least one more byte in the buffer, so we + // are able to test for the LF byte too. + if (data[pos + 1] == LF) { + pos += 2; // Advance over CR and LF. + + parser->multipart.flags |= HTP_MULTIPART_CRLF_LINE; + + // Prepare to switch to boundary testing. + data_return_pos = pos; + parser->boundary_candidate_pos = pos - startpos; + parser->boundary_match_pos = 2; // After LF; position of the first dash. + parser->parser_state = STATE_BOUNDARY; + + goto STATE_SWITCH; + } else { + // This is not a new line; advance over the + // byte and clear the CR set-aside flag. + pos++; + parser->cr_aside = 0; + } + } + } else if (data[pos] == LF) { // Check for a LF-terminated line. + pos++; // Advance over LF. + + // Did we have a CR in the previous input chunk? + if (parser->cr_aside == 0) { + parser->multipart.flags |= HTP_MULTIPART_LF_LINE; + } else { + parser->multipart.flags |= HTP_MULTIPART_CRLF_LINE; + } + + // Prepare to switch to boundary testing. + data_return_pos = pos; + parser->boundary_candidate_pos = pos - startpos; + parser->boundary_match_pos = 2; // After LF; position of the first dash. + parser->parser_state = STATE_BOUNDARY; + + goto STATE_SWITCH; + } else { + // Take one byte from input + pos++; + + // Earlier we might have set aside a CR byte not knowing if the next + // byte is a LF. Now we know that it is not, and so we can release the CR. + if (parser->cr_aside) { + parser->handle_data(parser, (unsigned char *) &"\r", 1, /* not a line */ 0); + parser->cr_aside = 0; + } + } + } // while + + // No more data in the input buffer; process the data chunk. + parser->handle_data(parser, data + startpos, pos - startpos - parser->cr_aside, /* not a line */ 0); + + break; + + case STATE_BOUNDARY: // Handle a possible boundary. + while (pos < len) { + #ifdef HTP_DEBUG + fprintf(stderr, "boundary (len %zd pos %zd char %d) data char %d\n", parser->multipart.boundary_len, + parser->boundary_match_pos, parser->multipart.boundary[parser->boundary_match_pos], tolower(data[pos])); + #endif + + // Check if the bytes match. + if (!(data[pos] == parser->multipart.boundary[parser->boundary_match_pos])) { + // Boundary mismatch. + + // Process stored (buffered) data. + htp_martp_process_aside(parser, /* no match */ 0); + + // Return back where data parsing left off. + if (parser->current_part_mode == MODE_LINE) { + // In line mode, we process the line. + parser->handle_data(parser, data + startpos, data_return_pos - startpos, /* line */ 1); + startpos = data_return_pos; + } else { + // In data mode, we go back where we left off. + pos = data_return_pos; + } + + parser->parser_state = STATE_DATA; + + goto STATE_SWITCH; + } + + // Consume one matched boundary byte + pos++; + parser->boundary_match_pos++; + + // Have we seen all boundary bytes? + if (parser->boundary_match_pos == parser->multipart.boundary_len) { + // Boundary match! + + // Process stored (buffered) data. + htp_martp_process_aside(parser, /* boundary match */ 1); + + // Process data prior to the boundary in the current input buffer. + // Because we know this is the last chunk before boundary, we can + // remove the line endings. + size_t dlen = data_return_pos - startpos; + if ((dlen > 0) && (data[startpos + dlen - 1] == LF)) dlen--; + if ((dlen > 0) && (data[startpos + dlen - 1] == CR)) dlen--; + parser->handle_data(parser, data + startpos, dlen, /* line */ 1); + + // Keep track of how many boundaries we've seen. + parser->multipart.boundary_count++; + + if (parser->multipart.flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY) { + parser->multipart.flags |= HTP_MULTIPART_PART_AFTER_LAST_BOUNDARY; + } + + // Run boundary match. + parser->handle_boundary(parser); + + // We now need to check if this is the last boundary in the payload + parser->parser_state = STATE_BOUNDARY_IS_LAST2; + + goto STATE_SWITCH; + } + } // while + + // No more data in the input buffer; store (buffer) the unprocessed + // part for later, for after we find out if this is a boundary. + bstr_builder_append_mem(parser->boundary_pieces, data + startpos, len - startpos); + + break; + + case STATE_BOUNDARY_IS_LAST2: + // Examine the first byte after the last boundary character. If it is + // a dash, then we maybe processing the last boundary in the payload. If + // it is not, move to eat all bytes until the end of the line. + + if (data[pos] == '-') { + // Found one dash, now go to check the next position. + pos++; + parser->parser_state = STATE_BOUNDARY_IS_LAST1; + } else { + // This is not the last boundary. Change state but + // do not advance the position, allowing the next + // state to process the byte. + parser->parser_state = STATE_BOUNDARY_EAT_LWS; + } + break; + + case STATE_BOUNDARY_IS_LAST1: + // Examine the byte after the first dash; expected to be another dash. + // If not, eat all bytes until the end of the line. + + if (data[pos] == '-') { + // This is indeed the last boundary in the payload. + pos++; + parser->multipart.flags |= HTP_MULTIPART_SEEN_LAST_BOUNDARY; + parser->parser_state = STATE_BOUNDARY_EAT_LWS; + } else { + // The second character is not a dash, and so this is not + // the final boundary. Raise the flag for the first dash, + // and change state to consume the rest of the boundary line. + parser->multipart.flags |= HTP_MULTIPART_BBOUNDARY_NLWS_AFTER; + parser->parser_state = STATE_BOUNDARY_EAT_LWS; + } + break; + + case STATE_BOUNDARY_EAT_LWS: + if (data[pos] == CR) { + // CR byte, which could indicate a CRLF line ending. + pos++; + parser->parser_state = STATE_BOUNDARY_EAT_LWS_CR; + } else if (data[pos] == LF) { + // LF line ending; we're done with boundary processing; data bytes follow. + pos++; + startpos = pos; + parser->multipart.flags |= HTP_MULTIPART_LF_LINE; + parser->parser_state = STATE_DATA; + } else { + if (htp_is_lws(data[pos])) { + // Linear white space is allowed here. + parser->multipart.flags |= HTP_MULTIPART_BBOUNDARY_LWS_AFTER; + pos++; + } else { + // Unexpected byte; consume, but remain in the same state. + parser->multipart.flags |= HTP_MULTIPART_BBOUNDARY_NLWS_AFTER; + pos++; + } + } + break; + + case STATE_BOUNDARY_EAT_LWS_CR: + if (data[pos] == LF) { + // CRLF line ending; we're done with boundary processing; data bytes follow. + pos++; + startpos = pos; + parser->multipart.flags |= HTP_MULTIPART_CRLF_LINE; + parser->parser_state = STATE_DATA; + } else { + // Not a line ending; start again, but do not process this byte. + parser->multipart.flags |= HTP_MULTIPART_BBOUNDARY_NLWS_AFTER; + parser->parser_state = STATE_BOUNDARY_EAT_LWS; + } + break; + } // switch + } + + return HTP_OK; +} + +static void htp_mpartp_validate_boundary(bstr *boundary, uint64_t *flags) { + /* + + RFC 1341: + + The only mandatory parameter for the multipart Content-Type + is the boundary parameter, which consists of 1 to 70 + characters from a set of characters known to be very robust + through email gateways, and NOT ending with white space. + (If a boundary appears to end with white space, the white + space must be presumed to have been added by a gateway, and + should be deleted.) It is formally specified by the + following BNF: + + boundary := 0*69<bchars> bcharsnospace + + bchars := bcharsnospace / " " + + bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" / "_" + / "," / "-" / "." / "/" / ":" / "=" / "?" + */ + + /* + Chrome: Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryT4AfwQCOgIxNVwlD + Firefox: Content-Type: multipart/form-data; boundary=---------------------------21071316483088 + MSIE: Content-Type: multipart/form-data; boundary=---------------------------7dd13e11c0452 + Opera: Content-Type: multipart/form-data; boundary=----------2JL5oh7QWEDwyBllIRc7fh + Safari: Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryre6zL3b0BelnTY5S + */ + + unsigned char *data = bstr_ptr(boundary); + size_t len = bstr_len(boundary); + + // The RFC allows up to 70 characters. In real life, + // boundaries tend to be shorter. + if ((len == 0) || (len > 70)) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } + + // Check boundary characters. This check is stricter than the + // RFC, which seems to allow many separator characters. + size_t pos = 0; + while (pos < len) { + if (!(((data[pos] >= '0') && (data[pos] <= '9')) + || ((data[pos] >= 'a') && (data[pos] <= 'z')) + || ((data[pos] >= 'A') && (data[pos] <= 'Z')) + || (data[pos] == '-'))) { + + switch (data[pos]) { + case '\'': + case '(': + case ')': + case '+': + case '_': + case ',': + case '.': + case '/': + case ':': + case '=': + case '?': + // These characters are allowed by the RFC, but not common. + *flags |= HTP_MULTIPART_HBOUNDARY_UNUSUAL; + break; + + default: + // Invalid character. + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + break; + } + } + + pos++; + } +} + +static void htp_mpartp_validate_content_type(bstr *content_type, uint64_t *flags) { + unsigned char *data = bstr_ptr(content_type); + size_t len = bstr_len(content_type); + size_t counter = 0; + + while (len > 0) { + int i = bstr_util_mem_index_of_c_nocase(data, len, "boundary"); + if (i == -1) break; + + data = data + i; + len = len - i; + + // In order to work around the fact that WebKit actually uses + // the word "boundary" in their boundary, we also require one + // equals character the follow the words. + // "multipart/form-data; boundary=----WebKitFormBoundaryT4AfwQCOgIxNVwlD" + if (memchr(data, '=', len) == NULL) break; + + counter++; + + // Check for case variations. + for (size_t j = 0; j < 8; j++) { + if (!((*data >= 'a') && (*data <= 'z'))) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } + + data++; + len--; + } + } + + // How many boundaries have we seen? + if (counter > 1) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } +} + +htp_status_t htp_mpartp_find_boundary(bstr *content_type, bstr **boundary, uint64_t *flags) { + if ((content_type == NULL) || (boundary == NULL) || (flags == NULL)) return HTP_ERROR; + + // Our approach is to ignore the MIME type and instead just look for + // the boundary. This approach is more reliable in the face of various + // evasion techniques that focus on submitting invalid MIME types. + + // Reset flags. + *flags = 0; + + // Look for the boundary, case insensitive. + int i = bstr_index_of_c_nocase(content_type, "boundary"); + if (i == -1) return HTP_DECLINED; + + unsigned char *data = bstr_ptr(content_type) + i + 8; + size_t len = bstr_len(content_type) - i - 8; + + // Look for the boundary value. + size_t pos = 0; + while ((pos < len) && (data[pos] != '=')) { + if (htp_is_space(data[pos])) { + // It is unusual to see whitespace before the equals sign. + *flags |= HTP_MULTIPART_HBOUNDARY_UNUSUAL; + } else { + // But seeing a non-whitespace character may indicate evasion. + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } + + pos++; + } + + if (pos >= len) { + // No equals sign in the header. + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + return HTP_DECLINED; + } + + // Go over the '=' character. + pos++; + + // Ignore any whitespace after the equals sign. + while ((pos < len) && (htp_is_space(data[pos]))) { + if (htp_is_space(data[pos])) { + // It is unusual to see whitespace after + // the equals sign. + *flags |= HTP_MULTIPART_HBOUNDARY_UNUSUAL; + } + + pos++; + } + + if (pos >= len) { + // No value after the equals sign. + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + return HTP_DECLINED; + } + + if (data[pos] == '"') { + // Quoted boundary. + + // Possibly not very unusual, but let's see. + *flags |= HTP_MULTIPART_HBOUNDARY_UNUSUAL; + + pos++; // Over the double quote. + size_t startpos = pos; // Starting position of the boundary. + + // Look for the terminating double quote. + while ((pos < len) && (data[pos] != '"')) pos++; + + if (pos >= len) { + // Ran out of space without seeing + // the terminating double quote. + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + + // Include the starting double quote in the boundary. + startpos--; + } + + *boundary = bstr_dup_mem(data + startpos, pos - startpos); + if (*boundary == NULL) return HTP_ERROR; + + pos++; // Over the double quote. + } else { + // Boundary not quoted. + + size_t startpos = pos; + + // Find the end of the boundary. For the time being, we replicate + // the behavior of PHP 5.4.x. This may result with a boundary that's + // closer to what would be accepted in real life. Our subsequent + // checks of boundary characters will catch irregularities. + while ((pos < len) && (data[pos] != ',') && (data[pos] != ';') && (!htp_is_space(data[pos]))) pos++; + + *boundary = bstr_dup_mem(data + startpos, pos - startpos); + if (*boundary == NULL) return HTP_ERROR; + } + + // Check for a zero-length boundary. + if (bstr_len(*boundary) == 0) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + bstr_free(*boundary); + *boundary = NULL; + return HTP_DECLINED; + } + + // Allow only whitespace characters after the boundary. + int seen_space = 0, seen_non_space = 0; + + while (pos < len) { + if (!htp_is_space(data[pos])) { + seen_non_space = 1; + } else { + seen_space = 1; + } + + pos++; + } + + // Raise INVALID if we see any non-space characters, + // but raise UNUSUAL if we see _only_ space characters. + if (seen_non_space) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } else if (seen_space) { + *flags |= HTP_MULTIPART_HBOUNDARY_UNUSUAL; + } + + #ifdef HTP_DEBUG + fprint_bstr(stderr, "Multipart boundary", *boundary); + #endif + + // Validate boundary characters. + htp_mpartp_validate_boundary(*boundary, flags); + + // Correlate with the MIME type. This might be a tad too + // sensitive because it may catch non-browser access with sloppy + // implementations, but let's go with it for now. + if (bstr_begins_with_c(content_type, "multipart/form-data;") == 0) { + *flags |= HTP_MULTIPART_HBOUNDARY_INVALID; + } + + htp_mpartp_validate_content_type(content_type, flags); + + return HTP_OK; +} diff --git a/htp/htp_multipart.h b/htp/htp_multipart.h new file mode 100644 index 0000000..614ef63 --- /dev/null +++ b/htp/htp_multipart.h @@ -0,0 +1,345 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_MULTIPART_H +#define _HTP_MULTIPART_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bstr.h" +#include "htp.h" +#include "htp_table.h" + + +// Constants and enums. + +/** + * Seen a LF line in the payload. LF lines are not allowed, but + * some clients do use them and some backends do accept them. Mixing + * LF and CRLF lines within some payload might be unusual. + */ +#define HTP_MULTIPART_LF_LINE 0x0001 + +/** Seen a CRLF line in the payload. This is normal and expected. */ +#define HTP_MULTIPART_CRLF_LINE 0x0002 + +/** Seen LWS after a boundary instance in the body. Unusual. */ +#define HTP_MULTIPART_BBOUNDARY_LWS_AFTER 0x0004 + +/** Seen non-LWS content after a boundary instance in the body. Highly unusual. */ +#define HTP_MULTIPART_BBOUNDARY_NLWS_AFTER 0x0008 + +/** + * Payload has a preamble part. Might not be that unusual. + */ +#define HTP_MULTIPART_HAS_PREAMBLE 0x0010 + +/** + * Payload has an epilogue part. Unusual. + */ +#define HTP_MULTIPART_HAS_EPILOGUE 0x0020 + +/** + * The last boundary was seen in the payload. Absence of the last boundary + * may not break parsing with some (most?) backends, but it means that the payload + * is not well formed. Can occur if the client gives up, or if the connection is + * interrupted. Incomplete payloads should be blocked whenever possible. + */ +#define HTP_MULTIPART_SEEN_LAST_BOUNDARY 0x0040 + +/** + * There was a part after the last boundary. This is highly irregular + * and indicative of evasion. + */ +#define HTP_MULTIPART_PART_AFTER_LAST_BOUNDARY 0x0080 + +/** + * The payloads ends abruptly, without proper termination. Can occur if the client gives up, + * or if the connection is interrupted. When this flag is raised, HTP_MULTIPART_PART_INCOMPLETE + * will also be raised for the part that was only partially processed. (But the opposite may not + * always be the case -- there are other ways in which a part can be left incomplete.) + */ +#define HTP_MULTIPART_INCOMPLETE 0x0100 + +/** The boundary in the Content-Type header is invalid. */ +#define HTP_MULTIPART_HBOUNDARY_INVALID 0x0200 + +/** + * The boundary in the Content-Type header is unusual. This may mean that evasion + * is attempted, but it could also mean that we have encountered a client that does + * not do things in the way it should. + */ +#define HTP_MULTIPART_HBOUNDARY_UNUSUAL 0x0400 + +/** + * The boundary in the Content-Type header is quoted. This is very unusual, + * and may be indicative of an evasion attempt. + */ +#define HTP_MULTIPART_HBOUNDARY_QUOTED 0x0800 + +/** Header folding was used in part headers. Very unusual. */ +#define HTP_MULTIPART_PART_HEADER_FOLDING 0x1000 + +/** + * A part of unknown type was encountered, which probably means that the part is lacking + * a Content-Disposition header, or that the header is invalid. Highly unusual. + */ +#define HTP_MULTIPART_PART_UNKNOWN 0x2000 + +/** There was a repeated part header, possibly in an attempt to confuse the parser. Very unusual. */ +#define HTP_MULTIPART_PART_HEADER_REPEATED 0x4000 + +/** Unknown part header encountered. */ +#define HTP_MULTIPART_PART_HEADER_UNKNOWN 0x8000 + +/** Invalid part header encountered. */ +#define HTP_MULTIPART_PART_HEADER_INVALID 0x10000 + +/** Part type specified in the C-D header is neither MULTIPART_PART_TEXT nor MULTIPART_PART_FILE. */ +#define HTP_MULTIPART_CD_TYPE_INVALID 0x20000 + +/** Content-Disposition part header with multiple parameters with the same name. */ +#define HTP_MULTIPART_CD_PARAM_REPEATED 0x40000 + +/** Unknown Content-Disposition parameter. */ +#define HTP_MULTIPART_CD_PARAM_UNKNOWN 0x80000 + +/** Invalid Content-Disposition syntax. */ +#define HTP_MULTIPART_CD_SYNTAX_INVALID 0x100000 + +/** + * There is an abruptly terminated part. This can happen when the payload itself is abruptly + * terminated (in which case HTP_MULTIPART_INCOMPLETE) will be raised. However, it can also + * happen when a boundary is seen before any part data. + */ +#define HTP_MULTIPART_PART_INCOMPLETE 0x200000 + +/** A NUL byte was seen in a part header area. */ +#define HTP_MULTIPART_NUL_BYTE 0x400000 + +/** A collection of flags that all indicate an invalid C-D header. */ +#define HTP_MULTIPART_CD_INVALID ( \ + HTP_MULTIPART_CD_TYPE_INVALID | \ + HTP_MULTIPART_CD_PARAM_REPEATED | \ + HTP_MULTIPART_CD_PARAM_UNKNOWN | \ + HTP_MULTIPART_CD_SYNTAX_INVALID ) + +/** A collection of flags that all indicate an invalid part. */ +#define HTP_MULTIPART_PART_INVALID ( \ + HTP_MULTIPART_CD_INVALID | \ + HTP_MULTIPART_NUL_BYTE | \ + HTP_MULTIPART_PART_UNKNOWN | \ + HTP_MULTIPART_PART_HEADER_REPEATED | \ + HTP_MULTIPART_PART_INCOMPLETE | \ + HTP_MULTIPART_PART_HEADER_UNKNOWN | \ + HTP_MULTIPART_PART_HEADER_INVALID ) + +/** A collection of flags that all indicate an invalid Multipart payload. */ +#define HTP_MULTIPART_INVALID ( \ + HTP_MULTIPART_PART_INVALID | \ + HTP_MULTIPART_PART_AFTER_LAST_BOUNDARY | \ + HTP_MULTIPART_INCOMPLETE | \ + HTP_MULTIPART_HBOUNDARY_INVALID ) + +/** A collection of flags that all indicate an unusual Multipart payload. */ +#define HTP_MULTIPART_UNUSUAL ( \ + HTP_MULTIPART_INVALID | \ + HTP_MULTIPART_PART_HEADER_FOLDING | \ + HTP_MULTIPART_BBOUNDARY_NLWS_AFTER | \ + HTP_MULTIPART_HAS_EPILOGUE | \ + HTP_MULTIPART_HBOUNDARY_UNUSUAL \ + HTP_MULTIPART_HBOUNDARY_QUOTED ) + +/** A collection of flags that all indicate an unusual Multipart payload, with a low sensitivity to irregularities. */ +#define HTP_MULTIPART_UNUSUAL_PARANOID ( \ + HTP_MULTIPART_UNUSUAL | \ + HTP_MULTIPART_LF_LINE | \ + HTP_MULTIPART_BBOUNDARY_LWS_AFTER | \ + HTP_MULTIPART_HAS_PREAMBLE ) + +#define HTP_MULTIPART_MIME_TYPE "multipart/form-data" + +enum htp_multipart_type_t { + + /** Unknown part. */ + MULTIPART_PART_UNKNOWN = 0, + + /** Text (parameter) part. */ + MULTIPART_PART_TEXT = 1, + + /** File part. */ + MULTIPART_PART_FILE = 2, + + /** Free-text part before the first boundary. */ + MULTIPART_PART_PREAMBLE = 3, + + /** Free-text part after the last boundary. */ + MULTIPART_PART_EPILOGUE = 4 +}; + + +// Structures + +/** + * Holds multipart parser configuration and state. Private. + */ +typedef struct htp_mpartp_t htp_mpartp_t; + +/** + * Holds information related to a multipart body. + */ +typedef struct htp_multipart_t { + /** Multipart boundary. */ + char *boundary; + + /** Boundary length. */ + size_t boundary_len; + + /** How many boundaries were there? */ + int boundary_count; + + /** List of parts, in the order in which they appeared in the body. */ + htp_list_t *parts; + + /** Parsing flags. */ + uint64_t flags; +} htp_multipart_t; + +/** + * Holds information related to a part. + */ +typedef struct htp_multipart_part_t { + /** Pointer to the parser. */ + htp_mpartp_t *parser; + + /** Part type; see the MULTIPART_PART_* constants. */ + enum htp_multipart_type_t type; + + /** Raw part length (i.e., headers and data). */ + size_t len; + + /** Part name, from the Content-Disposition header. Can be NULL. */ + bstr *name; + + /** + * Part value; the contents depends on the type of the part: + * 1) NULL for files; 2) contains complete part contents for + * preamble and epilogue parts (they have no headers), and + * 3) data only (headers excluded) for text and unknown parts. + */ + bstr *value; + + /** Part content type, from the Content-Type header. Can be NULL. */ + bstr *content_type; + + /** Part headers (htp_header_t instances), using header name as the key. */ + htp_table_t *headers; + + /** File data, available only for MULTIPART_PART_FILE parts. */ + htp_file_t *file; +} htp_multipart_part_t; + + +// Functions + +/** + * Creates a new multipart/form-data parser. On a successful invocation, + * the ownership of the boundary parameter is transferred to the parser. + * + * @param[in] cfg + * @param[in] boundary + * @param[in] flags + * @return New parser instance, or NULL on memory allocation failure. + */ +htp_mpartp_t *htp_mpartp_create(htp_cfg_t *cfg, bstr *boundary, uint64_t flags); + +/** + * Looks for boundary in the supplied Content-Type request header. The extracted + * boundary will be allocated on the heap. + * + * @param[in] content_type + * @param[out] boundary + * @param[out] multipart_flags Multipart flags, which are not compatible from general LibHTP flags. + * @return HTP_OK on success (boundary found), HTP_DECLINED if boundary was not found, + * and HTP_ERROR on failure. Flags may be set on HTP_OK and HTP_DECLINED. For + * example, if a boundary could not be extracted but there is indication that + * one is present, HTP_MULTIPART_HBOUNDARY_INVALID will be set. + */ +htp_status_t htp_mpartp_find_boundary(bstr *content_type, bstr **boundary, uint64_t *multipart_flags); + +/** + * Returns the multipart structure created by the parser. + * + * @param[in] parser + * @return The main multipart structure. + */ +htp_multipart_t *htp_mpartp_get_multipart(htp_mpartp_t *parser); + +/** + * Destroys the provided parser. + * + * @param[in] parser + */ +void htp_mpartp_destroy(htp_mpartp_t *parser); + +/** + * Finalize parsing. + * + * @param[in] parser + * @returns HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_mpartp_finalize(htp_mpartp_t *parser); + +/** + * Parses a chunk of multipart/form-data data. This function should be called + * as many times as necessary until all data has been consumed. + * + * @param[in] parser + * @param[in] data + * @param[in] len + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_mpartp_parse(htp_mpartp_t *parser, const void *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_MULTIPART_H */ diff --git a/htp/htp_multipart_private.h b/htp/htp_multipart_private.h new file mode 100644 index 0000000..5b8d228 --- /dev/null +++ b/htp/htp_multipart_private.h @@ -0,0 +1,203 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_MULTIPART_PRIVATE_H +#define _HTP_MULTIPART_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp_multipart.h" + +#define CD_PARAM_OTHER 0 +#define CD_PARAM_NAME 1 +#define CD_PARAM_FILENAME 2 + +#define DEFAULT_FILE_EXTRACT_LIMIT 16 + +enum htp_part_mode_t { + /** When in line mode, the parser is handling part headers. */ + MODE_LINE = 0, + + /** When in data mode, the parser is consuming part data. */ + MODE_DATA = 1 +}; + +enum htp_multipart_state_t { + /** Initial state, after the parser has been created but before the boundary initialized. */ + STATE_INIT = 0, + + /** Processing data, waiting for a new line (which might indicate a new boundary). */ + STATE_DATA = 1, + + /** Testing a potential boundary. */ + STATE_BOUNDARY = 2, + + /** Checking the first byte after a boundary. */ + STATE_BOUNDARY_IS_LAST1 = 3, + + /** Checking the second byte after a boundary. */ + STATE_BOUNDARY_IS_LAST2 = 4, + + /** Consuming linear whitespace after a boundary. */ + STATE_BOUNDARY_EAT_LWS = 5, + + /** Used after a CR byte is detected in STATE_BOUNDARY_EAT_LWS. */ + STATE_BOUNDARY_EAT_LWS_CR = 6 +}; + +struct htp_mpartp_t { + htp_multipart_t multipart; + + htp_cfg_t *cfg; + + int extract_files; + + int extract_limit; + + char *extract_dir; + + int file_count; + + // Parsing callbacks + + int (*handle_data)(htp_mpartp_t *mpartp, const unsigned char *data, + size_t len, int line_end); + int (*handle_boundary)(htp_mpartp_t *mpartp); + + // Internal parsing fields; move into a private structure + + /** + * Parser state; one of MULTIPART_STATE_* constants. + */ + enum htp_multipart_state_t parser_state; + + /** + * Keeps track of the current position in the boundary matching progress. + * When this field reaches boundary_len, we have a boundary match. + */ + size_t boundary_match_pos; + + /** + * Pointer to the part that is currently being processed. + */ + htp_multipart_part_t *current_part; + + /** + * This parser consists of two layers: the outer layer is charged with + * finding parts, and the internal layer handles part data. There is an + * interesting interaction between the two parsers. Because the + * outer layer is seeing every line (it has to, in order to test for + * boundaries), it also effectively also splits input into lines. The + * inner parser deals with two areas: first is the headers, which are + * line based, followed by binary data. When parsing headers, the inner + * parser can reuse the lines identified by the outer parser. In this + * variable we keep the current parsing mode of the part, which helps + * us process input data more efficiently. The possible values are + * MULTIPART_MODE_LINE and MULTIPART_MODE_DATA. + */ + enum htp_part_mode_t current_part_mode; + + /** + * Used for buffering when a potential boundary is fragmented + * across many input data buffers. On a match, the data stored here is + * discarded. When there is no match, the buffer is processed as data + * (belonging to the currently active part). + */ + bstr_builder_t *boundary_pieces; + + bstr_builder_t *part_header_pieces; + + bstr *pending_header_line; + + /** + * Stores text part pieces until the entire part is seen, at which + * point the pieces are assembled into a single buffer, and the + * builder cleared. + */ + bstr_builder_t *part_data_pieces; + + /** + * The offset of the current boundary candidate, relative to the most + * recent data chunk (first unprocessed chunk of data). + */ + size_t boundary_candidate_pos; + + /** + * When we encounter a CR as the last byte in a buffer, we don't know + * if the byte is part of a CRLF combination. If it is, then the CR + * might be a part of a boundary. But if it is not, it's current + * part's data. Because we know how to handle everything before the + * CR, we do, and we use this flag to indicate that a CR byte is + * effectively being buffered. This is probably a case of premature + * optimization, but I am going to leave it in for now. + */ + int cr_aside; + + /** + * When set, indicates that this parser no longer owns names and + * values of MULTIPART_PART_TEXT parts. It is used to avoid data + * duplication when the parser is used by LibHTP internally. + */ + int gave_up_data; +}; + +htp_status_t htp_mpartp_run_request_file_data_hook(htp_multipart_part_t *part, const unsigned char *data, size_t len); + +htp_status_t htp_mpart_part_process_headers(htp_multipart_part_t *part); + +htp_status_t htp_mpartp_parse_header(htp_multipart_part_t *part, const unsigned char *data, size_t len); + +htp_status_t htp_mpart_part_handle_data(htp_multipart_part_t *part, const unsigned char *data, size_t len, int is_line); + +int htp_mpartp_is_boundary_character(int c); + +htp_multipart_part_t *htp_mpart_part_create(htp_mpartp_t *parser); + +htp_status_t htp_mpart_part_finalize_data(htp_multipart_part_t *part); + +void htp_mpart_part_destroy(htp_multipart_part_t *part, int gave_up_data); + +htp_status_t htp_mpart_part_parse_c_d(htp_multipart_part_t *part); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_MULTIPART_PRIVATE_H */ diff --git a/htp/htp_parsers.c b/htp/htp_parsers.c new file mode 100644 index 0000000..3f41abb --- /dev/null +++ b/htp/htp_parsers.c @@ -0,0 +1,214 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Determines protocol number from a textual representation (i.e., "HTTP/1.1"). This + * function will only understand a properly formatted protocol information. It does + * not try to be flexible. + * + * @param[in] protocol + * @return Protocol version or PROTOCOL_UNKNOWN. + */ +int htp_parse_protocol(bstr *protocol) { + if (protocol == NULL) return HTP_PROTOCOL_INVALID; + + // TODO This function uses a very strict approach to parsing, whereas + // browsers will typically be more flexible, allowing whitespace + // before and after the forward slash, as well as allowing leading + // zeroes in the numbers. We should be able to parse such malformed + // content correctly (but emit a warning). + if (bstr_len(protocol) == 8) { + unsigned char *ptr = bstr_ptr(protocol); + if ((ptr[0] == 'H') && (ptr[1] == 'T') && (ptr[2] == 'T') && (ptr[3] == 'P') + && (ptr[4] == '/') && (ptr[6] == '.')) { + // Check the version numbers + if (ptr[5] == '0') { + if (ptr[7] == '9') { + return HTP_PROTOCOL_0_9; + } + } else if (ptr[5] == '1') { + if (ptr[7] == '0') { + return HTP_PROTOCOL_1_0; + } else if (ptr[7] == '1') { + return HTP_PROTOCOL_1_1; + } + } + } + } + + return HTP_PROTOCOL_INVALID; +} + +/** + * Determines the numerical value of a response status given as a string. + * + * @param[in] status + * @return Status code on success, or HTP_STATUS_INVALID on error. + */ +int htp_parse_status(bstr *status) { + int64_t r = htp_parse_positive_integer_whitespace((unsigned char *) bstr_ptr(status), bstr_len(status), 10); + if (r >= HTP_VALID_STATUS_MIN && r <= HTP_VALID_STATUS_MAX) { + return (int)r; + } else { + return HTP_STATUS_INVALID; + } +} + +/** + * Parses Digest Authorization request header. + * + * @param[in] connp + * @param[in] auth_header + */ +int htp_parse_authorization_digest(htp_connp_t *connp, htp_header_t *auth_header) { + // Extract the username + int i = bstr_index_of_c(auth_header->value, "username="); + if (i == -1) return HTP_DECLINED; + + unsigned char *data = bstr_ptr(auth_header->value); + size_t len = bstr_len(auth_header->value); + size_t pos = i + 9; + + // Ignore whitespace + while ((pos < len) && (isspace((int) data[pos]))) pos++; + if (pos == len) return HTP_DECLINED; + + if (data[pos] != '"') return HTP_DECLINED; + + return htp_extract_quoted_string_as_bstr(data + pos, len - pos, &(connp->in_tx->request_auth_username), NULL); +} + +/** + * Parses Basic Authorization request header. + * + * @param[in] connp + * @param[in] auth_header + */ +int htp_parse_authorization_basic(htp_connp_t *connp, htp_header_t *auth_header) { + unsigned char *data = bstr_ptr(auth_header->value); + size_t len = bstr_len(auth_header->value); + size_t pos = 5; + + // Ignore whitespace + while ((pos < len) && (isspace((int) data[pos]))) pos++; + if (pos == len) return HTP_DECLINED; + + // Decode base64-encoded data + bstr *decoded = htp_base64_decode_mem(data + pos, len - pos); + if (decoded == NULL) return HTP_ERROR; + + // Now extract the username and password + int i = bstr_index_of_c(decoded, ":"); + if (i == -1) { + bstr_free(decoded); + return HTP_DECLINED; + } + + connp->in_tx->request_auth_username = bstr_dup_ex(decoded, 0, i); + if (connp->in_tx->request_auth_username == NULL) { + bstr_free(decoded); + return HTP_ERROR; + } + + connp->in_tx->request_auth_password = bstr_dup_ex(decoded, i + 1, bstr_len(decoded) - i - 1); + if (connp->in_tx->request_auth_password == NULL) { + bstr_free(decoded); + bstr_free(connp->in_tx->request_auth_username); + return HTP_ERROR; + } + + bstr_free(decoded); + + return HTP_OK; +} + +/** + * Parses Bearer Authorization request header. + * + * @param[in] connp + * @param[in] auth_header + */ +int htp_parse_authorization_bearer(htp_connp_t *connp, htp_header_t *auth_header) { + unsigned char *data = bstr_ptr(auth_header->value); + size_t len = bstr_len(auth_header->value); + size_t pos = 6; + + // Ignore whitespace + while ((pos < len) && (isspace((int) data[pos]))) pos++; + if (pos == len) return HTP_DECLINED; + + // There is nothing much else to check with Bearer auth so we just return + return HTP_OK; +} +/** + * Parses Authorization request header. + * + * @param[in] connp + */ +int htp_parse_authorization(htp_connp_t *connp) { + htp_header_t *auth_header = htp_table_get_c(connp->in_tx->request_headers, "authorization"); + if (auth_header == NULL) { + connp->in_tx->request_auth_type = HTP_AUTH_NONE; + return HTP_OK; + } + + // TODO Need a flag to raise when failing to parse authentication headers. + + if (bstr_begins_with_c_nocase(auth_header->value, "basic")) { + // Basic authentication + connp->in_tx->request_auth_type = HTP_AUTH_BASIC; + return htp_parse_authorization_basic(connp, auth_header); + } else if (bstr_begins_with_c_nocase(auth_header->value, "digest")) { + // Digest authentication + connp->in_tx->request_auth_type = HTP_AUTH_DIGEST; + return htp_parse_authorization_digest(connp, auth_header); + } else if (bstr_begins_with_c_nocase(auth_header->value, "bearer")) { + // OAuth Bearer authentication + connp->in_tx->request_auth_type = HTP_AUTH_BEARER; + return htp_parse_authorization_bearer(connp, auth_header); + } else { + // Unrecognized authentication method + connp->in_tx->request_auth_type = HTP_AUTH_UNRECOGNIZED; + } + + return HTP_OK; +} diff --git a/htp/htp_php.c b/htp/htp_php.c new file mode 100644 index 0000000..582d5b3 --- /dev/null +++ b/htp/htp_php.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * This is a proof-of-concept processor that processes parameter names in + * a way _similar_ to PHP. Whitespace at the beginning is removed, and the + * remaining whitespace characters are converted to underscores. Proper + * research of PHP's behavior is needed before we can claim to be emulating it. + * + * @param[in,out] p + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_php_parameter_processor(htp_param_t *p) { + if (p == NULL) return HTP_ERROR; + + // Name transformation + + bstr *new_name = NULL; + + // Ignore whitespace characters at the beginning of parameter name. + + unsigned char *data = bstr_ptr(p->name); + size_t len = bstr_len(p->name); + size_t pos = 0; + + // Advance over any whitespace characters at the beginning of the name. + while ((pos < len) && (isspace(data[pos]))) pos++; + + // Have we seen any whitespace? + if (pos > 0) { + // Make a copy of the name, starting with + // the first non-whitespace character. + new_name = bstr_dup_mem(data + pos, len - pos); + if (new_name == NULL) return HTP_ERROR; + } + + // Replace remaining whitespace characters with underscores. + + size_t offset = pos; + pos = 0; + + // Advance to the end of name or to the first whitespace character. + while ((offset + pos < len)&&(!isspace(data[pos]))) pos++; + + // Are we at the end of the name? + if (offset + pos < len) { + // Seen whitespace within the string. + + // Make a copy of the name if needed (which would be the case + // with a parameter that does not have any whitespace in front). + if (new_name == NULL) { + new_name = bstr_dup(p->name); + if (new_name == NULL) return HTP_ERROR; + } + + // Change the pointers to the new name and ditch the offset. + data = bstr_ptr(new_name); + len = bstr_len(new_name); + + // Replace any whitespace characters in the copy with underscores. + while (pos < len) { + if (isspace(data[pos])) { + data[pos] = '_'; + } + + pos++; + } + } + + // If we made any changes, free the old parameter name and put the new one in. + if (new_name != NULL) { + bstr_free(p->name); + p->name = new_name; + } + + return HTP_OK; +} diff --git a/htp/htp_private.h b/htp/htp_private.h new file mode 100644 index 0000000..9bcf19d --- /dev/null +++ b/htp/htp_private.h @@ -0,0 +1,269 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_PRIVATE_H +#define _HTP_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__cplusplus) && !defined(__STDC_FORMAT_MACROS) +/* C99 requires that inttypes.h only exposes PRI* macros + * for C++ implementations if this is defined: */ +#define __STDC_FORMAT_MACROS +#endif + +#include <ctype.h> +#include <errno.h> +#include <iconv.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdint.h> + +#include "htp_config_auto_gen.h" +#include "htp.h" +#include "htp_config_private.h" +#include "htp_connection_parser_private.h" +#include "htp_connection_private.h" +#include "htp_list_private.h" +#include "htp_multipart_private.h" +#include "htp_table_private.h" + +#ifndef CR +#define CR '\r' +#endif + +#ifndef LF +#define LF '\n' +#endif + +// 1048576 is 1 Mbyte +#define HTP_LZMA_MEMLIMIT 1048576 +//deflate max ratio is about 1000 +#define HTP_COMPRESSION_BOMB_RATIO 2048 +#define HTP_COMPRESSION_BOMB_LIMIT 1048576 +// 0.1 second +#define HTP_COMPRESSION_TIME_LIMIT_USEC 100000 +// test time for compression every 256 callbacks +#define HTP_COMPRESSION_TIME_FREQ_TEST 256 + +#define HTP_FIELD_LIMIT_HARD 18000 +#define HTP_FIELD_LIMIT_SOFT 9000 + +#define HTP_VALID_STATUS_MIN 100 +#define HTP_VALID_STATUS_MAX 999 + +// Parser states, in the order in which they are +// used as a single transaction is processed. + +htp_status_t htp_connp_REQ_IDLE(htp_connp_t *connp); +htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp); +htp_status_t htp_connp_REQ_LINE_complete(htp_connp_t *connp); +htp_status_t htp_connp_REQ_PROTOCOL(htp_connp_t *connp); +htp_status_t htp_connp_REQ_HEADERS(htp_connp_t *connp); +htp_status_t htp_connp_REQ_CONNECT_CHECK(htp_connp_t *connp); +htp_status_t htp_connp_REQ_CONNECT_WAIT_RESPONSE(htp_connp_t *connp); +htp_status_t htp_connp_REQ_CONNECT_PROBE_DATA(htp_connp_t *connp); +htp_status_t htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp); +htp_status_t htp_connp_REQ_BODY_IDENTITY(htp_connp_t *connp); +htp_status_t htp_connp_REQ_BODY_CHUNKED_LENGTH(htp_connp_t *connp); +htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA(htp_connp_t *connp); +htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA_END(htp_connp_t *connp); +htp_status_t htp_connp_REQ_FINALIZE(htp_connp_t *connp); +htp_status_t htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9(htp_connp_t *connp); + +htp_status_t htp_connp_RES_IDLE(htp_connp_t *connp); +htp_status_t htp_connp_RES_LINE(htp_connp_t *connp); +htp_status_t htp_connp_RES_HEADERS(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_DETERMINE(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_IDENTITY_CL_KNOWN(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_CHUNKED_LENGTH(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_CHUNKED_DATA(htp_connp_t *connp); +htp_status_t htp_connp_RES_BODY_CHUNKED_DATA_END(htp_connp_t *connp); +htp_status_t htp_connp_RES_FINALIZE(htp_connp_t *connp); + +// Parsing functions + +htp_status_t htp_parse_request_line_generic(htp_connp_t *connp); +htp_status_t htp_parse_request_line_generic_ex(htp_connp_t *connp, int nul_terminates); +htp_status_t htp_parse_request_header_generic(htp_connp_t *connp, htp_header_t *h, unsigned char *data, size_t len); +htp_status_t htp_process_request_header_generic(htp_connp_t *, unsigned char *data, size_t len); + +htp_status_t htp_parse_request_line_apache_2_2(htp_connp_t *connp); +htp_status_t htp_process_request_header_apache_2_2(htp_connp_t *, unsigned char *data, size_t len); + +htp_status_t htp_parse_response_line_generic(htp_connp_t *connp); +htp_status_t htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, unsigned char *data, size_t len); +htp_status_t htp_process_response_header_generic(htp_connp_t *connp, unsigned char *data, size_t len); + + +// Private transaction functions + +htp_status_t htp_tx_state_response_complete_ex(htp_tx_t *tx, int hybrid_mode); + + +// Utility functions + +int htp_convert_method_to_number(bstr *); +int htp_is_lws(int c); +int htp_is_separator(int c); +int htp_is_text(int c); +int htp_is_token(int c); +int htp_chomp(unsigned char *data, size_t *len); +int htp_is_space(int c); + +int htp_parse_protocol(bstr *protocol); + +int htp_is_line_empty(unsigned char *data, size_t len); +int htp_is_line_whitespace(unsigned char *data, size_t len); + +int htp_connp_is_line_folded(unsigned char *data, size_t len); +int htp_is_folding_char(int c); +int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len, int next_no_lf); +int htp_connp_is_line_ignorable(htp_connp_t *connp, unsigned char *data, size_t len); + +int htp_parse_uri(bstr *input, htp_uri_t **uri); +htp_status_t htp_parse_hostport(bstr *authority, bstr **hostname, bstr **port, int *port_number, int *invalid); +htp_status_t htp_parse_header_hostport(bstr *authority, bstr **hostname, bstr **port, int *port_number, uint64_t *flags); +int htp_validate_hostname(bstr *hostname); +int htp_parse_uri_hostport(htp_connp_t *connp, bstr *input, htp_uri_t *uri); +int htp_normalize_parsed_uri(htp_tx_t *tx, htp_uri_t *parsed_uri_incomplete, htp_uri_t *parsed_uri); +bstr *htp_normalize_hostname_inplace(bstr *input); + +int htp_decode_path_inplace(htp_tx_t *tx, bstr *path); + + int htp_prenormalize_uri_path_inplace(bstr *s, int *flags, int case_insensitive, int backslash, int decode_separators, int remove_consecutive); +void htp_normalize_uri_path_inplace(bstr *s); + +void htp_utf8_decode_path_inplace(htp_cfg_t *cfg, htp_tx_t *tx, bstr *path); +void htp_utf8_validate_path(htp_tx_t *tx, bstr *path); + +int64_t htp_parse_content_length(bstr *b, htp_connp_t *connp); +int64_t htp_parse_chunked_length(unsigned char *data, size_t len, int *extension); +int64_t htp_parse_positive_integer_whitespace(unsigned char *data, size_t len, int base); +int htp_parse_status(bstr *status); +int htp_parse_authorization_digest(htp_connp_t *connp, htp_header_t *auth_header); +int htp_parse_authorization_basic(htp_connp_t *connp, htp_header_t *auth_header); +int htp_parse_authorization_bearer(htp_connp_t *connp, htp_header_t *auth_header); + +void htp_print_log(FILE *stream, htp_log_t *log); + +void fprint_bstr(FILE *stream, const char *name, bstr *b); +void fprint_raw_data(FILE *stream, const char *name, const void *data, size_t len); +void fprint_raw_data_ex(FILE *stream, const char *name, const void *data, size_t offset, size_t len); + +char *htp_connp_in_state_as_string(htp_connp_t *connp); +char *htp_connp_out_state_as_string(htp_connp_t *connp); +char *htp_tx_request_progress_as_string(htp_tx_t *tx); +char *htp_tx_response_progress_as_string(htp_tx_t *tx); + +bstr *htp_unparse_uri_noencode(htp_uri_t *uri); + +int htp_treat_response_line_as_body(const uint8_t *data, size_t len); + +htp_status_t htp_req_run_hook_body_data(htp_connp_t *connp, htp_tx_data_t *d); +htp_status_t htp_res_run_hook_body_data(htp_connp_t *connp, htp_tx_data_t *d); + +htp_status_t htp_ch_urlencoded_callback_request_body_data(htp_tx_data_t *d); +htp_status_t htp_ch_urlencoded_callback_request_headers(htp_tx_t *tx); +htp_status_t htp_ch_urlencoded_callback_request_line(htp_tx_t *tx); +htp_status_t htp_ch_multipart_callback_request_body_data(htp_tx_data_t *d); +htp_status_t htp_ch_multipart_callback_request_headers(htp_tx_t *tx); + +htp_status_t htp_php_parameter_processor(htp_param_t *p); + +int htp_transcode_params(htp_connp_t *connp, htp_table_t **params, int destroy_old); +int htp_transcode_bstr(iconv_t cd, bstr *input, bstr **output); + +int htp_parse_single_cookie_v0(htp_connp_t *connp, unsigned char *data, size_t len); +int htp_parse_cookies_v0(htp_connp_t *connp); +int htp_parse_authorization(htp_connp_t *connp); + +htp_status_t htp_extract_quoted_string_as_bstr(unsigned char *data, size_t len, bstr **out, size_t *endoffset); + +htp_header_t *htp_connp_header_parse(htp_connp_t *, unsigned char *, size_t); + +htp_status_t htp_parse_ct_header(bstr *header, bstr **ct); + +htp_status_t htp_connp_req_receiver_finalize_clear(htp_connp_t *connp); +htp_status_t htp_connp_res_receiver_finalize_clear(htp_connp_t *connp); + +htp_status_t htp_tx_finalize(htp_tx_t *tx); + +int htp_tx_is_complete(htp_tx_t *tx); + +htp_status_t htp_tx_state_request_complete_partial(htp_tx_t *tx); + +void htp_connp_tx_remove(htp_connp_t *connp, htp_tx_t *tx); + +void htp_tx_destroy_incomplete(htp_tx_t *tx); + +htp_status_t htp_tx_req_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len); +htp_status_t htp_tx_res_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len); + +htp_status_t htp_tx_urldecode_uri_inplace(htp_tx_t *tx, bstr *input); +htp_status_t htp_tx_urldecode_params_inplace(htp_tx_t *tx, bstr *input); + +void htp_connp_destroy_decompressors(htp_connp_t *connp); + +htp_status_t htp_header_has_token(const unsigned char *hvp, size_t hvlen, const unsigned char *value); + +#ifndef HAVE_STRLCAT +size_t strlcat(char *dst, const char *src, size_t size); +#endif + +#ifndef HAVE_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#ifdef __cplusplus +} +#endif + +// as CURL_MAX_HTTP_HEADER +#define HTP_MAX_HEADER_FOLDED 102400 + +#endif /* _HTP_PRIVATE_H */ + diff --git a/htp/htp_request.c b/htp/htp_request.c new file mode 100644 index 0000000..9fddbd8 --- /dev/null +++ b/htp/htp_request.c @@ -0,0 +1,1173 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +#define IN_TEST_NEXT_BYTE_OR_RETURN(X) \ +if ((X)->in_current_read_offset >= (X)->in_current_len) { \ + return HTP_DATA; \ +} + +#define IN_PEEK_NEXT(X) \ +if ((X)->in_current_read_offset >= (X)->in_current_len) { \ + (X)->in_next_byte = -1; \ +} else { \ + (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ +} + +#define IN_NEXT_BYTE(X) \ +if ((X)->in_current_read_offset < (X)->in_current_len) { \ + (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ + (X)->in_current_read_offset++; \ + (X)->in_current_consume_offset++; \ + (X)->in_stream_offset++; \ +} else { \ + (X)->in_next_byte = -1; \ +} + +#define IN_NEXT_BYTE_OR_RETURN(X) \ +if ((X)->in_current_read_offset < (X)->in_current_len) { \ + (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ + (X)->in_current_read_offset++; \ + (X)->in_current_consume_offset++; \ + (X)->in_stream_offset++; \ +} else { \ + return HTP_DATA; \ +} + +#define IN_COPY_BYTE_OR_RETURN(X) \ +if ((X)->in_current_read_offset < (X)->in_current_len) { \ + (X)->in_next_byte = (X)->in_current_data[(X)->in_current_read_offset]; \ + (X)->in_current_read_offset++; \ + (X)->in_stream_offset++; \ +} else { \ + return HTP_DATA_BUFFER; \ +} + +/** + * Sends outstanding connection data to the currently active data receiver hook. + * + * @param[in] connp + * @param[in] is_last + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_connp_req_receiver_send_data(htp_connp_t *connp, int is_last) { + if (connp->in_data_receiver_hook == NULL) return HTP_OK; + + htp_tx_data_t d; + d.tx = connp->in_tx; + d.data = connp->in_current_data + connp->in_current_receiver_offset; + d.len = connp->in_current_read_offset - connp->in_current_receiver_offset; + d.is_last = is_last; + + htp_status_t rc = htp_hook_run_all(connp->in_data_receiver_hook, &d); + if (rc != HTP_OK) return rc; + + connp->in_current_receiver_offset = connp->in_current_read_offset; + + return HTP_OK; +} + +/** + * Configures the data receiver hook. If there is a previous hook, it will be finalized and cleared. + * + * @param[in] connp + * @param[in] data_receiver_hook + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_connp_req_receiver_set(htp_connp_t *connp, htp_hook_t *data_receiver_hook) { + htp_status_t rc = htp_connp_req_receiver_finalize_clear(connp); + + connp->in_data_receiver_hook = data_receiver_hook; + connp->in_current_receiver_offset = connp->in_current_read_offset; + + return rc; +} + +/** + * Finalizes an existing data receiver hook by sending any outstanding data to it. The + * hook is then removed so that it receives no more data. + * + * @param[in] connp + * @return HTP_OK, or a value returned from a callback. + */ +htp_status_t htp_connp_req_receiver_finalize_clear(htp_connp_t *connp) { + if (connp->in_data_receiver_hook == NULL) return HTP_OK; + + htp_status_t rc = htp_connp_req_receiver_send_data(connp, 1 /* last */); + + connp->in_data_receiver_hook = NULL; + + return rc; +} + +/** + * Handles request parser state changes. At the moment, this function is used only + * to configure data receivers, which are sent raw connection data. + * + * @param[in] connp + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_req_handle_state_change(htp_connp_t *connp) { + if (connp->in_state_previous == connp->in_state) return HTP_OK; + + if (connp->in_state == htp_connp_REQ_HEADERS) { + htp_status_t rc = HTP_OK; + + switch (connp->in_tx->request_progress) { + case HTP_REQUEST_HEADERS: + rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_header_data); + break; + + case HTP_REQUEST_TRAILER: + rc = htp_connp_req_receiver_set(connp, connp->in_tx->cfg->hook_request_trailer_data); + break; + + default: + // Do nothing; receivers are currently used only for header blocks. + break; + } + + if (rc != HTP_OK) return rc; + } + + // Initially, I had the finalization of raw data sending here, but that + // caused the last REQUEST_HEADER_DATA hook to be invoked after the + // REQUEST_HEADERS hook -- which I thought made no sense. For that reason, + // the finalization is now initiated from the request header processing code, + // which is less elegant but provides a better user experience. Having some + // (or all) hooks to be invoked on state change might work better. + + connp->in_state_previous = connp->in_state; + + return HTP_OK; +} + +/** + * If there is any data left in the inbound data chunk, this function will preserve + * it for later consumption. The maximum amount accepted for buffering is controlled + * by htp_config_t::field_limit_hard. + * + * @param[in] connp + * @return HTP_OK, or HTP_ERROR on fatal failure. + */ +static htp_status_t htp_connp_req_buffer(htp_connp_t *connp) { + if (connp->in_current_data == NULL) return HTP_OK; + + unsigned char *data = connp->in_current_data + connp->in_current_consume_offset; + size_t len = connp->in_current_read_offset - connp->in_current_consume_offset; + + if (len == 0) + return HTP_OK; + + // Check the hard (buffering) limit. + + size_t newlen = connp->in_buf_size + len; + + // When calculating the size of the buffer, take into account the + // space we're using for the request header buffer. + if (connp->in_header != NULL) { + newlen += bstr_len(connp->in_header); + } + + if (newlen > connp->in_tx->cfg->field_limit_hard) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request buffer over the limit: size %zd limit %zd.", + newlen, connp->in_tx->cfg->field_limit_hard); + return HTP_ERROR; + } + + // Copy the data remaining in the buffer. + + if (connp->in_buf == NULL) { + connp->in_buf = malloc(len); + if (connp->in_buf == NULL) return HTP_ERROR; + memcpy(connp->in_buf, data, len); + connp->in_buf_size = len; + } else { + size_t newsize = connp->in_buf_size + len; + unsigned char *newbuf = realloc(connp->in_buf, newsize); + if (newbuf == NULL) return HTP_ERROR; + connp->in_buf = newbuf; + memcpy(connp->in_buf + connp->in_buf_size, data, len); + connp->in_buf_size = newsize; + } + + // Reset the consumer position. + connp->in_current_consume_offset = connp->in_current_read_offset; + + return HTP_OK; +} + +/** + * Returns to the caller the memory region that should be processed next. This function + * hides away the buffering process from the rest of the code, allowing it to work with + * non-buffered data that's in the inbound chunk, or buffered data that's in our structures. + * + * @param[in] connp + * @param[out] data + * @param[out] len + * @return HTP_OK + */ +static htp_status_t htp_connp_req_consolidate_data(htp_connp_t *connp, unsigned char **data, size_t *len) { + if (connp->in_buf == NULL) { + // We do not have any data buffered; point to the current data chunk. + *data = connp->in_current_data + connp->in_current_consume_offset; + *len = connp->in_current_read_offset - connp->in_current_consume_offset; + } else { + // We already have some data in the buffer. Add the data from the current + // chunk to it, and point to the consolidated buffer. + if (htp_connp_req_buffer(connp) != HTP_OK) { + return HTP_ERROR; + } + + *data = connp->in_buf; + *len = connp->in_buf_size; + } + + return HTP_OK; +} + +/** + * Clears buffered inbound data and resets the consumer position to the reader position. + * + * @param[in] connp + */ +static void htp_connp_req_clear_buffer(htp_connp_t *connp) { + connp->in_current_consume_offset = connp->in_current_read_offset; + + if (connp->in_buf != NULL) { + free(connp->in_buf); + connp->in_buf = NULL; + connp->in_buf_size = 0; + } +} + +/** + * Performs a check for a CONNECT transaction to decide whether inbound + * parsing needs to be suspended. + * + * @param[in] connp + * @return HTP_OK if the request does not use CONNECT, HTP_DATA_OTHER if + * inbound parsing needs to be suspended until we hear from the + * other side + */ +htp_status_t htp_connp_REQ_CONNECT_CHECK(htp_connp_t *connp) { + // If the request uses the CONNECT method, then there will + // not be a request body, but first we need to wait to see the + // response in order to determine if the tunneling request + // was a success. + if (connp->in_tx->request_method_number == HTP_M_CONNECT) { + connp->in_state = htp_connp_REQ_CONNECT_WAIT_RESPONSE; + connp->in_status = HTP_STREAM_DATA_OTHER; + return HTP_DATA_OTHER; + } + + // Continue to the next step to determine + // the presence of request body + connp->in_state = htp_connp_REQ_BODY_DETERMINE; + + return HTP_OK; +} + +/** + * Determines whether inbound parsing needs to continue or stop. In + * case the data appears to be plain text HTTP, we try to continue. + * + * @param[in] connp + * @return HTP_OK if the parser can resume parsing, HTP_DATA_BUFFER if + * we need more data. + */ +htp_status_t htp_connp_REQ_CONNECT_PROBE_DATA(htp_connp_t *connp) { + for (;;) {//;i < max_read; i++) { + IN_PEEK_NEXT(connp); + // Have we reached the end of the line? For some reason + // we can't test after IN_COPY_BYTE_OR_RETURN */ + if (connp->in_next_byte == LF || connp->in_next_byte == 0x00) + break; + + IN_COPY_BYTE_OR_RETURN(connp); + + } + + unsigned char *data; + size_t len; + if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } +#ifdef HTP_DEBUG + fprint_raw_data(stderr, "PROBING", data, len); +#endif + + size_t pos = 0; + size_t mstart = 0; + // skip past leading whitespace. IIS allows this + while ((pos < len) && htp_is_space(data[pos])) + pos++; + if (pos) + mstart = pos; + // The request method starts at the beginning of the + // line and ends with the first whitespace character. + while ((pos < len) && (!htp_is_space(data[pos]))) + pos++; + + int methodi = HTP_M_UNKNOWN; + bstr *method = bstr_dup_mem(data + mstart, pos - mstart); + if (method) { + methodi = htp_convert_method_to_number(method); + bstr_free(method); + } + if (methodi != HTP_M_UNKNOWN) { +#ifdef HTP_DEBUG + fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel contains plain text HTTP", data, len); +#endif + return htp_tx_state_request_complete(connp->in_tx); + } else { +#ifdef HTP_DEBUG + fprint_raw_data(stderr, "htp_connp_REQ_CONNECT_PROBE_DATA: tunnel is not HTTP", data, len); +#endif + connp->in_status = HTP_STREAM_TUNNEL; + connp->out_status = HTP_STREAM_TUNNEL; + } + + // not calling htp_connp_req_clear_buffer, we're not consuming the data + + return HTP_OK; +} + +/** + * Determines whether inbound parsing, which was suspended after + * encountering a CONNECT transaction, can proceed (after receiving + * the response). + * + * @param[in] connp + * @return HTP_OK if the parser can resume parsing, HTP_DATA_OTHER if + * it needs to continue waiting. + */ +htp_status_t htp_connp_REQ_CONNECT_WAIT_RESPONSE(htp_connp_t *connp) { + // Check that we saw the response line of the current inbound transaction. + if (connp->in_tx->response_progress <= HTP_RESPONSE_LINE) { + return HTP_DATA_OTHER; + } + + // A 2xx response means a tunnel was established. Anything + // else means we continue to follow the HTTP stream. + if ((connp->in_tx->response_status_number >= 200) && (connp->in_tx->response_status_number <= 299)) { + // TODO Check that the server did not accept a connection to itself. + + // The requested tunnel was established: we are going + // to probe the remaining data on this stream to see + // if we need to ignore it or parse it + connp->in_state = htp_connp_REQ_CONNECT_PROBE_DATA; + } else { + // No tunnel; continue to the next transaction + connp->in_state = htp_connp_REQ_FINALIZE; + } + + return HTP_OK; +} + +/** + * Consumes bytes until the end of the current line. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA_END(htp_connp_t *connp) { + // TODO We shouldn't really see anything apart from CR and LF, + // so we should warn about anything else. + + for (;;) { + IN_NEXT_BYTE_OR_RETURN(connp); + + connp->in_tx->request_message_len++; + + if (connp->in_next_byte == LF) { + connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; + return HTP_OK; + } + } + + return HTP_ERROR; +} + +/** + * Processes a chunk of data. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_BODY_CHUNKED_DATA(htp_connp_t *connp) { + // Determine how many bytes we can consume. + size_t bytes_to_consume; + if (connp->in_current_len - connp->in_current_read_offset >= connp->in_chunked_length) { + // Entire chunk available in the buffer; read all of it. + bytes_to_consume = connp->in_chunked_length; + } else { + // Partial chunk available in the buffer; read as much as we can. + bytes_to_consume = connp->in_current_len - connp->in_current_read_offset; + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_REQ_BODY_CHUNKED_DATA Consuming %zd bytes\n", bytes_to_consume); + #endif + + // If the input buffer is empty, ask for more data. + if (bytes_to_consume == 0) return HTP_DATA; + + // Consume the data. + htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume); + if (rc != HTP_OK) return rc; + + // Adjust counters. + connp->in_current_read_offset += bytes_to_consume; + connp->in_current_consume_offset += bytes_to_consume; + connp->in_stream_offset += bytes_to_consume; + connp->in_tx->request_message_len += bytes_to_consume; + connp->in_chunked_length -= bytes_to_consume; + + if (connp->in_chunked_length == 0) { + // End of the chunk. + connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA_END; + return HTP_OK; + } + + // Ask for more data. + return HTP_DATA; +} + +/** + * Extracts chunk length. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_BODY_CHUNKED_LENGTH(htp_connp_t *connp) { + for (;;) { + IN_COPY_BYTE_OR_RETURN(connp); + + // Have we reached the end of the line? + if (connp->in_next_byte == LF) { + unsigned char *data; + size_t len; + + if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + connp->in_tx->request_message_len += len; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "Chunk length line", data, len); + #endif + + htp_chomp(data, &len); + + int chunk_ext = 0; + connp->in_chunked_length = htp_parse_chunked_length(data, len, &chunk_ext); + if (chunk_ext == 1) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request chunk extension"); + } + + htp_connp_req_clear_buffer(connp); + + // Handle chunk length. + if (connp->in_chunked_length > 0) { + // More data available. + connp->in_state = htp_connp_REQ_BODY_CHUNKED_DATA; + } else if (connp->in_chunked_length == 0) { + // End of data. + connp->in_state = htp_connp_REQ_HEADERS; + connp->in_tx->request_progress = HTP_REQUEST_TRAILER; + } else { + // Invalid chunk length. + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request chunk encoding: Invalid chunk length"); + return HTP_ERROR; + } + + return HTP_OK; + } + } + + return HTP_ERROR; +} + +/** + * Processes identity request body. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_BODY_IDENTITY(htp_connp_t *connp) { + // Determine how many bytes we can consume. + size_t bytes_to_consume; + if (connp->in_current_len - connp->in_current_read_offset >= connp->in_body_data_left) { + bytes_to_consume = connp->in_body_data_left; + } else { + bytes_to_consume = connp->in_current_len - connp->in_current_read_offset; + } + + // If the input buffer is empty, ask for more data. + if (bytes_to_consume == 0) return HTP_DATA; + + // Consume data. + int rc = htp_tx_req_process_body_data_ex(connp->in_tx, connp->in_current_data + connp->in_current_read_offset, bytes_to_consume); + if (rc != HTP_OK) return rc; + + // Adjust counters. + connp->in_current_read_offset += bytes_to_consume; + connp->in_current_consume_offset += bytes_to_consume; + connp->in_stream_offset += bytes_to_consume; + connp->in_tx->request_message_len += bytes_to_consume; + connp->in_body_data_left -= bytes_to_consume; + + if (connp->in_body_data_left == 0) { + // End of request body. + connp->in_state = htp_connp_REQ_FINALIZE; + return HTP_OK; + } + + // Ask for more data. + return HTP_DATA; +} + +/** + * Determines presence (and encoding) of a request body. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp) { + // Determine the next state based on the presence of the request + // body, and the coding used. + switch (connp->in_tx->request_transfer_coding) { + + case HTP_CODING_CHUNKED: + connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; + connp->in_tx->request_progress = HTP_REQUEST_BODY; + break; + + case HTP_CODING_IDENTITY: + connp->in_content_length = connp->in_tx->request_content_length; + connp->in_body_data_left = connp->in_content_length; + + if (connp->in_content_length != 0) { + connp->in_state = htp_connp_REQ_BODY_IDENTITY; + connp->in_tx->request_progress = HTP_REQUEST_BODY; + } else { + connp->in_tx->connp->in_state = htp_connp_REQ_FINALIZE; + } + break; + + case HTP_CODING_NO_BODY: + // This request does not have a body, which + // means that we're done with it + connp->in_state = htp_connp_REQ_FINALIZE; + break; + + default: + // Should not be here + return HTP_ERROR; + break; + } + + return HTP_OK; +} + +/** + * Parses request headers. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_HEADERS(htp_connp_t *connp) { + for (;;) { + if (connp->in_status == HTP_STREAM_CLOSED) { + // Parse previous header, if any. + if (connp->in_header != NULL) { + if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), + bstr_len(connp->in_header)) != HTP_OK) + return HTP_ERROR; + bstr_free(connp->in_header); + connp->in_header = NULL; + } + + htp_connp_req_clear_buffer(connp); + + connp->in_tx->request_progress = HTP_REQUEST_TRAILER; + + // We've seen all the request headers. + return htp_tx_state_request_headers(connp->in_tx); + } + IN_COPY_BYTE_OR_RETURN(connp); + + // Have we reached the end of the line? + if (connp->in_next_byte == LF) { + unsigned char *data; + size_t len; + + if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, data, len); + #endif + + // Should we terminate headers? + if (htp_connp_is_line_terminator(connp, data, len, 0)) { + // Parse previous header, if any. + if (connp->in_header != NULL) { + if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), + bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR; + + bstr_free(connp->in_header); + connp->in_header = NULL; + } + + htp_connp_req_clear_buffer(connp); + + // We've seen all the request headers. + return htp_tx_state_request_headers(connp->in_tx); + } + + htp_chomp(data, &len); + + // Check for header folding. + if (htp_connp_is_line_folded(data, len) == 0) { + // New header line. + + // Parse previous header, if any. + if (connp->in_header != NULL) { + if (connp->cfg->process_request_header(connp, bstr_ptr(connp->in_header), + bstr_len(connp->in_header)) != HTP_OK) return HTP_ERROR; + + bstr_free(connp->in_header); + connp->in_header = NULL; + } + + IN_PEEK_NEXT(connp); + + if (connp->in_next_byte != -1 && htp_is_folding_char(connp->in_next_byte) == 0) { + // Because we know this header is not folded, we can process the buffer straight away. + if (connp->cfg->process_request_header(connp, data, len) != HTP_OK) return HTP_ERROR; + } else { + // Keep the partial header data for parsing later. + connp->in_header = bstr_dup_mem(data, len); + if (connp->in_header == NULL) return HTP_ERROR; + } + } else { + // Folding; check that there's a previous header line to add to. + if (connp->in_header == NULL) { + // Invalid folding. + + // Warn only once per transaction. + if (!(connp->in_tx->flags & HTP_INVALID_FOLDING)) { + connp->in_tx->flags |= HTP_INVALID_FOLDING; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid request field folding"); + } + + // Keep the header data for parsing later. + size_t trim = 0; + while(trim < len) { + if (!htp_is_folding_char(data[trim])) { + break; + } + trim++; + } + connp->in_header = bstr_dup_mem(data + trim, len - trim); + if (connp->in_header == NULL) return HTP_ERROR; + } else { + // Add to the existing header. + if (bstr_len(connp->in_header) < HTP_MAX_HEADER_FOLDED) { + bstr *new_in_header = bstr_add_mem(connp->in_header, data, len); + if (new_in_header == NULL) return HTP_ERROR; + connp->in_header = new_in_header; + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field length exceeds folded maximum"); + } + } + } + + htp_connp_req_clear_buffer(connp); + } + } + + return HTP_ERROR; +} + +// HTTP/0.9 is supposed to be only a request line without protocol. +// Libhtp will still consider the request to be HTTP/0.9 if there +// are some junk whitespaces after that request line. +// Libhtp allows the small value of 16 extra bytes/whitespaces, +// otherwise we consider it to be a HTTP/1.x request with missing protocol. +// It is unlikely to meet HTTP/0.9, and we want to limit probing. +#define HTTP09_MAX_JUNK_LEN 16 + +/** + * Determines request protocol. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_PROTOCOL(htp_connp_t *connp) { + // Is this a short-style HTTP/0.9 request? If it is, + // we will not want to parse request headers. + if (connp->in_tx->is_protocol_0_9 == 0) { + // Switch to request header parsing. + connp->in_state = htp_connp_REQ_HEADERS; + connp->in_tx->request_progress = HTP_REQUEST_HEADERS; + } else { + // Let's check if the protocol was simply missing + int64_t pos = connp->in_current_read_offset; + // Probe if data looks like a header line + if (connp->in_current_len > connp->in_current_read_offset + HTTP09_MAX_JUNK_LEN) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: missing protocol"); + connp->in_tx->is_protocol_0_9 = 0; + // Switch to request header parsing. + connp->in_state = htp_connp_REQ_HEADERS; + connp->in_tx->request_progress = HTP_REQUEST_HEADERS; + return HTP_OK; + } + while (pos < connp->in_current_len) { + if (!htp_is_space(connp->in_current_data[pos])) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: missing protocol"); + connp->in_tx->is_protocol_0_9 = 0; + // Switch to request header parsing. + connp->in_state = htp_connp_REQ_HEADERS; + connp->in_tx->request_progress = HTP_REQUEST_HEADERS; + return HTP_OK; + } + pos++; + } + // We're done with this request. + connp->in_state = htp_connp_REQ_FINALIZE; + } + + return HTP_OK; +} + +/** + * Parse the request line. + * + * @param[in] connp + * @returns HTP_OK on succesful parse, HTP_ERROR on error. + */ +htp_status_t htp_connp_REQ_LINE_complete(htp_connp_t *connp) { + unsigned char *data; + size_t len; + + if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, data, len); + #endif + if (len == 0) { + htp_connp_req_clear_buffer(connp); + return HTP_DATA; + } + + // Is this a line that should be ignored? + if (htp_connp_is_line_ignorable(connp, data, len)) { + // We have an empty/whitespace line, which we'll note, ignore and move on. + connp->in_tx->request_ignored_lines++; + + htp_connp_req_clear_buffer(connp); + + return HTP_OK; + } + + // Process request line. + + htp_chomp(data, &len); + + connp->in_tx->request_line = bstr_dup_mem(data, len); + if (connp->in_tx->request_line == NULL) + return HTP_ERROR; + + if (connp->cfg->parse_request_line(connp) != HTP_OK) + return HTP_ERROR; + + // Finalize request line parsing. + + if (htp_tx_state_request_line(connp->in_tx) != HTP_OK) + return HTP_ERROR; + + htp_connp_req_clear_buffer(connp); + + return HTP_OK; +} + +/** + * Parses request line. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_LINE(htp_connp_t *connp) { + for (;;) { + // Get one byte + IN_PEEK_NEXT(connp); + if (connp->in_status == HTP_STREAM_CLOSED && connp->in_next_byte == -1) { + return htp_connp_REQ_LINE_complete(connp); + } + IN_COPY_BYTE_OR_RETURN(connp); + + // Have we reached the end of the line? + if (connp->in_next_byte == LF) { + return htp_connp_REQ_LINE_complete(connp); + } + } + + return HTP_ERROR; +} + +htp_status_t htp_connp_REQ_FINALIZE(htp_connp_t *connp) { + if (connp->in_status != HTP_STREAM_CLOSED) { + IN_PEEK_NEXT(connp); + if (connp->in_next_byte == -1) { + return htp_tx_state_request_complete(connp->in_tx); + } + if (connp->in_next_byte != LF || connp->in_current_consume_offset >= connp->in_current_read_offset) { + for (;;) {//;i < max_read; i++) { + // peek until LF but do not mark it read so that REQ_LINE works + IN_PEEK_NEXT(connp); + if (connp->in_next_byte == LF) + break; + IN_COPY_BYTE_OR_RETURN(connp); + } + } + } + + unsigned char *data; + size_t len; + if (htp_connp_req_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } +#ifdef HTP_DEBUG + fprint_raw_data(stderr, "PROBING request finalize", data, len); +#endif + if (len == 0) { + //closing + return htp_tx_state_request_complete(connp->in_tx); + } + + size_t pos = 0; + size_t mstart = 0; + // skip past leading whitespace. IIS allows this + while ((pos < len) && htp_is_space(data[pos])) + pos++; + if (pos) + mstart = pos; + // The request method starts at the beginning of the + // line and ends with the first whitespace character. + while ((pos < len) && (!htp_is_space(data[pos]))) + pos++; + + if (pos > mstart) { + //non empty whitespace line + int methodi = HTP_M_UNKNOWN; + bstr *method = bstr_dup_mem(data + mstart, pos - mstart); + if (method) { + methodi = htp_convert_method_to_number(method); + bstr_free(method); + } + if (methodi != HTP_M_UNKNOWN) { + connp->in_body_data_left = -1; + return htp_tx_state_request_complete(connp->in_tx); + } // else continue + if (connp->in_body_data_left <= 0) { + // log only once per transaction + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Unexpected request body"); + } else { + connp->in_body_data_left = 1; + } + } + //Adds linefeed to the buffer if there was one + if (connp->in_next_byte == LF) { + IN_COPY_BYTE_OR_RETURN(connp); + htp_connp_req_consolidate_data(connp, &data, &len); + } + // Interpret remaining bytes as body data + htp_status_t rc = htp_tx_req_process_body_data_ex(connp->in_tx, data, len); + htp_connp_req_clear_buffer(connp); + return rc; +} + +htp_status_t htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9(htp_connp_t *connp) { + // Consume whatever is left in the buffer. + + size_t bytes_left = connp->in_current_len - connp->in_current_read_offset; + + if (bytes_left > 0) { + connp->conn->flags |= HTP_CONN_HTTP_0_9_EXTRA; + } + + connp->in_current_read_offset += bytes_left; + connp->in_current_consume_offset += bytes_left; + connp->in_stream_offset += bytes_left; + + return HTP_DATA; +} + +/** + * The idle state is where the parser will end up after a transaction is processed. + * If there is more data available, a new request will be started. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_REQ_IDLE(htp_connp_t * connp) { + // We want to start parsing the next request (and change + // the state from IDLE) only if there's at least one + // byte of data available. Otherwise we could be creating + // new structures even if there's no more data on the + // connection. + IN_TEST_NEXT_BYTE_OR_RETURN(connp); + + connp->in_tx = htp_connp_tx_create(connp); + if (connp->in_tx == NULL) return HTP_ERROR; + + // Change state to TRANSACTION_START + htp_tx_state_request_start(connp->in_tx); + + return HTP_OK; +} + +/** + * Returns how many bytes from the current data chunks were consumed so far. + * + * @param[in] connp + * @return The number of bytes consumed. + */ +size_t htp_connp_req_data_consumed(htp_connp_t *connp) { + return connp->in_current_read_offset; +} + +int htp_connp_req_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data(connp->in_status %x)\n", connp->in_status); + fprint_raw_data(stderr, __func__, data, len); + #endif + + // Return if the connection is in stop state. + if (connp->in_status == HTP_STREAM_STOP) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_INFO, 0, "Inbound parser is in HTP_STREAM_STOP"); + return HTP_STREAM_STOP; + } + + // Return if the connection had a fatal error earlier + if (connp->in_status == HTP_STREAM_ERROR) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Inbound parser is in HTP_STREAM_ERROR"); + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (previous error)\n"); + #endif + + return HTP_STREAM_ERROR; + } + + // Sanity check: we must have a transaction pointer if the state is not IDLE (no inbound transaction) + if ((connp->in_tx == NULL)&&(connp->in_state != htp_connp_REQ_IDLE)) { + connp->in_status = HTP_STREAM_ERROR; + + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Missing inbound transaction data"); + + return HTP_STREAM_ERROR; + } + + // If the length of the supplied data chunk is zero, proceed + // only if the stream has been closed. We do not allow zero-sized + // chunks in the API, but we use them internally to force the parsers + // to finalize parsing. + if (len == 0 && connp->in_status != HTP_STREAM_CLOSED) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed"); + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (zero-length chunk)\n"); + #endif + + return HTP_STREAM_CLOSED; + } + + // Remember the timestamp of the current request data chunk + if (timestamp != NULL) { + memcpy(&connp->in_timestamp, timestamp, sizeof (*timestamp)); + } + + // Store the current chunk information + connp->in_current_data = (unsigned char *) data; + connp->in_current_len = len; + connp->in_current_read_offset = 0; + connp->in_current_consume_offset = 0; + connp->in_current_receiver_offset = 0; + connp->in_chunk_count++; + + htp_conn_track_inbound_data(connp->conn, len, timestamp); + + + // Return without processing any data if the stream is in tunneling + // mode (which it would be after an initial CONNECT transaction). + if (connp->in_status == HTP_STREAM_TUNNEL) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); + #endif + + return HTP_STREAM_TUNNEL; + } + + if (connp->out_status == HTP_STREAM_DATA_OTHER) { + connp->out_status = HTP_STREAM_DATA; + } + + // Invoke a processor, in a loop, until an error + // occurs or until we run out of data. Many processors + // will process a request, each pointing to the next + // processor that needs to run. + for (;;) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: in state=%s, progress=%s\n", + htp_connp_in_state_as_string(connp), + htp_tx_request_progress_as_string(connp->in_tx)); + #endif + + // Return if there's been an error or if we've run out of data. We are relying + // on processors to supply error messages, so we'll keep quiet here. + + htp_status_t rc; + //handle gap + if (data == NULL && len > 0) { + //cannot switch over a function pointer in C + if (connp->in_state == htp_connp_REQ_BODY_IDENTITY || + connp->in_state == htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9) { + rc = connp->in_state(connp); + } else if (connp->in_state == htp_connp_REQ_FINALIZE) { + //simple version without probing + rc = htp_tx_state_request_complete(connp->in_tx); + } else { + // go to htp_connp_REQ_CONNECT_PROBE_DATA ? + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Gaps are not allowed during this state"); + return HTP_STREAM_CLOSED; + } + } else { + rc = connp->in_state(connp); + } + if (rc == HTP_OK) { + if (connp->in_status == HTP_STREAM_TUNNEL) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_TUNNEL\n"); + #endif + + return HTP_STREAM_TUNNEL; + } + + rc = htp_req_handle_state_change(connp); + } + + if (rc != HTP_OK) { + // Do we need more data? + if ((rc == HTP_DATA) || (rc == HTP_DATA_BUFFER)) { + htp_connp_req_receiver_send_data(connp, 0 /* not last */); + + if (rc == HTP_DATA_BUFFER) { + if (htp_connp_req_buffer(connp) != HTP_OK) { + connp->in_status = HTP_STREAM_ERROR; + return HTP_STREAM_ERROR; + } + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA\n"); + #endif + + connp->in_status = HTP_STREAM_DATA; + + return HTP_STREAM_DATA; + } + + // Check for suspended parsing. + if (rc == HTP_DATA_OTHER) { + // We might have actually consumed the entire data chunk? + if (connp->in_current_read_offset >= connp->in_current_len) { + // Do not send STREAM_DATE_DATA_OTHER if we've consumed the entire chunk. + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA (suspended parsing)\n"); + #endif + + connp->in_status = HTP_STREAM_DATA; + + return HTP_STREAM_DATA; + } else { + // Partial chunk consumption. + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_DATA_OTHER\n"); + #endif + + connp->in_status = HTP_STREAM_DATA_OTHER; + + return HTP_STREAM_DATA_OTHER; + } + } + + // Check for the stop signal. + if (rc == HTP_STOP) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_STOP\n"); + #endif + + connp->in_status = HTP_STREAM_STOP; + + return HTP_STREAM_STOP; + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_req_data: returning HTP_STREAM_ERROR\n"); + #endif + + // Permanent stream error. + connp->in_status = HTP_STREAM_ERROR; + + return HTP_STREAM_ERROR; + } + } +} diff --git a/htp/htp_request_apache_2_2.c b/htp/htp_request_apache_2_2.c new file mode 100644 index 0000000..6a56e4e --- /dev/null +++ b/htp/htp_request_apache_2_2.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Extract one request header. A header can span multiple lines, in + * which case they will be folded into one before parsing is attempted. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return HTP_OK or HTP_ERROR + */ +htp_status_t htp_process_request_header_apache_2_2(htp_connp_t *connp, unsigned char *data, size_t len) { + return htp_process_request_header_generic(connp, data, len); +} + +/** + * Parse request line as Apache 2.2 does. + * + * @param[in] connp + * @return HTP_OK or HTP_ERROR + */ +htp_status_t htp_parse_request_line_apache_2_2(htp_connp_t *connp) { + return htp_parse_request_line_generic_ex(connp, 1 /* NUL terminates line */); +} diff --git a/htp/htp_request_generic.c b/htp/htp_request_generic.c new file mode 100644 index 0000000..435cf0a --- /dev/null +++ b/htp/htp_request_generic.c @@ -0,0 +1,462 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Extract one request header. A header can span multiple lines, in + * which case they will be folded into one before parsing is attempted. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return HTP_OK or HTP_ERROR + */ +htp_status_t htp_process_request_header_generic(htp_connp_t *connp, unsigned char *data, size_t len) { + // Create a new header structure. + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return HTP_ERROR; + + // Now try to parse the header. + if (htp_parse_request_header_generic(connp, h, data, len) != HTP_OK) { + free(h); + return HTP_ERROR; + } + + #ifdef HTP_DEBUG + fprint_bstr(stderr, "Header name", h->name); + fprint_bstr(stderr, "Header value", h->value); + #endif + + // Do we already have a header with the same name? + htp_header_t *h_existing = htp_table_get(connp->in_tx->request_headers, h->name); + if (h_existing != NULL) { + // TODO Do we want to have a list of the headers that are + // allowed to be combined in this way? + if ((h_existing->flags & HTP_FIELD_REPEATED) == 0) { + // This is the second occurence for this header. + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Repetition for header"); + } else { + // For simplicity reasons, we count the repetitions of all headers + if (connp->in_tx->req_header_repetitions < HTP_MAX_HEADERS_REPETITIONS) { + connp->in_tx->req_header_repetitions++; + } else { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_OK; + } + } + // Keep track of repeated same-name headers. + h_existing->flags |= HTP_FIELD_REPEATED; + + // Having multiple C-L headers is against the RFC but + // servers may ignore the subsequent headers if the values are the same. + if (bstr_cmp_c_nocase(h->name, "Content-Length") == 0) { + // Don't use string comparison here because we want to + // ignore small formatting differences. + + int64_t existing_cl = htp_parse_content_length(h_existing->value, NULL); + int64_t new_cl = htp_parse_content_length(h->value, NULL); + // Ambiguous response C-L value. + if ((existing_cl == -1) || (new_cl == -1) || (existing_cl != new_cl)) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Ambiguous request C-L value"); + } + // Ignoring the new C-L header that has the same value as the previous ones. + } else { + // Add to the existing header. + bstr *new_value = bstr_expand(h_existing->value, bstr_len(h_existing->value) + 2 + bstr_len(h->value)); + if (new_value == NULL) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + + h_existing->value = new_value; + bstr_add_mem_noex(h_existing->value, ", ", 2); + bstr_add_noex(h_existing->value, h->value); + } + + // The new header structure is no longer needed. + bstr_free(h->name); + bstr_free(h->value); + free(h); + } else { + // Add as a new header. + if (htp_table_add(connp->in_tx->request_headers, h->name, h) != HTP_OK) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + } + + return HTP_OK; +} + +/** + * Generic request header parser. + * + * @param[in] connp + * @param[in] h + * @param[in] data + * @param[in] len + * @return HTP_OK or HTP_ERROR + */ +htp_status_t htp_parse_request_header_generic(htp_connp_t *connp, htp_header_t *h, unsigned char *data, size_t len) { + size_t name_start, name_end; + size_t value_start, value_end; + + htp_chomp(data, &len); + + name_start = 0; + + // Look for the colon. + size_t colon_pos = 0; + while ((colon_pos < len) && (data[colon_pos] != '\0') && (data[colon_pos] != ':')) colon_pos++; + + if ((colon_pos == len) || (data[colon_pos] == '\0')) { + // Missing colon. + + h->flags |= HTP_FIELD_UNPARSEABLE; + + // Log only once per transaction. + if (!(connp->in_tx->flags & HTP_FIELD_UNPARSEABLE)) { + connp->in_tx->flags |= HTP_FIELD_UNPARSEABLE; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field invalid: colon missing"); + } + + // We handle this case as a header with an empty name, with the value equal + // to the entire input string. + + // TODO Apache will respond to this problem with a 400. + + // Now extract the name and the value + h->name = bstr_dup_c(""); + if (h->name == NULL) return HTP_ERROR; + + h->value = bstr_dup_mem(data, len); + if (h->value == NULL) { + bstr_free(h->name); + return HTP_ERROR; + } + + return HTP_OK; + } + + if (colon_pos == 0) { + // Empty header name. + + h->flags |= HTP_FIELD_INVALID; + + // Log only once per transaction. + if (!(connp->in_tx->flags & HTP_FIELD_INVALID)) { + connp->in_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field invalid: empty name"); + } + } + + name_end = colon_pos; + + // Ignore LWS after field-name. + size_t prev = name_end; + while ((prev > name_start) && (htp_is_lws(data[prev - 1]))) { + // LWS after header name. + + prev--; + name_end--; + + h->flags |= HTP_FIELD_INVALID; + + // Log only once per transaction. + if (!(connp->in_tx->flags & HTP_FIELD_INVALID)) { + connp->in_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request field invalid: LWS after name"); + } + } + + // Header value. + + value_start = colon_pos; + + // Go over the colon. + if (value_start < len) { + value_start++; + } + + // Ignore LWS before field-content. + while ((value_start < len) && (htp_is_lws(data[value_start]))) { + value_start++; + } + + // Look for the end of field-content. + value_end = len; + + // Ignore LWS after field-content. + prev = value_end - 1; + while ((prev > value_start) && (htp_is_lws(data[prev]))) { + prev--; + value_end--; + } + + // Check that the header name is a token. + size_t i = name_start; + while (i < name_end) { + if (!htp_is_token(data[i])) { + // Incorrectly formed header name. + + h->flags |= HTP_FIELD_INVALID; + + // Log only once per transaction. + if (!(connp->in_tx->flags & HTP_FIELD_INVALID)) { + connp->in_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request header name is not a token"); + } + + break; + } + + i++; + } + + // Now extract the name and the value + h->name = bstr_dup_mem(data + name_start, name_end - name_start); + if (h->name == NULL) return HTP_ERROR; + + h->value = bstr_dup_mem(data + value_start, value_end - value_start); + if (h->value == NULL) { + bstr_free(h->name); + return HTP_ERROR; + } + + return HTP_OK; +} + +/** + * Generic request line parser. + * + * @param[in] connp + * @return HTP_OK or HTP_ERROR + */ +htp_status_t htp_parse_request_line_generic(htp_connp_t *connp) { + return htp_parse_request_line_generic_ex(connp, 0 /* NUL does not terminates line */); +} + +htp_status_t htp_parse_request_line_generic_ex(htp_connp_t *connp, int nul_terminates) { + htp_tx_t *tx = connp->in_tx; + unsigned char *data = bstr_ptr(tx->request_line); + size_t len = bstr_len(tx->request_line); + size_t pos = 0; + size_t mstart = 0; + size_t start; + size_t bad_delim; + + if (nul_terminates) { + // The line ends with the first NUL byte. + + size_t newlen = 0; + while ((pos < len) && (data[pos] != '\0')) { + pos++; + newlen++; + } + + // Start again, with the new length. + len = newlen; + pos = 0; + } + + // skip past leading whitespace. IIS allows this + while ((pos < len) && htp_is_space(data[pos])) pos++; + if (pos) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: leading whitespace"); + mstart = pos; + + if (connp->cfg->requestline_leading_whitespace_unwanted != HTP_UNWANTED_IGNORE) { + // reset mstart so that we copy the whitespace into the method + mstart = 0; + // set expected response code to this anomaly + tx->response_status_expected_number = connp->cfg->requestline_leading_whitespace_unwanted; + } + } + + // The request method starts at the beginning of the + // line and ends with the first whitespace character. + while ((pos < len) && (!htp_is_space(data[pos]))) pos++; + + // No, we don't care if the method is empty. + + tx->request_method = bstr_dup_mem(data + mstart, pos - mstart); + if (tx->request_method == NULL) return HTP_ERROR; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, bstr_ptr(tx->request_method), bstr_len(tx->request_method)); + #endif + + tx->request_method_number = htp_convert_method_to_number(tx->request_method); + + bad_delim = 0; + // Ignore whitespace after request method. The RFC allows + // for only one SP, but then suggests any number of SP and HT + // should be permitted. Apache uses isspace(), which is even + // more permitting, so that's what we use here. + while ((pos < len) && (isspace(data[pos]))) { + if (!bad_delim && data[pos] != 0x20) { + bad_delim++; + } + pos++; + } +// Too much performance overhead for fuzzing +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (bad_delim) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: non-compliant delimiter between Method and URI"); + } +#endif + + // Is there anything after the request method? + if (pos == len) { + // No, this looks like a HTTP/0.9 request. + + tx->is_protocol_0_9 = 1; + tx->request_protocol_number = HTP_PROTOCOL_0_9; + if (tx->request_method_number == HTP_M_UNKNOWN) + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: unknown method only"); + + return HTP_OK; + } + + start = pos; + bad_delim = 0; + if (tx->connp->cfg->allow_space_uri) { + pos = len - 1; + // Skips the spaces at the end of line (after protocol) + while (pos > start && htp_is_space(data[pos])) pos--; + // The URI ends with the last whitespace. + while ((pos > start) && (data[pos] != 0x20)) { + if (!bad_delim && htp_is_space(data[pos])) { + bad_delim++; + } + pos--; + } + /* if we've seen some 'bad' delimiters, we retry with those */ + if (bad_delim && pos == start) { + // special case: even though RFC's allow only SP (0x20), many + // implementations allow other delimiters, like tab or other + // characters that isspace() accepts. + pos = len - 1; + while ((pos > start) && (!htp_is_space(data[pos]))) pos--; + } else { + // reset bad_delim found in protocol part + bad_delim = 0; + for (size_t i = start; i < pos; i++) { + if (data[i] != 0x20 && htp_is_space(data[i])) { + bad_delim = 1; + break; + } + } + } + if (bad_delim) { +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // warn regardless if we've seen non-compliant chars + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: URI contains non-compliant delimiter"); +#endif + } else if (pos == start) { + pos = len; + } + } else { + // The URI ends with the first whitespace. + while ((pos < len) && (data[pos] != 0x20)) { + if (!bad_delim && htp_is_space(data[pos])) { + bad_delim++; + } + pos++; + } + /* if we've seen some 'bad' delimiters, we retry with those */ + if (bad_delim && pos == len) { + // special case: even though RFC's allow only SP (0x20), many + // implementations allow other delimiters, like tab or other + // characters that isspace() accepts. + pos = start; + while ((pos < len) && (!htp_is_space(data[pos]))) pos++; + } +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (bad_delim) { + // warn regardless if we've seen non-compliant chars + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: URI contains non-compliant delimiter"); + } +#endif + } + + tx->request_uri = bstr_dup_mem(data + start, pos - start); + if (tx->request_uri == NULL) return HTP_ERROR; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, bstr_ptr(tx->request_uri), bstr_len(tx->request_uri)); + #endif + + // Ignore whitespace after URI. + while ((pos < len) && (htp_is_space(data[pos]))) pos++; + + // Is there protocol information available? + if (pos == len) { + // No, this looks like a HTTP/0.9 request. + + tx->is_protocol_0_9 = 1; + tx->request_protocol_number = HTP_PROTOCOL_0_9; + if (tx->request_method_number == HTP_M_UNKNOWN) + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: unknown method and no protocol"); + + return HTP_OK; + } + + // The protocol information continues until the end of the line. + tx->request_protocol = bstr_dup_mem(data + pos, len - pos); + if (tx->request_protocol == NULL) return HTP_ERROR; + + tx->request_protocol_number = htp_parse_protocol(tx->request_protocol); + if (tx->request_method_number == HTP_M_UNKNOWN && tx->request_protocol_number == HTP_PROTOCOL_INVALID) + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line: unknown method and invalid protocol"); + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, bstr_ptr(tx->request_protocol), bstr_len(tx->request_protocol)); + #endif + + return HTP_OK; +} + diff --git a/htp/htp_request_parsers.c b/htp/htp_request_parsers.c new file mode 100644 index 0000000..448ed4c --- /dev/null +++ b/htp/htp_request_parsers.c @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +#if 0 + +/** + * + */ +int htp_header_parse_internal_strict(unsigned char *data, size_t len, htp_header_t *h) { + size_t name_start, name_end; + size_t value_start, value_end; + + // Deal with the name first + name_start = name_end = 0; + + // Find where the header name ends + while (name_end < len) { + if (htp_is_lws(data[name_end]) || data[name_end] == ':') break; + name_end++; + } + + if (name_end == 0) { + // Empty header name + return -1; + } + + if (name_end == len) { + // TODO + return -1; + } + + // Is there any LWS before colon? + size_t pos = name_end; + while (pos < len) { + if (!htp_is_lws(data[pos])) break; + pos++; + // TODO + // return -1; + } + + if (pos == len) { + // TODO + return -1; + } + + // The next character must be a colon + if (data[pos] != ':') { + // TODO + return -1; + } + + // Move over the colon + pos++; + + // Again, ignore any LWS + while (pos < len) { + if (!htp_is_lws(data[pos])) break; + pos++; + } + + if (pos == len) { + // TODO + return -1; + } + + value_start = value_end = pos; + + while (value_end < len) { + if (htp_is_lws(data[value_end])) break; + value_end++; + } + + h->name_offset = name_start; + h->name_len = name_end - name_start; + h->value_offset = value_start; + h->value_len = value_end - value_start; + + return 1; +} + */ + +/** + * + */ +htp_header_t *htp_connp_header_parse(htp_connp_t *reqp, unsigned char *data, size_t len) { + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return NULL; + + // Parse the header line + if (reqp->impl_header_parse(data, len, h) < 0) { + // Invalid header line + h->is_parsed = 0; + h->name = bstr_dup_mem(data, len); + + return h; + } + + // Now extract the name and the value + h->name = bstr_dup_mem(data + h->name_offset, h->name_len); + h->value = bstr_dup_mem(data + h->value_offset, h->value_len); + h->is_parsed = 1; + + // Because header names are case-insensitive, we will convert + // the name to lowercase to use it as a lookup key. + h->name_lowercase = bstr_to_lowercase(h->name); + + return h; +} + +#endif diff --git a/htp/htp_response.c b/htp/htp_response.c new file mode 100644 index 0000000..121004c --- /dev/null +++ b/htp/htp_response.c @@ -0,0 +1,1436 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +#define OUT_TEST_NEXT_BYTE_OR_RETURN(X) \ +if ((X)->out_current_read_offset >= (X)->out_current_len) { \ + return HTP_DATA; \ +} + +#define OUT_PEEK_NEXT(X) \ +if ((X)->out_current_read_offset >= (X)->out_current_len) { \ + (X)->out_next_byte = -1; \ +} else { \ + (X)->out_next_byte = (X)->out_current_data[(X)->out_current_read_offset]; \ +} + +#define OUT_NEXT_BYTE(X) \ +if ((X)->out_current_read_offset < (X)->out_current_len) { \ + (X)->out_next_byte = (X)->out_current_data[(X)->out_current_read_offset]; \ + (X)->out_current_read_offset++; \ + (X)->out_current_consume_offset++; \ + (X)->out_stream_offset++; \ +} else { \ + (X)->out_next_byte = -1; \ +} + +#define OUT_NEXT_BYTE_OR_RETURN(X) \ +if ((X)->out_current_read_offset < (X)->out_current_len) { \ + (X)->out_next_byte = (X)->out_current_data[(X)->out_current_read_offset]; \ + (X)->out_current_read_offset++; \ + (X)->out_current_consume_offset++; \ + (X)->out_stream_offset++; \ +} else { \ + return HTP_DATA; \ +} + +#define OUT_COPY_BYTE_OR_RETURN(X) \ +if ((X)->out_current_read_offset < (X)->out_current_len) { \ + (X)->out_next_byte = (X)->out_current_data[(X)->out_current_read_offset]; \ + (X)->out_current_read_offset++; \ + (X)->out_stream_offset++; \ +} else { \ + return HTP_DATA_BUFFER; \ +} + +#define REQUEST_URI_NOT_SEEN "/libhtp::request_uri_not_seen" + +/** + * Sends outstanding connection data to the currently active data receiver hook. + * + * @param[in] connp + * @param[in] is_last + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_connp_res_receiver_send_data(htp_connp_t *connp, int is_last) { + if (connp->out_data_receiver_hook == NULL) return HTP_OK; + + htp_tx_data_t d; + d.tx = connp->out_tx; + d.data = connp->out_current_data + connp->out_current_receiver_offset; + d.len = connp->out_current_read_offset - connp->out_current_receiver_offset; + d.is_last = is_last; + + htp_status_t rc = htp_hook_run_all(connp->out_data_receiver_hook, &d); + if (rc != HTP_OK) return rc; + + connp->out_current_receiver_offset = connp->out_current_read_offset; + + return HTP_OK; +} + +/** + * Finalizes an existing data receiver hook by sending any outstanding data to it. The + * hook is then removed so that it receives no more data. + * + * @param[in] connp + * @return HTP_OK, or a value returned from a callback. + */ +htp_status_t htp_connp_res_receiver_finalize_clear(htp_connp_t *connp) { + if (connp->out_data_receiver_hook == NULL) return HTP_OK; + + htp_status_t rc = htp_connp_res_receiver_send_data(connp, 1 /* last */); + + connp->out_data_receiver_hook = NULL; + + return rc; +} + +/** + * Configures the data receiver hook. If there is a previous hook, it will be finalized and cleared. + * + * @param[in] connp + * @param[in] data_receiver_hook + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_connp_res_receiver_set(htp_connp_t *connp, htp_hook_t *data_receiver_hook) { + htp_status_t rc = htp_connp_res_receiver_finalize_clear(connp); + + connp->out_data_receiver_hook = data_receiver_hook; + connp->out_current_receiver_offset = connp->out_current_read_offset; + + return rc; +} + +/** + * Handles request parser state changes. At the moment, this function is used only + * to configure data receivers, which are sent raw connection data. + * + * @param[in] connp + * @return HTP_OK, or a value returned from a callback. + */ +static htp_status_t htp_res_handle_state_change(htp_connp_t *connp) { + if (connp->out_state_previous == connp->out_state) return HTP_OK; + + if (connp->out_state == htp_connp_RES_HEADERS) { + htp_status_t rc = HTP_OK; + + switch (connp->out_tx->response_progress) { + case HTP_RESPONSE_HEADERS: + rc = htp_connp_res_receiver_set(connp, connp->out_tx->cfg->hook_response_header_data); + break; + + case HTP_RESPONSE_TRAILER: + rc = htp_connp_res_receiver_set(connp, connp->out_tx->cfg->hook_response_trailer_data); + break; + + default: + // Do nothing; receivers are currently used only for header blocks. + break; + } + + if (rc != HTP_OK) return rc; + } + + // Same comment as in htp_req_handle_state_change(). Below is a copy. + + // Initially, I had the finalization of raw data sending here, but that + // caused the last REQUEST_HEADER_DATA hook to be invoked after the + // REQUEST_HEADERS hook -- which I thought made no sense. For that reason, + // the finalization is now initiated from the request header processing code, + // which is less elegant but provides a better user experience. Having some + // (or all) hooks to be invoked on state change might work better. + + connp->out_state_previous = connp->out_state; + + return HTP_OK; +} + +/** + * If there is any data left in the outbound data chunk, this function will preserve + * it for later consumption. The maximum amount accepted for buffering is controlled + * by htp_config_t::field_limit_hard. + * + * @param[in] connp + * @return HTP_OK, or HTP_ERROR on fatal failure. + */ +static htp_status_t htp_connp_res_buffer(htp_connp_t *connp) { + if (connp->out_current_data == NULL) return HTP_OK; + + unsigned char *data = connp->out_current_data + connp->out_current_consume_offset; + size_t len = connp->out_current_read_offset - connp->out_current_consume_offset; + + // Check the hard (buffering) limit. + + size_t newlen = connp->out_buf_size + len; + + // When calculating the size of the buffer, take into account the + // space we're using for the response header buffer. + if (connp->out_header != NULL) { + newlen += bstr_len(connp->out_header); + } + + if (newlen > connp->out_tx->cfg->field_limit_hard) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Response the buffer limit: size %zd limit %zd.", + newlen, connp->out_tx->cfg->field_limit_hard); + return HTP_ERROR; + } + + // Copy the data remaining in the buffer. + + if (connp->out_buf == NULL) { + connp->out_buf = malloc(len); + if (connp->out_buf == NULL) return HTP_ERROR; + memcpy(connp->out_buf, data, len); + connp->out_buf_size = len; + } else { + size_t newsize = connp->out_buf_size + len; + unsigned char *newbuf = realloc(connp->out_buf, newsize); + if (newbuf == NULL) return HTP_ERROR; + connp->out_buf = newbuf; + memcpy(connp->out_buf + connp->out_buf_size, data, len); + connp->out_buf_size = newsize; + } + + // Reset the consumer position. + connp->out_current_consume_offset = connp->out_current_read_offset; + + return HTP_OK; +} + +/** + * Returns to the caller the memory region that should be processed next. This function + * hides away the buffering process from the rest of the code, allowing it to work with + * non-buffered data that's in the outbound chunk, or buffered data that's in our structures. + * + * @param[in] connp + * @param[out] data + * @param[out] len + * @return HTP_OK + */ +static htp_status_t htp_connp_res_consolidate_data(htp_connp_t *connp, unsigned char **data, size_t *len) { + if (connp->out_buf == NULL) { + // We do not have any data buffered; point to the current data chunk. + *data = connp->out_current_data + connp->out_current_consume_offset; + *len = connp->out_current_read_offset - connp->out_current_consume_offset; + } else { + // We do have data in the buffer. Add data from the current + // chunk, and point to the consolidated buffer. + if (htp_connp_res_buffer(connp) != HTP_OK) { + return HTP_ERROR; + } + + *data = connp->out_buf; + *len = connp->out_buf_size; + } + + return HTP_OK; +} + +/** + * Clears buffered outbound data and resets the consumer position to the reader position. + * + * @param[in] connp + */ +static void htp_connp_res_clear_buffer(htp_connp_t *connp) { + connp->out_current_consume_offset = connp->out_current_read_offset; + + if (connp->out_buf != NULL) { + free(connp->out_buf); + connp->out_buf = NULL; + connp->out_buf_size = 0; + } +} + +/** + * Consumes bytes until the end of the current line. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_CHUNKED_DATA_END(htp_connp_t *connp) { + // TODO We shouldn't really see anything apart from CR and LF, + // so we should warn about anything else. + + for (;;) { + OUT_NEXT_BYTE_OR_RETURN(connp); + + connp->out_tx->response_message_len++; + + if (connp->out_next_byte == LF) { + connp->out_state = htp_connp_RES_BODY_CHUNKED_LENGTH; + + return HTP_OK; + } + } + + return HTP_ERROR; +} + +/** + * Processes a chunk of data. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_CHUNKED_DATA(htp_connp_t *connp) { + size_t bytes_to_consume; + + // Determine how many bytes we can consume. + if (connp->out_current_len - connp->out_current_read_offset >= connp->out_chunked_length) { + bytes_to_consume = connp->out_chunked_length; + } else { + bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; + } + + if (bytes_to_consume == 0) return HTP_DATA; + + // Consume the data. + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); + if (rc != HTP_OK) return rc; + + // Adjust the counters. + connp->out_current_read_offset += bytes_to_consume; + connp->out_current_consume_offset += bytes_to_consume; + connp->out_stream_offset += bytes_to_consume; + connp->out_chunked_length -= bytes_to_consume; + + // Have we seen the entire chunk? + if (connp->out_chunked_length == 0) { + connp->out_state = htp_connp_RES_BODY_CHUNKED_DATA_END; + return HTP_OK; + } + + return HTP_DATA; +} + +static inline int is_chunked_ctl_char(const unsigned char c) { + switch (c) { + case 0x0d: + case 0x0a: + case 0x20: + case 0x09: + case 0x0b: + case 0x0c: + return 1; + default: + return 0; + } +} + +/** + * Peeks ahead into the data to try to see if it starts with a valid Chunked + * length field. + * + * @returns 1 if it looks valid, 0 if it looks invalid + */ +static inline int data_probe_chunk_length(htp_connp_t *connp) { + if (connp->out_current_read_offset - connp->out_current_consume_offset < 8) { + // not enough data so far, consider valid still + return 1; + } + + unsigned char *data = connp->out_current_data + connp->out_current_consume_offset; + size_t len = connp->out_current_read_offset - connp->out_current_consume_offset; + + size_t i = 0; + while (i < len) { + unsigned char c = data[i]; + + if (is_chunked_ctl_char(c)) { + // ctl char, still good. + } else if (isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + // real chunklen char + return 1; + } else { + // leading junk, bad + return 0; + } + i++; + } + return 1; +} + +/** + * Extracts chunk length. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_CHUNKED_LENGTH(htp_connp_t *connp) { + for (;;) { + OUT_COPY_BYTE_OR_RETURN(connp); + + // Have we reached the end of the line? Or is this not chunked after all? + if (connp->out_next_byte == LF || + (!is_chunked_ctl_char((unsigned char) connp->out_next_byte) && !data_probe_chunk_length(connp))) { + unsigned char *data; + size_t len; + + if (htp_connp_res_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + connp->out_tx->response_message_len += len; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "Chunk length line", data, len); + #endif + + int chunk_ext = 0; + connp->out_chunked_length = htp_parse_chunked_length(data, len, &chunk_ext); + if (chunk_ext == 1) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request chunk extension"); + } + // empty chunk length line, lets try to continue + if (connp->out_chunked_length == -1004) { + connp->out_current_consume_offset = connp->out_current_read_offset; + continue; + } + if (connp->out_chunked_length < 0) { + // reset out_current_read_offset so htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE + // doesn't miss the first bytes + + if (len > (size_t)connp->out_current_read_offset) { + connp->out_current_read_offset = 0; + } else { + connp->out_current_read_offset -= len; + } + + connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE; + connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; + + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Response chunk encoding: Invalid chunk length: %"PRId64"", + connp->out_chunked_length); + return HTP_OK; + } + htp_connp_res_clear_buffer(connp); + + // Handle chunk length + if (connp->out_chunked_length > 0) { + // More data available + connp->out_state = htp_connp_RES_BODY_CHUNKED_DATA; + } else if (connp->out_chunked_length == 0) { + // End of data + connp->out_state = htp_connp_RES_HEADERS; + connp->out_tx->response_progress = HTP_RESPONSE_TRAILER; + } + + return HTP_OK; + } + } + + return HTP_ERROR; +} + +/** + * Processes an identity response body of known length. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_IDENTITY_CL_KNOWN(htp_connp_t *connp) { + size_t bytes_to_consume; + + // Determine how many bytes we can consume. + if (connp->out_current_len - connp->out_current_read_offset >= connp->out_body_data_left) { + bytes_to_consume = connp->out_body_data_left; + } else { + bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; + } + + if (connp->out_status == HTP_STREAM_CLOSED) { + connp->out_state = htp_connp_RES_FINALIZE; + // Sends close signal to decompressors + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, NULL, 0); + return rc; + } + if (bytes_to_consume == 0) return HTP_DATA; + + // Consume the data. + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); + if (rc != HTP_OK) return rc; + + // Adjust the counters. + connp->out_current_read_offset += bytes_to_consume; + connp->out_current_consume_offset += bytes_to_consume; + connp->out_stream_offset += bytes_to_consume; + connp->out_body_data_left -= bytes_to_consume; + + // Have we seen the entire response body? + if (connp->out_body_data_left == 0) { + connp->out_state = htp_connp_RES_FINALIZE; + // Tells decompressors to output partially decompressed data + rc = htp_tx_res_process_body_data_ex(connp->out_tx, NULL, 0); + return rc; + } + + return HTP_DATA; +} + +/** + * Processes identity response body of unknown length. In this case, we assume the + * response body consumes all data until the end of the stream. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE(htp_connp_t *connp) { + // Consume all data from the input buffer. + size_t bytes_to_consume = connp->out_current_len - connp->out_current_read_offset; + + #ifdef HTP_DEBUG + fprintf(stderr, "bytes_to_consume %"PRIuMAX, (uintmax_t)bytes_to_consume); + #endif + if (bytes_to_consume != 0) { + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, connp->out_current_data + connp->out_current_read_offset, bytes_to_consume); + if (rc != HTP_OK) return rc; + + // Adjust the counters. + connp->out_current_read_offset += bytes_to_consume; + connp->out_current_consume_offset += bytes_to_consume; + connp->out_stream_offset += bytes_to_consume; + } + + // Have we seen the entire response body? + if (connp->out_status == HTP_STREAM_CLOSED) { + connp->out_state = htp_connp_RES_FINALIZE; + return HTP_OK; + } + + return HTP_DATA; +} + +/** + * Determines presence (and encoding) of a response body. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_BODY_DETERMINE(htp_connp_t *connp) { + // If the request uses the CONNECT method, then not only are we + // to assume there's no body, but we need to ignore all + // subsequent data in the stream. + if (connp->out_tx->request_method_number == HTP_M_CONNECT) { + if ((connp->out_tx->response_status_number >= 200) + && (connp->out_tx->response_status_number <= 299)) { + // This is a successful CONNECT stream, which means + // we need to switch into tunneling mode: on the + // request side we'll now probe the tunnel data to see + // if we need to parse or ignore it. So on the response + // side we wrap up the tx and wait. + connp->out_state = htp_connp_RES_FINALIZE; + + // we may have response headers + htp_status_t rc = htp_tx_state_response_headers(connp->out_tx); + return rc; + } else if (connp->out_tx->response_status_number == 407) { + // proxy telling us to auth + if (connp->in_status != HTP_STREAM_ERROR) + connp->in_status = HTP_STREAM_DATA; + } else { + // This is a failed CONNECT stream, which means that + // we can unblock request parsing + if (connp->in_status != HTP_STREAM_ERROR) + connp->in_status = HTP_STREAM_DATA; + + // We are going to continue processing this transaction, + // adding a note for ourselves to stop at the end (because + // we don't want to see the beginning of a new transaction). + connp->out_data_other_at_tx_end = 1; + } + } + + htp_header_t *cl = htp_table_get_c(connp->out_tx->response_headers, "content-length"); + htp_header_t *te = htp_table_get_c(connp->out_tx->response_headers, "transfer-encoding"); + + // Check for "101 Switching Protocol" response. + // If it's seen, it means that traffic after empty line following headers + // is no longer HTTP. We can treat it similarly to CONNECT. + // Unlike CONNECT, however, upgrades from HTTP to HTTP seem + // rather unlikely, so don't try to probe tunnel for nested HTTP, + // and switch to tunnel mode right away. + if (connp->out_tx->response_status_number == 101) { + if (te == NULL && cl == NULL) { + connp->out_state = htp_connp_RES_FINALIZE; + + if (connp->in_status != HTP_STREAM_ERROR) + connp->in_status = HTP_STREAM_TUNNEL; + connp->out_status = HTP_STREAM_TUNNEL; + + // we may have response headers + htp_status_t rc = htp_tx_state_response_headers(connp->out_tx); + return rc; + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Switching Protocol with Content-Length"); + } + } + + // Check for an interim "100 Continue" response. Ignore it if found, and revert back to RES_LINE. + if (connp->out_tx->response_status_number == 100 && te == NULL) { + int is100continue = 1; + if (cl != NULL){ + if (htp_parse_content_length(cl->value, connp) > 0) { + is100continue = 0; + } + } + if (is100continue) { + if (connp->out_tx->seen_100continue != 0) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Already seen 100-Continue."); + } + + // Ignore any response headers seen so far. + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(connp->out_tx->response_headers); i < n; i++) { + h = htp_table_get_index(connp->out_tx->response_headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_clear(connp->out_tx->response_headers); + + // Expecting to see another response line next. + connp->out_state = htp_connp_RES_LINE; + connp->out_tx->response_progress = HTP_RESPONSE_LINE; + connp->out_tx->seen_100continue++; + + return HTP_OK; + } + } + + // A request can indicate it waits for headers validation + // before sending its body cf + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect + if (connp->out_tx->response_status_number >= 400 && + connp->out_tx->response_status_number <= 499 && + connp->in_content_length > 0 && + connp->in_body_data_left == connp->in_content_length) { + htp_header_t *exp = htp_table_get_c(connp->out_tx->request_headers, "expect"); + if ((exp != NULL) && (bstr_cmp_c_nocase(exp->value, "100-continue") == 0)) { + connp->in_state = htp_connp_REQ_FINALIZE; + } + } + + // 1. Any response message which MUST NOT include a message-body + // (such as the 1xx, 204, and 304 responses and any response to a HEAD + // request) is always terminated by the first empty line after the + // header fields, regardless of the entity-header fields present in the + // message. + if (connp->out_tx->request_method_number == HTP_M_HEAD) { + // There's no response body whatsoever + connp->out_tx->response_transfer_coding = HTP_CODING_NO_BODY; + connp->out_state = htp_connp_RES_FINALIZE; + } + else if (((connp->out_tx->response_status_number >= 100) && (connp->out_tx->response_status_number <= 199)) + || (connp->out_tx->response_status_number == 204) || (connp->out_tx->response_status_number == 304)) { + // There should be no response body + // but browsers interpret content sent by the server as such + if (te == NULL && cl == NULL) { + connp->out_tx->response_transfer_coding = HTP_CODING_NO_BODY; + connp->out_state = htp_connp_RES_FINALIZE; + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Unexpected Response body"); + } + } + // Hack condition to check that we do not assume "no body" + if (connp->out_state != htp_connp_RES_FINALIZE) { + // We have a response body + htp_header_t *ct = htp_table_get_c(connp->out_tx->response_headers, "content-type"); + if (ct != NULL) { + connp->out_tx->response_content_type = bstr_dup_lower(ct->value); + if (connp->out_tx->response_content_type == NULL) return HTP_ERROR; + + // Ignore parameters + unsigned char *data = bstr_ptr(connp->out_tx->response_content_type); + size_t len = bstr_len(ct->value); + size_t newlen = 0; + while (newlen < len) { + // TODO Some platforms may do things differently here. + if (htp_is_space(data[newlen]) || (data[newlen] == ';')) { + bstr_adjust_len(connp->out_tx->response_content_type, newlen); + break; + } + + newlen++; + } + } + + // 2. If a Transfer-Encoding header field (section 14.40) is present and + // indicates that the "chunked" transfer coding has been applied, then + // the length is defined by the chunked encoding (section 3.6). + if ((te != NULL) && (bstr_index_of_c_nocasenorzero(te->value, "chunked") != -1)) { + if (bstr_cmp_c_nocase(te->value, "chunked") != 0) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Transfer-encoding has abnormal chunked value"); + } + + // spec says chunked is HTTP/1.1 only, but some browsers accept it + // with 1.0 as well + if (connp->out_tx->response_protocol_number < HTP_PROTOCOL_1_1) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Chunked transfer-encoding on HTTP/0.9 or HTTP/1.0"); + } + + // If the T-E header is present we are going to use it. + connp->out_tx->response_transfer_coding = HTP_CODING_CHUNKED; + + // We are still going to check for the presence of C-L + if (cl != NULL) { + // This is a violation of the RFC + connp->out_tx->flags |= HTP_REQUEST_SMUGGLING; + } + + connp->out_state = htp_connp_RES_BODY_CHUNKED_LENGTH; + connp->out_tx->response_progress = HTP_RESPONSE_BODY; + }// 3. If a Content-Length header field (section 14.14) is present, its + // value in bytes represents the length of the message-body. + else if (cl != NULL) { + // We know the exact length + connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; + + // Check for multiple C-L headers + if (cl->flags & HTP_FIELD_REPEATED) { + connp->out_tx->flags |= HTP_REQUEST_SMUGGLING; + } + + // Get body length + connp->out_tx->response_content_length = htp_parse_content_length(cl->value, connp); + if (connp->out_tx->response_content_length < 0) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid C-L field in response: %"PRId64"", + connp->out_tx->response_content_length); + return HTP_ERROR; + } else { + connp->out_content_length = connp->out_tx->response_content_length; + connp->out_body_data_left = connp->out_content_length; + + if (connp->out_content_length != 0) { + connp->out_state = htp_connp_RES_BODY_IDENTITY_CL_KNOWN; + connp->out_tx->response_progress = HTP_RESPONSE_BODY; + } else { + connp->out_state = htp_connp_RES_FINALIZE; + } + } + } else { + // 4. If the message uses the media type "multipart/byteranges", which is + // self-delimiting, then that defines the length. This media type MUST + // NOT be used unless the sender knows that the recipient can parse it; + // the presence in a request of a Range header with multiple byte-range + // specifiers implies that the client can parse multipart/byteranges + // responses. + if (ct != NULL) { + // TODO Handle multipart/byteranges + if (bstr_index_of_c_nocase(ct->value, "multipart/byteranges") != -1) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "C-T multipart/byteranges in responses not supported"); + return HTP_ERROR; + } + } + + // 5. By the server closing the connection. (Closing the connection + // cannot be used to indicate the end of a request body, since that + // would leave no possibility for the server to send back a response.) + connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE; + connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; + connp->out_tx->response_progress = HTP_RESPONSE_BODY; + connp->out_body_data_left = -1; + } + } + + // NOTE We do not need to check for short-style HTTP/0.9 requests here because + // that is done earlier, before response line parsing begins + + htp_status_t rc = htp_tx_state_response_headers(connp->out_tx); + if (rc != HTP_OK) return rc; + + return HTP_OK; +} + +/** + * Parses response headers. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_HEADERS(htp_connp_t *connp) { + int endwithcr; + int lfcrending = 0; + + for (;;) { + if (connp->out_status == HTP_STREAM_CLOSED) { + // Finalize sending raw trailer data. + htp_status_t rc = htp_connp_res_receiver_finalize_clear(connp); + if (rc != HTP_OK) return rc; + + // Run hook response_TRAILER. + rc = htp_hook_run_all(connp->cfg->hook_response_trailer, connp->out_tx); + if (rc != HTP_OK) return rc; + + connp->out_state = htp_connp_RES_FINALIZE; + return HTP_OK; + } + OUT_COPY_BYTE_OR_RETURN(connp); + + // Have we reached the end of the line? + if (connp->out_next_byte != LF && connp->out_next_byte != CR) { + lfcrending = 0; + } else { + endwithcr = 0; + if (connp->out_next_byte == CR) { + OUT_PEEK_NEXT(connp); + if (connp->out_next_byte == -1) { + return HTP_DATA_BUFFER; + } else if (connp->out_next_byte == LF) { + OUT_COPY_BYTE_OR_RETURN(connp); + if (lfcrending) { + // Handling LFCRCRLFCRLF + // These 6 characters mean only 2 end of lines + OUT_PEEK_NEXT(connp); + if (connp->out_next_byte == CR) { + OUT_COPY_BYTE_OR_RETURN(connp); + connp->out_current_consume_offset++; + OUT_PEEK_NEXT(connp); + if (connp->out_next_byte == LF) { + OUT_COPY_BYTE_OR_RETURN(connp); + connp->out_current_consume_offset++; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Weird response end of lines mix"); + } + } + } + } else if (connp->out_next_byte == CR) { + continue; + } + lfcrending = 0; + endwithcr = 1; + } else { + // connp->out_next_byte == LF + OUT_PEEK_NEXT(connp); + lfcrending = 0; + if (connp->out_next_byte == CR) { + // hanldes LF-CR sequence as end of line + OUT_COPY_BYTE_OR_RETURN(connp); + lfcrending = 1; + } + } + + unsigned char *data; + size_t len; + + if (htp_connp_res_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + // CRCRLF is not an empty line + if (endwithcr && len < 2) { + continue; + } + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, data, len); + #endif + + int next_no_lf = 0; + if (connp->out_current_read_offset < connp->out_current_len && + connp->out_current_data[connp->out_current_read_offset] != LF) { + next_no_lf = 1; + } + // Should we terminate headers? + if (htp_connp_is_line_terminator(connp, data, len, next_no_lf)) { + // Parse previous header, if any. + if (connp->out_header != NULL) { + if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), + bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR; + + bstr_free(connp->out_header); + connp->out_header = NULL; + } + + htp_connp_res_clear_buffer(connp); + + // We've seen all response headers. + if (connp->out_tx->response_progress == HTP_RESPONSE_HEADERS) { + // Response headers. + + // The next step is to determine if this response has a body. + connp->out_state = htp_connp_RES_BODY_DETERMINE; + } else { + // Response trailer. + + // Finalize sending raw trailer data. + htp_status_t rc = htp_connp_res_receiver_finalize_clear(connp); + if (rc != HTP_OK) return rc; + + // Run hook response_TRAILER. + rc = htp_hook_run_all(connp->cfg->hook_response_trailer, connp->out_tx); + if (rc != HTP_OK) return rc; + + // The next step is to finalize this response. + connp->out_state = htp_connp_RES_FINALIZE; + } + + return HTP_OK; + } + + htp_chomp(data, &len); + + // Check for header folding. + if (htp_connp_is_line_folded(data, len) == 0) { + // New header line. + + // Parse previous header, if any. + if (connp->out_header != NULL) { + if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), + bstr_len(connp->out_header)) != HTP_OK) return HTP_ERROR; + + bstr_free(connp->out_header); + connp->out_header = NULL; + } + + OUT_PEEK_NEXT(connp); + + if (htp_is_folding_char(connp->out_next_byte) == 0) { + // Because we know this header is not folded, we can process the buffer straight away. + if (connp->cfg->process_response_header(connp, data, len) != HTP_OK) return HTP_ERROR; + } else { + // Keep the partial header data for parsing later. + connp->out_header = bstr_dup_mem(data, len); + if (connp->out_header == NULL) return HTP_ERROR; + } + } else { + // Folding; check that there's a previous header line to add to. + if (connp->out_header == NULL) { + // Invalid folding. + + // Warn only once per transaction. + if (!(connp->out_tx->flags & HTP_INVALID_FOLDING)) { + connp->out_tx->flags |= HTP_INVALID_FOLDING; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response field folding"); + } + + // Keep the header data for parsing later. + size_t trim = 0; + while(trim < len) { + if (!htp_is_folding_char(data[trim])) { + break; + } + trim++; + } + connp->out_header = bstr_dup_mem(data + trim, len - trim); + if (connp->out_header == NULL) return HTP_ERROR; + } else { + size_t colon_pos = 0; + while ((colon_pos < len) && (data[colon_pos] != ':')) colon_pos++; + + if (colon_pos < len && + bstr_chr(connp->out_header, ':') >= 0 && + connp->out_tx->response_protocol_number == HTP_PROTOCOL_1_1) { + // Warn only once per transaction. + if (!(connp->out_tx->flags & HTP_INVALID_FOLDING)) { + connp->out_tx->flags |= HTP_INVALID_FOLDING; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Invalid response field folding"); + } + if (connp->cfg->process_response_header(connp, bstr_ptr(connp->out_header), + bstr_len(connp->out_header)) != HTP_OK) + return HTP_ERROR; + bstr_free(connp->out_header); + connp->out_header = bstr_dup_mem(data+1, len-1); + if (connp->out_header == NULL) + return HTP_ERROR; + } else { + // Add to the existing header. + if (bstr_len(connp->out_header) < HTP_MAX_HEADER_FOLDED) { + bstr *new_out_header = bstr_add_mem(connp->out_header, data, len); + if (new_out_header == NULL) + return HTP_ERROR; + connp->out_header = new_out_header; + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field length exceeds folded maximum"); + } + } + } + } + + htp_connp_res_clear_buffer(connp); + } + } + + return HTP_ERROR; +} + +/** + * Parses response line. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_LINE(htp_connp_t *connp) { + for (;;) { + // Don't try to get more data if the stream is closed. If we do, we'll return, asking for more data. + if (connp->out_status != HTP_STREAM_CLOSED) { + // Get one byte + OUT_COPY_BYTE_OR_RETURN(connp); + } + + // Have we reached the end of the line? We treat stream closure as end of line in + // order to handle the case when the first line of the response is actually response body + // (and we wish it processed as such). + if (connp->out_next_byte == CR) { + OUT_PEEK_NEXT(connp); + if (connp->out_next_byte == -1) { + return HTP_DATA_BUFFER; + } else if (connp->out_next_byte == LF) { + continue; + } + connp->out_next_byte = LF; + } + if ((connp->out_next_byte == LF)||(connp->out_status == HTP_STREAM_CLOSED)) { + unsigned char *data; + size_t len; + + if (htp_connp_res_consolidate_data(connp, &data, &len) != HTP_OK) { + return HTP_ERROR; + } + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, data, len); + #endif + + // Is this a line that should be ignored? + if (htp_connp_is_line_ignorable(connp, data, len)) { + if (connp->out_status == HTP_STREAM_CLOSED) { + connp->out_state = htp_connp_RES_FINALIZE; + } + // We have an empty/whitespace line, which we'll note, ignore and move on + connp->out_tx->response_ignored_lines++; + + // TODO How many lines are we willing to accept? + + // Start again + htp_connp_res_clear_buffer(connp); + + return HTP_OK; + } + + // Deallocate previous response line allocations, which we would have on a 100 response. + + if (connp->out_tx->response_line != NULL) { + bstr_free(connp->out_tx->response_line); + connp->out_tx->response_line = NULL; + } + + if (connp->out_tx->response_protocol != NULL) { + bstr_free(connp->out_tx->response_protocol); + connp->out_tx->response_protocol = NULL; + } + + if (connp->out_tx->response_status != NULL) { + bstr_free(connp->out_tx->response_status); + connp->out_tx->response_status = NULL; + } + + if (connp->out_tx->response_message != NULL) { + bstr_free(connp->out_tx->response_message); + connp->out_tx->response_message = NULL; + } + + // Process response line. + + int chomp_result = htp_chomp(data, &len); + + // If the response line is invalid, determine if it _looks_ like + // a response line. If it does not look like a line, process the + // data as a response body because that is what browsers do. + + if (htp_treat_response_line_as_body(data, len)) { + // if we have a next line beginning with H, skip this one + if (connp->out_current_read_offset+1 < connp->out_current_len && (connp->out_current_data[connp->out_current_read_offset] == 'H' || len <= 2)) { + connp->out_tx->response_ignored_lines++; + htp_connp_res_clear_buffer(connp); + return HTP_OK; + } + connp->out_tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; + + connp->out_current_consume_offset = connp->out_current_read_offset; + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, data, len + chomp_result); + htp_connp_res_clear_buffer(connp); + if (rc != HTP_OK) return rc; + + // Continue to process response body. Because we don't have + // any headers to parse, we assume the body continues until + // the end of the stream. + + // Have we seen the entire response body? + if (connp->out_current_len <= connp->out_current_read_offset) { + connp->out_tx->response_transfer_coding = HTP_CODING_IDENTITY; + connp->out_tx->response_progress = HTP_RESPONSE_BODY; + connp->out_body_data_left = -1; + connp->out_state = htp_connp_RES_FINALIZE; + } + + return HTP_OK; + } + + connp->out_tx->response_line = bstr_dup_mem(data, len); + if (connp->out_tx->response_line == NULL) return HTP_ERROR; + + if (connp->cfg->parse_response_line(connp) != HTP_OK) return HTP_ERROR; + + htp_status_t rc = htp_tx_state_response_line(connp->out_tx); + if (rc != HTP_OK) return rc; + + htp_connp_res_clear_buffer(connp); + + // Move on to the next phase. + connp->out_state = htp_connp_RES_HEADERS; + connp->out_tx->response_progress = HTP_RESPONSE_HEADERS; + + return HTP_OK; + } + } + + return HTP_ERROR; +} + +size_t htp_connp_res_data_consumed(htp_connp_t *connp) { + return connp->out_current_read_offset; +} + +htp_status_t htp_connp_RES_FINALIZE(htp_connp_t *connp) { + if (connp->out_status != HTP_STREAM_CLOSED) { + OUT_PEEK_NEXT(connp); + if (connp->out_next_byte == -1) { + return htp_tx_state_response_complete_ex(connp->out_tx, 0); + } + if (connp->out_next_byte != LF || connp->out_current_consume_offset >= connp->out_current_read_offset) { + for (;;) {//;i < max_read; i++) { + OUT_COPY_BYTE_OR_RETURN(connp); + // Have we reached the end of the line? For some reason + // we can't test after IN_COPY_BYTE_OR_RETURN */ + if (connp->out_next_byte == LF) + break; + } + } + } + size_t bytes_left; + unsigned char * data; + + if (htp_connp_res_consolidate_data(connp, &data, &bytes_left) != HTP_OK) { + return HTP_ERROR; + } +#ifdef HTP_DEBUG + fprint_raw_data(stderr, "PROBING response finalize", data, bytes_left); +#endif + if (bytes_left == 0) { + //closing + return htp_tx_state_response_complete_ex(connp->out_tx, 0); + } + + if (htp_treat_response_line_as_body(data, bytes_left)) { + // Interpret remaining bytes as body data + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Unexpected response body"); + htp_status_t rc = htp_tx_res_process_body_data_ex(connp->out_tx, data, bytes_left); + htp_connp_res_clear_buffer(connp); + return rc; + } + + //unread last end of line so that RES_LINE works + if (connp->out_current_read_offset < (int64_t)bytes_left) { + connp->out_current_read_offset=0; + } else { + connp->out_current_read_offset-=bytes_left; + } + if (connp->out_current_read_offset < connp->out_current_consume_offset) { + connp->out_current_consume_offset=connp->out_current_read_offset; + } + return htp_tx_state_response_complete_ex(connp->out_tx, 0 /* not hybrid mode */); +} + +/** + * The response idle state will initialize response processing, as well as + * finalize each transactions after we are done with it. + * + * @param[in] connp + * @returns HTP_OK on state change, HTP_ERROR on error, or HTP_DATA when more data is needed. + */ +htp_status_t htp_connp_RES_IDLE(htp_connp_t *connp) { + + // We want to start parsing the next response (and change + // the state from IDLE) only if there's at least one + // byte of data available. Otherwise we could be creating + // new structures even if there's no more data on the + // connection. + OUT_TEST_NEXT_BYTE_OR_RETURN(connp); + + // Parsing a new response + + // Find the next outgoing transaction + // If there is none, we just create one so that responses without + // request can still be processed. + connp->out_tx = htp_list_get(connp->conn->transactions, connp->out_next_tx_index); + if (connp->out_tx == NULL) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Unable to match response to request"); + // finalize dangling request waiting for next request or body + if (connp->in_state == htp_connp_REQ_FINALIZE) { + htp_tx_state_request_complete(connp->in_tx); + } + connp->out_tx = htp_connp_tx_create(connp); + if (connp->out_tx == NULL) { + return HTP_ERROR; + } + connp->out_tx->parsed_uri = htp_uri_alloc(); + if (connp->out_tx->parsed_uri == NULL) { + return HTP_ERROR; + } + connp->out_tx->parsed_uri->path = bstr_dup_c(REQUEST_URI_NOT_SEEN); + if (connp->out_tx->parsed_uri->path == NULL) { + return HTP_ERROR; + } + connp->out_tx->request_uri = bstr_dup_c(REQUEST_URI_NOT_SEEN); + if (connp->out_tx->request_uri == NULL) { + return HTP_ERROR; + } + + connp->in_state = htp_connp_REQ_FINALIZE; +#ifdef HTP_DEBUG + fprintf(stderr, "picked up response w/o request"); +#endif + // We've used one transaction + connp->out_next_tx_index++; + } else { + // We've used one transaction + connp->out_next_tx_index++; + + // TODO Detect state mismatch + + connp->out_content_length = -1; + connp->out_body_data_left = -1; + } + + htp_status_t rc = htp_tx_state_response_start(connp->out_tx); + if (rc != HTP_OK) return rc; + + return HTP_OK; +} + +int htp_connp_res_data(htp_connp_t *connp, const htp_time_t *timestamp, const void *data, size_t len) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data(connp->out_status %x)\n", connp->out_status); + fprint_raw_data(stderr, __func__, data, len); + #endif + + // Return if the connection is in stop state + if (connp->out_status == HTP_STREAM_STOP) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_INFO, 0, "Outbound parser is in HTP_STREAM_STOP"); + + return HTP_STREAM_STOP; + } + + // Return if the connection has had a fatal error + if (connp->out_status == HTP_STREAM_ERROR) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Outbound parser is in HTP_STREAM_ERROR"); + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_DATA (previous error)\n"); + #endif + + return HTP_STREAM_ERROR; + } + + // Sanity check: we must have a transaction pointer if the state is not IDLE (no outbound transaction) + if ((connp->out_tx == NULL)&&(connp->out_state != htp_connp_RES_IDLE)) { + connp->out_status = HTP_STREAM_ERROR; + + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Missing outbound transaction data"); + + return HTP_STREAM_ERROR; + } + + // If the length of the supplied data chunk is zero, proceed + // only if the stream has been closed. We do not allow zero-sized + // chunks in the API, but we use it internally to force the parsers + // to finalize parsing. + if (len == 0 && connp->out_status != HTP_STREAM_CLOSED) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Zero-length data chunks are not allowed"); + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_DATA (zero-length chunk)\n"); + #endif + + return HTP_STREAM_CLOSED; + } + + // Remember the timestamp of the current response data chunk + if (timestamp != NULL) { + memcpy(&connp->out_timestamp, timestamp, sizeof (*timestamp)); + } + + // Store the current chunk information + connp->out_current_data = (unsigned char *) data; + connp->out_current_len = len; + connp->out_current_read_offset = 0; + connp->out_current_consume_offset = 0; + connp->out_current_receiver_offset = 0; + + htp_conn_track_outbound_data(connp->conn, len, timestamp); + + // Return without processing any data if the stream is in tunneling + // mode (which it would be after an initial CONNECT transaction. + if (connp->out_status == HTP_STREAM_TUNNEL) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_TUNNEL\n"); + #endif + + return HTP_STREAM_TUNNEL; + } + + // Invoke a processor, in a loop, until an error + // occurs or until we run out of data. Many processors + // will process a request, each pointing to the next + // processor that needs to run. + for (;;) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: out state=%s, progress=%s\n", + htp_connp_out_state_as_string(connp), + htp_tx_response_progress_as_string(connp->out_tx)); + #endif + + // Return if there's been an error + // or if we've run out of data. We are relying + // on processors to add error messages, so we'll + // keep quiet here. + htp_status_t rc; + + //handle gap + if (data == NULL && len > 0) { + if (connp->out_state == htp_connp_RES_BODY_IDENTITY_CL_KNOWN || + connp->out_state == htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE) { + rc = connp->out_state(connp); + } else if (connp->out_state == htp_connp_RES_FINALIZE) { + rc = htp_tx_state_response_complete_ex(connp->out_tx, 0); + } else { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Gaps are not allowed during this state"); + return HTP_STREAM_CLOSED; + } + } else { + rc = connp->out_state(connp); + } + if (rc == HTP_OK) { + if (connp->out_status == HTP_STREAM_TUNNEL) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_TUNNEL\n"); + #endif + + return HTP_STREAM_TUNNEL; + } + + rc = htp_res_handle_state_change(connp); + } + + if (rc != HTP_OK) { + // Do we need more data? + if ((rc == HTP_DATA) || (rc == HTP_DATA_BUFFER)) { + htp_connp_res_receiver_send_data(connp, 0 /* not last */); + + if (rc == HTP_DATA_BUFFER) { + if (htp_connp_res_buffer(connp) != HTP_OK) { + connp->out_status = HTP_STREAM_ERROR; + return HTP_STREAM_ERROR; + } + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_DATA\n"); + #endif + + connp->out_status = HTP_STREAM_DATA; + + return HTP_STREAM_DATA; + } + + // Check for stop + if (rc == HTP_STOP) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_STOP\n"); + #endif + + connp->out_status = HTP_STREAM_STOP; + + return HTP_STREAM_STOP; + } + + // Check for suspended parsing + if (rc == HTP_DATA_OTHER) { + // We might have actually consumed the entire data chunk? + if (connp->out_current_read_offset >= connp->out_current_len) { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_DATA (suspended parsing)\n"); + #endif + + connp->out_status = HTP_STREAM_DATA; + + // Do not send STREAM_DATE_DATA_OTHER if we've + // consumed the entire chunk + return HTP_STREAM_DATA; + } else { + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_DATA_OTHER\n"); + #endif + + connp->out_status = HTP_STREAM_DATA_OTHER; + + // Partial chunk consumption + return HTP_STREAM_DATA_OTHER; + } + } + + #ifdef HTP_DEBUG + fprintf(stderr, "htp_connp_res_data: returning HTP_STREAM_ERROR\n"); + #endif + + // Permanent stream error. + connp->out_status = HTP_STREAM_ERROR; + + return HTP_STREAM_ERROR; + } + } +} diff --git a/htp/htp_response_generic.c b/htp/htp_response_generic.c new file mode 100644 index 0000000..f5fa59e --- /dev/null +++ b/htp/htp_response_generic.c @@ -0,0 +1,334 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Generic response line parser. + * + * @param[in] connp + * @return HTP status + */ +htp_status_t htp_parse_response_line_generic(htp_connp_t *connp) { + htp_tx_t *tx = connp->out_tx; + unsigned char *data = bstr_ptr(tx->response_line); + size_t len = bstr_len(tx->response_line); + size_t pos = 0; + + tx->response_protocol = NULL; + tx->response_protocol_number = HTP_PROTOCOL_INVALID; + tx->response_status = NULL; + tx->response_status_number = HTP_STATUS_INVALID; + tx->response_message = NULL; + + // Ignore whitespace at the beginning of the line. + while ((pos < len) && (htp_is_space(data[pos]))) pos++; + + size_t start = pos; + + // Find the end of the protocol string. + while ((pos < len) && (!htp_is_space(data[pos]))) pos++; + if (pos - start == 0) return HTP_OK; + + tx->response_protocol = bstr_dup_mem(data + start, pos - start); + if (tx->response_protocol == NULL) return HTP_ERROR; + + tx->response_protocol_number = htp_parse_protocol(tx->response_protocol); + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "Response protocol", bstr_ptr(tx->response_protocol), bstr_len(tx->response_protocol)); + fprintf(stderr, "Response protocol number: %d\n", tx->response_protocol_number); + #endif + + // Ignore whitespace after the response protocol. + while ((pos < len) && (htp_is_space(data[pos]))) pos++; + if (pos == len) return HTP_OK; + + start = pos; + + // Find the next whitespace character. + while ((pos < len) && (!htp_is_space(data[pos]))) pos++; + if (pos - start == 0) return HTP_OK; + + tx->response_status = bstr_dup_mem(data + start, pos - start); + if (tx->response_status == NULL) return HTP_ERROR; + + tx->response_status_number = htp_parse_status(tx->response_status); + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "Response status (as text)", bstr_ptr(tx->response_status), bstr_len(tx->response_status)); + fprintf(stderr, "Response status number: %d\n", tx->response_status_number); + #endif + + // Ignore whitespace that follows the status code. + while ((pos < len) && (isspace(data[pos]))) pos++; + if (pos == len) return HTP_OK; + + // Assume the message stretches until the end of the line. + tx->response_message = bstr_dup_mem(data + pos, len - pos); + if (tx->response_message == NULL) return HTP_ERROR; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "Response status message", bstr_ptr(tx->response_message), bstr_len(tx->response_message)); + #endif + + return HTP_OK; +} + +/** + * Generic response header parser. + * + * @param[in] connp + * @param[in] h + * @param[in] data + * @param[in] len + * @return HTP status + */ +htp_status_t htp_parse_response_header_generic(htp_connp_t *connp, htp_header_t *h, unsigned char *data, size_t len) { + size_t name_start, name_end; + size_t value_start, value_end; + size_t prev; + + htp_chomp(data, &len); + + name_start = 0; + + // Look for the first colon. + size_t colon_pos = 0; + while ((colon_pos < len) && (data[colon_pos] != ':')) colon_pos++; + + if (colon_pos == len) { + // Header line with a missing colon. + + h->flags |= HTP_FIELD_UNPARSEABLE; + h->flags |= HTP_FIELD_INVALID; + + if (!(connp->out_tx->flags & HTP_FIELD_UNPARSEABLE)) { + // Only once per transaction. + connp->out_tx->flags |= HTP_FIELD_UNPARSEABLE; + connp->out_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field invalid: missing colon."); + } + + // Reset the position. We're going to treat this invalid header + // as a header with an empty name. That will increase the probability + // that the content will be inspected. + colon_pos = 0; + (void)colon_pos; // suppress scan-build warning + name_end = 0; + value_start = 0; + } else { + // Header line with a colon. + + if (colon_pos == 0) { + // Empty header name. + + h->flags |= HTP_FIELD_INVALID; + + if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { + // Only once per transaction. + connp->out_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field invalid: empty name."); + } + } + + name_end = colon_pos; + + // Ignore unprintable after field-name. + prev = name_end; + while ((prev > name_start) && htp_is_space(data[prev - 1])) { + prev--; + name_end--; + + h->flags |= HTP_FIELD_INVALID; + + if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { + // Only once per transaction. + connp->out_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response field invalid: LWS after name."); + } + } + + value_start = colon_pos + 1; + } + + // Header value. + + // Ignore LWS before field-content. + while ((value_start < len) && (htp_is_lws(data[value_start]))) { + value_start++; + } + + // Look for the end of field-content. + value_end = len; + + // Check that the header name is a token. + size_t i = name_start; + while (i < name_end) { + if (!htp_is_token(data[i])) { + h->flags |= HTP_FIELD_INVALID; + + if (!(connp->out_tx->flags & HTP_FIELD_INVALID)) { + connp->out_tx->flags |= HTP_FIELD_INVALID; + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response header name is not a token."); + } + + break; + } + + i++; + } + for (i = value_start; i < value_end; i++) { + if (data[i] == 0) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Response header value contains null."); + break; + } + } + // Ignore LWS after field-content. + prev = value_end - 1; + while ((prev > value_start) && (htp_is_lws(data[prev]))) { + prev--; + value_end--; + } + + // Now extract the name and the value. + h->name = bstr_dup_mem(data + name_start, name_end - name_start); + h->value = bstr_dup_mem(data + value_start, value_end - value_start); + if ((h->name == NULL) || (h->value == NULL)) { + bstr_free(h->name); + bstr_free(h->value); + return HTP_ERROR; + } + + return HTP_OK; +} + +/** + * Generic response header line(s) processor, which assembles folded lines + * into a single buffer before invoking the parsing function. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return HTP status + */ +htp_status_t htp_process_response_header_generic(htp_connp_t *connp, unsigned char *data, size_t len) { + // Create a new header structure. + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return HTP_ERROR; + + if (htp_parse_response_header_generic(connp, h, data, len) != HTP_OK) { + free(h); + return HTP_ERROR; + } + + #ifdef HTP_DEBUG + fprint_bstr(stderr, "Header name", h->name); + fprint_bstr(stderr, "Header value", h->value); + #endif + + // Do we already have a header with the same name? + htp_header_t *h_existing = htp_table_get(connp->out_tx->response_headers, h->name); + if (h_existing != NULL) { + // Keep track of repeated same-name headers. + if ((h_existing->flags & HTP_FIELD_REPEATED) == 0) { + // This is the second occurence for this header. + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Repetition for header"); + } else { + // For simplicity reasons, we count the repetitions of all headers + if (connp->out_tx->res_header_repetitions < HTP_MAX_HEADERS_REPETITIONS) { + connp->out_tx->res_header_repetitions++; + } else { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_OK; + } + } + h_existing->flags |= HTP_FIELD_REPEATED; + + // Having multiple C-L headers is against the RFC but many + // browsers ignore the subsequent headers if the values are the same. + if (bstr_cmp_c_nocase(h->name, "Content-Length") == 0) { + // Don't use string comparison here because we want to + // ignore small formatting differences. + + int64_t existing_cl, new_cl; + + existing_cl = htp_parse_content_length(h_existing->value, NULL); + new_cl = htp_parse_content_length(h->value, NULL); + if ((existing_cl == -1) || (new_cl == -1) || (existing_cl != new_cl)) { + // Ambiguous response C-L value. + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Ambiguous response C-L value"); + } + + // Ignoring the new C-L header that has the same value as the previous ones. + } else { + // Add to the existing header. + + bstr *new_value = bstr_expand(h_existing->value, bstr_len(h_existing->value) + 2 + bstr_len(h->value)); + if (new_value == NULL) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + + h_existing->value = new_value; + bstr_add_mem_noex(h_existing->value, (unsigned char *) ", ", 2); + bstr_add_noex(h_existing->value, h->value); + } + + // The new header structure is no longer needed. + bstr_free(h->name); + bstr_free(h->value); + free(h); + } else { + // Add as a new header. + if (htp_table_add(connp->out_tx->response_headers, h->name, h) != HTP_OK) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + } + + return HTP_OK; +} diff --git a/htp/htp_table.c b/htp/htp_table.c new file mode 100644 index 0000000..535b961 --- /dev/null +++ b/htp/htp_table.c @@ -0,0 +1,250 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +static htp_status_t _htp_table_add(htp_table_t *table, const bstr *key, const void *element) { + // Add key. + if (htp_list_add(&table->list, (void *)key) != HTP_OK) return HTP_ERROR; + + // Add element. + if (htp_list_add(&table->list, (void *)element) != HTP_OK) { + htp_list_pop(&table->list); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_table_add(htp_table_t *table, const bstr *key, const void *element) { + if ((table == NULL)||(key == NULL)) return HTP_ERROR; + + // Keep track of how keys are allocated, and + // ensure that all invocations are consistent. + if (table->alloc_type == HTP_TABLE_KEYS_ALLOC_UKNOWN) { + table->alloc_type = HTP_TABLE_KEYS_COPIED; + } else { + if (table->alloc_type != HTP_TABLE_KEYS_COPIED) { + #ifdef HTP_DEBUG + fprintf(stderr, "# Inconsistent key management strategy. Actual %d. Attempted %d.\n", + table->alloc_type, HTP_TABLE_KEYS_COPIED); + #endif + + return HTP_ERROR; + } + } + + bstr *dupkey = bstr_dup(key); + if (dupkey == NULL) return HTP_ERROR; + + if (_htp_table_add(table, dupkey, element) != HTP_OK) { + bstr_free(dupkey); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_table_addn(htp_table_t *table, const bstr *key, const void *element) { + if ((table == NULL)||(key == NULL)) return HTP_ERROR; + + // Keep track of how keys are allocated, and + // ensure that all invocations are consistent. + if (table->alloc_type == HTP_TABLE_KEYS_ALLOC_UKNOWN) { + table->alloc_type = HTP_TABLE_KEYS_ADOPTED; + } else { + if (table->alloc_type != HTP_TABLE_KEYS_ADOPTED) { + #ifdef HTP_DEBUG + fprintf(stderr, "# Inconsistent key management strategy. Actual %d. Attempted %d.\n", + table->alloc_type, HTP_TABLE_KEYS_ADOPTED); + #endif + + return HTP_ERROR; + } + } + + return _htp_table_add(table, key, element); +} + +htp_status_t htp_table_addk(htp_table_t *table, const bstr *key, const void *element) { + if ((table == NULL)||(key == NULL)) return HTP_ERROR; + + // Keep track of how keys are allocated, and + // ensure that all invocations are consistent. + if (table->alloc_type == HTP_TABLE_KEYS_ALLOC_UKNOWN) { + table->alloc_type = HTP_TABLE_KEYS_REFERENCED; + } else { + if (table->alloc_type != HTP_TABLE_KEYS_REFERENCED) { + #ifdef HTP_DEBUG + fprintf(stderr, "# Inconsistent key management strategy. Actual %d. Attempted %d.\n", + table->alloc_type, HTP_TABLE_KEYS_REFERENCED); + #endif + + return HTP_ERROR; + } + } + + return _htp_table_add(table, key, element); +} + +void htp_table_clear(htp_table_t *table) { + if (table == NULL) return; + + // Free the table keys, but only if we're managing them. + if ((table->alloc_type == HTP_TABLE_KEYS_COPIED)||(table->alloc_type == HTP_TABLE_KEYS_ADOPTED)) { + bstr *key = NULL; + for (size_t i = 0, n = htp_list_size(&table->list); i < n; i += 2) { + key = htp_list_get(&table->list, i); + bstr_free(key); + } + } + + htp_list_clear(&table->list); +} + +void htp_table_clear_ex(htp_table_t *table) { + if (table == NULL) return; + + // This function does not free table keys. + + htp_list_clear(&table->list); +} + +htp_table_t *htp_table_create(size_t size) { + if (size == 0) return NULL; + + htp_table_t *table = calloc(1, sizeof (htp_table_t)); + if (table == NULL) return NULL; + + table->alloc_type = HTP_TABLE_KEYS_ALLOC_UKNOWN; + + // Use a list behind the scenes. + if (htp_list_init(&table->list, size * 2) == HTP_ERROR) { + free(table); + return NULL; + } + + return table; +} + +void htp_table_destroy(htp_table_t *table) { + if (table == NULL) return; + + htp_table_clear(table); + + htp_list_array_release(&table->list); + + free(table); +} + +void htp_table_destroy_ex(htp_table_t *table) { + if (table == NULL) return; + + // Change allocation strategy in order to + // prevent the keys from being freed. + table->alloc_type = HTP_TABLE_KEYS_REFERENCED; + + htp_table_destroy(table); +} + +void *htp_table_get(const htp_table_t *table, const bstr *key) { + if ((table == NULL)||(key == NULL)) return NULL; + + // Iterate through the list, comparing + // keys with the parameter, return data if found. + for (size_t i = 0, n = htp_list_size(&table->list); i < n; i += 2) { + bstr *key_candidate = htp_list_get(&table->list, i); + void *element = htp_list_get(&table->list, i + 1); + if (bstr_cmp_nocase(key_candidate, key) == 0) { + return element; + } + } + + return NULL; +} + +void *htp_table_get_c(const htp_table_t *table, const char *ckey) { + if ((table == NULL)||(ckey == NULL)) return NULL; + + // Iterate through the list, comparing + // keys with the parameter, return data if found. + for (size_t i = 0, n = htp_list_size(&table->list); i < n; i += 2) { + bstr *key_candidate = htp_list_get(&table->list, i); + void *element = htp_list_get(&table->list, i + 1); + if (bstr_cmp_c_nocasenorzero(key_candidate, ckey) == 0) { + return element; + } + } + + return NULL; +} + +void *htp_table_get_index(const htp_table_t *table, size_t idx, bstr **key) { + if (table == NULL) return NULL; + + if (idx >= htp_list_size(&table->list)) return NULL; + + if (key != NULL) { + *key = htp_list_get(&table->list, idx * 2); + } + + return htp_list_get(&table->list, (idx * 2) + 1); +} + +void *htp_table_get_mem(const htp_table_t *table, const void *key, size_t key_len) { + if ((table == NULL)||(key == NULL)) return NULL; + + // Iterate through the list, comparing + // keys with the parameter, return data if found. + for (size_t i = 0, n = htp_list_size(&table->list); i < n; i += 2) { + bstr *key_candidate = htp_list_get(&table->list, i); + void *element = htp_list_get(&table->list, i + 1); + if (bstr_cmp_mem_nocase(key_candidate, key, key_len) == 0) { + return element; + } + } + + return NULL; +} + +size_t htp_table_size(const htp_table_t *table) { + if (table == NULL) return 0; + return htp_list_size(&table->list) / 2; +} diff --git a/htp/htp_table.h b/htp/htp_table.h new file mode 100644 index 0000000..70a1e9b --- /dev/null +++ b/htp/htp_table.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_TABLE_H +#define HTP_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct htp_table_t htp_table_t; + +/** + * Add a new element to the table. The key will be copied, and the copy + * managed by the table. The table keeps a pointer to the element. It is the + * callers responsibility to ensure the pointer remains valid. + * + * @param[in] table + * @param[in] key + * @param[in] element + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_table_add(htp_table_t *table, const bstr *key, const void *element); + +/** + * Add a new element to the table. The key provided will be adopted and managed + * by the table. You should not keep a copy of the pointer to the key unless you're + * certain that the table will live longer that the copy. The table keeps a pointer + * to the element. It is the callers responsibility to ensure the pointer remains + * valid. + * + * @param[in] table + * @param[in] key + * @param[in] element + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_table_addn(htp_table_t *table, const bstr *key, const void *element); + +/** + * Add a new element to the table. The key provided will be only referenced and the + * caller remains responsible to keep it alive until after the table is destroyed. The + * table keeps a pointer to the element. It is the callers responsibility to ensure + * the pointer remains valid. + * + * @param[in] table + * @param[in] key + * @param[in] element + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_table_addk(htp_table_t *table, const bstr *key, const void *element); + +/** + * Remove all elements from the table. This function handles keys + * according to the active allocation strategy. If the elements need freeing, + * you need to free them before invoking this function. + * + * @param[in] table + */ +void htp_table_clear(htp_table_t *table); + +/** + * Remove all elements from the table without freeing any of the keys, even + * if the table is using an allocation strategy where keys belong to it. This + * function is useful if all the keys have been adopted by some other structure. + * + * @param[in] table + */ +void htp_table_clear_ex(htp_table_t *table); + +/** + * Create a new table structure. The table will grow automatically as needed, + * but you are required to provide a starting size. + * + * @param[in] size The starting size. + * @return Newly created table instance, or NULL on failure. + */ +htp_table_t *htp_table_create(size_t size); + +/** + * Destroy a table. This function handles the keys according to the active + * allocation strategy. If the elements need freeing, you need to free them + * before invoking this function. After the table has been destroyed, + * the pointer is set to NULL. + * + * @param[in] table + */ +void htp_table_destroy(htp_table_t *table); + +/** + * Destroy the given table, but don't free the keys. even if they are managed by + * the table. Use this method when the responsibility for the keys has been transferred + * elsewhere. After the table has been destroyed, the pointer is set to NULL. + * + * @param[in] table + */ +void htp_table_destroy_ex(htp_table_t *table); + +/** + * Retrieve the first element that matches the given bstr key. + * + * @param[in] table + * @param[in] key + * @return Matched element, or NULL if no elements match the key. + */ +void *htp_table_get(const htp_table_t *table, const bstr *key); + +/** + * Retrieve the first element that matches the given NUL-terminated key. + * + * @param[in] table + * @param[in] ckey + * @return Matched element, or NULL if no elements match the key. + */ +void *htp_table_get_c(const htp_table_t *table, const char *ckey); + +/** + * Retrieve key and element at the given index. + * + * @param[in] table + * @param[in] idx + * @param[in,out] key Pointer in which the key will be returned. Can be NULL. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +void *htp_table_get_index(const htp_table_t *table, size_t idx, bstr **key); + +/** + * Retrieve table key defined by the provided pointer and length. + * + * @param[in] table + * @param[in] key + * @param[in] key_len + * @return Matched element, or NULL if no elements match the key. + */ +void *htp_table_get_mem(const htp_table_t *table, const void *key, size_t key_len); + +/** + * Return the size of the table. + * + * @param[in] table + * @return table size + */ +size_t htp_table_size(const htp_table_t *table); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_TABLE_H */ + diff --git a/htp/htp_table_private.h b/htp/htp_table_private.h new file mode 100644 index 0000000..273ec99 --- /dev/null +++ b/htp/htp_table_private.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_TABLE_PRIVATE_H +#define HTP_TABLE_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp_list.h" +#include "htp_table.h" + +enum htp_table_alloc_t { + /** This is the default value, used only until the first element is added. */ + HTP_TABLE_KEYS_ALLOC_UKNOWN = 0, + + /** Keys are copied.*/ + HTP_TABLE_KEYS_COPIED = 1, + + /** Keys are adopted and freed when the table is destroyed. */ + HTP_TABLE_KEYS_ADOPTED = 2, + + /** Keys are only referenced; the caller is still responsible for freeing them after the table is destroyed. */ + HTP_TABLE_KEYS_REFERENCED = 3 +}; + +struct htp_table_t { + /** Table key and value pairs are stored in this list; name first, then value. */ + htp_list_t list; + + /** + * Key management strategy. Initially set to HTP_TABLE_KEYS_ALLOC_UKNOWN. The + * actual strategy is determined by the first allocation. + */ + enum htp_table_alloc_t alloc_type; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_TABLE_PRIVATE_H */ diff --git a/htp/htp_transaction.c b/htp/htp_transaction.c new file mode 100644 index 0000000..7220459 --- /dev/null +++ b/htp/htp_transaction.c @@ -0,0 +1,1558 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +static void htp_tx_req_destroy_decompressors(htp_connp_t *connp); +static htp_status_t htp_tx_req_process_body_data_decompressor_callback(htp_tx_data_t *d); + +static bstr *copy_or_wrap_mem(const void *data, size_t len, enum htp_alloc_strategy_t alloc) { + if (data == NULL) return NULL; + + if (alloc == HTP_ALLOC_REUSE) { + return bstr_wrap_mem(data, len); + } else { + return bstr_dup_mem(data, len); + } +} + +htp_tx_t *htp_tx_create(htp_connp_t *connp) { + if (connp == NULL) return NULL; + + htp_tx_t *tx = calloc(1, sizeof (htp_tx_t)); + if (tx == NULL) return NULL; + + tx->connp = connp; + tx->conn = connp->conn; + tx->index = htp_list_size(tx->conn->transactions); + tx->cfg = connp->cfg; + tx->is_config_shared = HTP_CONFIG_SHARED; + + // Request fields. + + tx->request_progress = HTP_REQUEST_NOT_STARTED; + tx->request_protocol_number = HTP_PROTOCOL_UNKNOWN; + tx->request_content_length = -1; + + tx->parsed_uri_raw = htp_uri_alloc(); + if (tx->parsed_uri_raw == NULL) { + htp_tx_destroy_incomplete(tx); + return NULL; + } + + tx->request_headers = htp_table_create(32); + if (tx->request_headers == NULL) { + htp_tx_destroy_incomplete(tx); + return NULL; + } + + tx->request_params = htp_table_create(32); + if (tx->request_params == NULL) { + htp_tx_destroy_incomplete(tx); + return NULL; + } + + // Response fields. + + tx->response_progress = HTP_RESPONSE_NOT_STARTED; + tx->response_status = NULL; + tx->response_status_number = HTP_STATUS_UNKNOWN; + tx->response_protocol_number = HTP_PROTOCOL_UNKNOWN; + tx->response_content_length = -1; + + tx->response_headers = htp_table_create(32); + if (tx->response_headers == NULL) { + htp_tx_destroy_incomplete(tx); + return NULL; + } + + htp_list_add(tx->conn->transactions, tx); + + return tx; +} + +htp_status_t htp_tx_destroy(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + if (!htp_tx_is_complete(tx)) return HTP_ERROR; + + htp_tx_destroy_incomplete(tx); + + return HTP_OK; +} + +void htp_tx_destroy_incomplete(htp_tx_t *tx) { + if (tx == NULL) return; + + // Disconnect transaction from other structures. + htp_conn_remove_tx(tx->conn, tx); + htp_connp_tx_remove(tx->connp, tx); + + // Request fields. + + bstr_free(tx->request_line); + bstr_free(tx->request_method); + bstr_free(tx->request_uri); + bstr_free(tx->request_protocol); + bstr_free(tx->request_content_type); + bstr_free(tx->request_hostname); + htp_uri_free(tx->parsed_uri_raw); + htp_uri_free(tx->parsed_uri); + bstr_free(tx->request_auth_username); + bstr_free(tx->request_auth_password); + + // Request_headers. + if (tx->request_headers != NULL) { + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(tx->request_headers); i < n; i++) { + h = htp_table_get_index(tx->request_headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_destroy(tx->request_headers); + } + + // Request parsers. + + htp_urlenp_destroy(tx->request_urlenp_query); + htp_urlenp_destroy(tx->request_urlenp_body); + htp_mpartp_destroy(tx->request_mpartp); + + // Request parameters. + + htp_param_t *param = NULL; + for (size_t i = 0, n = htp_table_size(tx->request_params); i < n; i++) { + param = htp_table_get_index(tx->request_params, i, NULL); + bstr_free(param->name); + bstr_free(param->value); + free(param); + } + + htp_table_destroy(tx->request_params); + + // Request cookies. + + if (tx->request_cookies != NULL) { + bstr *b = NULL; + for (size_t i = 0, n = htp_table_size(tx->request_cookies); i < n; i++) { + b = htp_table_get_index(tx->request_cookies, i, NULL); + bstr_free(b); + } + + htp_table_destroy(tx->request_cookies); + } + + htp_hook_destroy(tx->hook_request_body_data); + + // Response fields. + + bstr_free(tx->response_line); + bstr_free(tx->response_protocol); + bstr_free(tx->response_status); + bstr_free(tx->response_message); + bstr_free(tx->response_content_type); + + // Destroy response headers. + if (tx->response_headers != NULL) { + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(tx->response_headers); i < n; i++) { + h = htp_table_get_index(tx->response_headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_destroy(tx->response_headers); + } + + // If we're using a private configuration structure, destroy it. + if (tx->is_config_shared == HTP_CONFIG_PRIVATE) { + htp_config_destroy(tx->cfg); + } + + free(tx); +} + +int htp_tx_get_is_config_shared(const htp_tx_t *tx) { + if (tx == NULL) return -1; + return tx->is_config_shared; +} + +void *htp_tx_get_user_data(const htp_tx_t *tx) { + if (tx == NULL) return NULL; + return tx->user_data; +} + +void htp_tx_set_config(htp_tx_t *tx, htp_cfg_t *cfg, int is_cfg_shared) { + if ((tx == NULL) || (cfg == NULL)) return; + + if ((is_cfg_shared != HTP_CONFIG_PRIVATE) && (is_cfg_shared != HTP_CONFIG_SHARED)) return; + + // If we're using a private configuration, destroy it. + if (tx->is_config_shared == HTP_CONFIG_PRIVATE) { + htp_config_destroy(tx->cfg); + } + + tx->cfg = cfg; + tx->is_config_shared = is_cfg_shared; +} + +void htp_tx_set_user_data(htp_tx_t *tx, void *user_data) { + if (tx == NULL) return; + tx->user_data = user_data; +} + +htp_status_t htp_tx_req_add_param(htp_tx_t *tx, htp_param_t *param) { + if ((tx == NULL) || (param == NULL)) return HTP_ERROR; + + if (tx->cfg->parameter_processor != NULL) { + if (tx->cfg->parameter_processor(param) != HTP_OK) return HTP_ERROR; + } + + return htp_table_addk(tx->request_params, param->name, param); +} + +htp_param_t *htp_tx_req_get_param(htp_tx_t *tx, const char *name, size_t name_len) { + if ((tx == NULL) || (name == NULL)) return NULL; + return htp_table_get_mem(tx->request_params, name, name_len); +} + +htp_param_t *htp_tx_req_get_param_ex(htp_tx_t *tx, enum htp_data_source_t source, const char *name, size_t name_len) { + if ((tx == NULL) || (name == NULL)) return NULL; + + htp_param_t *p = NULL; + + for (size_t i = 0, n = htp_table_size(tx->request_params); i < n; i++) { + p = htp_table_get_index(tx->request_params, i, NULL); + if (p->source != source) continue; + + if (bstr_cmp_mem_nocase(p->name, name, name_len) == 0) return p; + } + + return NULL; +} + +int htp_tx_req_has_body(const htp_tx_t *tx) { + if (tx == NULL) return -1; + + if ((tx->request_transfer_coding == HTP_CODING_IDENTITY) || (tx->request_transfer_coding == HTP_CODING_CHUNKED)) { + return 1; + } + + return 0; +} + +htp_status_t htp_tx_req_set_header(htp_tx_t *tx, const char *name, size_t name_len, + const char *value, size_t value_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (name == NULL) || (value == NULL)) return HTP_ERROR; + + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return HTP_ERROR; + + h->name = copy_or_wrap_mem(name, name_len, alloc); + if (h->name == NULL) { + free(h); + return HTP_ERROR; + } + + h->value = copy_or_wrap_mem(value, value_len, alloc); + if (h->value == NULL) { + bstr_free(h->name); + free(h); + return HTP_ERROR; + } + + if (htp_table_add(tx->request_headers, h->name, h) != HTP_OK) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_req_set_method(htp_tx_t *tx, const char *method, size_t method_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (method == NULL)) return HTP_ERROR; + + tx->request_method = copy_or_wrap_mem(method, method_len, alloc); + if (tx->request_method == NULL) return HTP_ERROR; + + return HTP_OK; +} + +void htp_tx_req_set_method_number(htp_tx_t *tx, enum htp_method_t method_number) { + if (tx == NULL) return; + tx->request_method_number = method_number; +} + +htp_status_t htp_tx_req_set_uri(htp_tx_t *tx, const char *uri, size_t uri_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (uri == NULL)) return HTP_ERROR; + + tx->request_uri = copy_or_wrap_mem(uri, uri_len, alloc); + if (tx->request_uri == NULL) return HTP_ERROR; + + return HTP_OK; +} + +htp_status_t htp_tx_req_set_protocol(htp_tx_t *tx, const char *protocol, size_t protocol_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (protocol == NULL)) return HTP_ERROR; + + tx->request_protocol = copy_or_wrap_mem(protocol, protocol_len, alloc); + if (tx->request_protocol == NULL) return HTP_ERROR; + + return HTP_OK; +} + +void htp_tx_req_set_protocol_number(htp_tx_t *tx, int protocol_number) { + if (tx == NULL) return; + tx->request_protocol_number = protocol_number; +} + +void htp_tx_req_set_protocol_0_9(htp_tx_t *tx, int is_protocol_0_9) { + if (tx == NULL) return; + + if (is_protocol_0_9) { + tx->is_protocol_0_9 = 1; + } else { + tx->is_protocol_0_9 = 0; + } +} + +static htp_status_t htp_tx_process_request_headers(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // Determine if we have a request body, and how it is packaged. + + htp_status_t rc = HTP_OK; + + if (tx->connp->cfg->request_decompression_enabled) { + tx->request_content_encoding = HTP_COMPRESSION_NONE; + htp_header_t *ce = htp_table_get_c(tx->request_headers, "content-encoding"); + if (ce != NULL) { + /* fast paths: regular gzip and friends */ + if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) || + (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) { + tx->request_content_encoding = HTP_COMPRESSION_GZIP; + } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) || + (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) { + tx->request_content_encoding = HTP_COMPRESSION_DEFLATE; + } else if (bstr_cmp_c_nocasenorzero(ce->value, "lzma") == 0) { + tx->request_content_encoding = HTP_COMPRESSION_LZMA; + } + //ignore other cases such as inflate, ot multiple layers + if ((tx->request_content_encoding != HTP_COMPRESSION_NONE)) + { + if (tx->connp->req_decompressor != NULL) { + htp_tx_req_destroy_decompressors(tx->connp); + } + tx->connp->req_decompressor = htp_gzip_decompressor_create(tx->connp, tx->request_content_encoding); + if (tx->connp->req_decompressor == NULL) + return HTP_ERROR; + + tx->connp->req_decompressor->callback = htp_tx_req_process_body_data_decompressor_callback; + } + } + } + + htp_header_t *cl = htp_table_get_c(tx->request_headers, "content-length"); + htp_header_t *te = htp_table_get_c(tx->request_headers, "transfer-encoding"); + + // Check for the Transfer-Encoding header, which would indicate a chunked request body. + if (te != NULL) { + // Make sure it contains "chunked" only. + // TODO The HTTP/1.1 RFC also allows the T-E header to contain "identity", which + // presumably should have the same effect as T-E header absence. However, Apache + // (2.2.22 on Ubuntu 12.04 LTS) instead errors out with "Unknown Transfer-Encoding: identity". + // And it behaves strangely, too, sending a 501 and proceeding to process the request + // (e.g., PHP is run), but without the body. It then closes the connection. + if (htp_header_has_token(bstr_ptr(te->value), bstr_len(te->value), (unsigned char*) "chunked") != HTP_OK) { + // Invalid T-E header value. + tx->request_transfer_coding = HTP_CODING_INVALID; + tx->flags |= HTP_REQUEST_INVALID_T_E; + tx->flags |= HTP_REQUEST_INVALID; + } else { + // Chunked encoding is a HTTP/1.1 feature, so check that an earlier protocol + // version is not used. The flag will also be set if the protocol could not be parsed. + // + // TODO IIS 7.0, for example, would ignore the T-E header when it + // it is used with a protocol below HTTP 1.1. This should be a + // personality trait. + if (tx->request_protocol_number < HTP_PROTOCOL_1_1) { + tx->flags |= HTP_REQUEST_INVALID_T_E; + tx->flags |= HTP_REQUEST_SMUGGLING; + } + + // If the T-E header is present we are going to use it. + tx->request_transfer_coding = HTP_CODING_CHUNKED; + + // We are still going to check for the presence of C-L. + if (cl != NULL) { + // According to the HTTP/1.1 RFC (section 4.4): + // + // "The Content-Length header field MUST NOT be sent + // if these two lengths are different (i.e., if a Transfer-Encoding + // header field is present). If a message is received with both a + // Transfer-Encoding header field and a Content-Length header field, + // the latter MUST be ignored." + // + tx->flags |= HTP_REQUEST_SMUGGLING; + } + } + } else if (cl != NULL) { + // Check for a folded C-L header. + if (cl->flags & HTP_FIELD_FOLDED) { + tx->flags |= HTP_REQUEST_SMUGGLING; + } + + // Check for multiple C-L headers. + if (cl->flags & HTP_FIELD_REPEATED) { + tx->flags |= HTP_REQUEST_SMUGGLING; + // TODO Personality trait to determine which C-L header to parse. + // At the moment we're parsing the combination of all instances, + // which is bound to fail (because it will contain commas). + } + + // Get the body length. + tx->request_content_length = htp_parse_content_length(cl->value, tx->connp); + if (tx->request_content_length < 0) { + tx->request_transfer_coding = HTP_CODING_INVALID; + tx->flags |= HTP_REQUEST_INVALID_C_L; + tx->flags |= HTP_REQUEST_INVALID; + } else { + // We have a request body of known length. + tx->request_transfer_coding = HTP_CODING_IDENTITY; + } + } else { + // No body. + tx->request_transfer_coding = HTP_CODING_NO_BODY; + } + + // If we could not determine the correct body handling, + // consider the request invalid. + if (tx->request_transfer_coding == HTP_CODING_UNKNOWN) { + tx->request_transfer_coding = HTP_CODING_INVALID; + tx->flags |= HTP_REQUEST_INVALID; + } + + // Check for PUT requests, which we need to treat as file uploads. + if (tx->request_method_number == HTP_M_PUT) { + if (htp_tx_req_has_body(tx)) { + // Prepare to treat PUT request body as a file. + + tx->connp->put_file = calloc(1, sizeof (htp_file_t)); + if (tx->connp->put_file == NULL) return HTP_ERROR; + + tx->connp->put_file->fd = -1; + tx->connp->put_file->source = HTP_FILE_PUT; + } else { + // TODO Warn about PUT request without a body. + } + } + + // Determine hostname. + + // Use the hostname from the URI, when available. + if (tx->parsed_uri->hostname != NULL) { + tx->request_hostname = bstr_dup(tx->parsed_uri->hostname); + if (tx->request_hostname == NULL) return HTP_ERROR; + } + + tx->request_port_number = tx->parsed_uri->port_number; + + // Examine the Host header. + + htp_header_t *h = htp_table_get_c(tx->request_headers, "host"); + if (h == NULL) { + // No host information in the headers. + + // HTTP/1.1 requires host information in the headers. + if (tx->request_protocol_number >= HTP_PROTOCOL_1_1) { + tx->flags |= HTP_HOST_MISSING; + } + } else { + // Host information available in the headers. + + bstr *hostname; + int port; + + rc = htp_parse_header_hostport(h->value, &hostname, NULL, &port, &(tx->flags)); + if (rc != HTP_OK) return rc; + + if (hostname != NULL) { + // The host information in the headers is valid. + + // Is there host information in the URI? + if (tx->request_hostname == NULL) { + // There is no host information in the URI. Place the + // hostname from the headers into the parsed_uri structure. + tx->request_hostname = hostname; + tx->request_port_number = port; + } else { + // The host information appears in the URI and in the headers. The + // HTTP RFC states that we should ignore the header copy. + + // Check for different hostnames. + if (bstr_cmp_nocase(hostname, tx->request_hostname) != 0) { + tx->flags |= HTP_HOST_AMBIGUOUS; + } + + // Check for different ports. + if (((tx->request_port_number != -1)&&(port != -1))&&(tx->request_port_number != port)) { + tx->flags |= HTP_HOST_AMBIGUOUS; + } + + bstr_free(hostname); + } + } else { + // Invalid host information in the headers. + + if (tx->request_hostname != NULL) { + // Raise the flag, even though the host information in the headers is invalid. + tx->flags |= HTP_HOST_AMBIGUOUS; + } + } + } + + // Determine Content-Type. + htp_header_t *ct = htp_table_get_c(tx->request_headers, "content-type"); + if (ct != NULL) { + rc = htp_parse_ct_header(ct->value, &tx->request_content_type); + if (rc != HTP_OK) return rc; + } + + // Parse cookies. + if (tx->connp->cfg->parse_request_cookies) { + rc = htp_parse_cookies_v0(tx->connp); + if (rc != HTP_OK) return rc; + } + + // Parse authentication information. + if (tx->connp->cfg->parse_request_auth) { + rc = htp_parse_authorization(tx->connp); + if (rc == HTP_DECLINED) { + // Don't fail the stream if an authorization header is invalid, just set a flag. + tx->flags |= HTP_AUTH_INVALID; + } else { + if (rc != HTP_OK) return rc; + } + } + + // Finalize sending raw header data. + rc = htp_connp_req_receiver_finalize_clear(tx->connp); + if (rc != HTP_OK) return rc; + + // Run hook REQUEST_HEADERS. + rc = htp_hook_run_all(tx->connp->cfg->hook_request_headers, tx); + if (rc != HTP_OK) return rc; + + // We still proceed if the request is invalid. + + return HTP_OK; +} + +htp_status_t htp_tx_req_process_body_data(htp_tx_t *tx, const void *data, size_t len) { + if ((tx == NULL) || (data == NULL)) return HTP_ERROR; + if (len == 0) return HTP_OK; + + return htp_tx_req_process_body_data_ex(tx, data, len); +} + +htp_status_t htp_tx_req_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) { + if (tx == NULL) return HTP_ERROR; + + // NULL data is allowed in this private function; it's + // used to indicate the end of request body. + + // Send data to the callbacks. + + htp_tx_data_t d; + d.tx = tx; + d.data = (unsigned char *) data; + d.len = len; + d.is_last = (data == NULL && len == 0); + + switch(tx->request_content_encoding) { + case HTP_COMPRESSION_UNKNOWN: + case HTP_COMPRESSION_NONE: + // When there's no decompression, request_entity_len. + // is identical to request_message_len. + tx->request_entity_len += d.len; + htp_status_t rc = htp_req_run_hook_body_data(tx->connp, &d); + if (rc != HTP_OK) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request body data callback returned error (%d)", rc); + return HTP_ERROR; + } + break; + + case HTP_COMPRESSION_GZIP: + case HTP_COMPRESSION_DEFLATE: + case HTP_COMPRESSION_LZMA: + // In severe memory stress these could be NULL + if (tx->connp->req_decompressor == NULL) + return HTP_ERROR; + + // Send data buffer to the decompressor. + htp_gzip_decompressor_decompress(tx->connp->req_decompressor, &d); + + if (data == NULL) { + // Shut down the decompressor, if we used one. + htp_tx_req_destroy_decompressors(tx->connp); + } + break; + + default: + // Internal error. + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "[Internal Error] Invalid tx->request_content_encoding value: %d", + tx->request_content_encoding); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_req_set_headers_clear(htp_tx_t *tx) { + if ((tx == NULL) || (tx->request_headers == NULL)) return HTP_ERROR; + + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(tx->request_headers); i < n; i++) { + h = htp_table_get_index(tx->request_headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_destroy(tx->request_headers); + + tx->request_headers = htp_table_create(32); + if (tx->request_headers == NULL) return HTP_ERROR; + + return HTP_OK; +} + +htp_status_t htp_tx_req_set_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (line == NULL) || (line_len == 0)) return HTP_ERROR; + + tx->request_line = copy_or_wrap_mem(line, line_len, alloc); + if (tx->request_line == NULL) return HTP_ERROR; + + if (tx->connp->cfg->parse_request_line(tx->connp) != HTP_OK) return HTP_ERROR; + + return HTP_OK; +} + +void htp_tx_req_set_parsed_uri(htp_tx_t *tx, htp_uri_t *parsed_uri) { + if ((tx == NULL) || (parsed_uri == NULL)) return; + + if (tx->parsed_uri != NULL) { + htp_uri_free(tx->parsed_uri); + } + + tx->parsed_uri = parsed_uri; +} + +htp_status_t htp_tx_res_set_status_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (line == NULL) || (line_len == 0)) return HTP_ERROR; + + tx->response_line = copy_or_wrap_mem(line, line_len, alloc); + if (tx->response_line == NULL) return HTP_ERROR; + + if (tx->connp->cfg->parse_response_line(tx->connp) != HTP_OK) return HTP_ERROR; + + return HTP_OK; +} + +void htp_tx_res_set_protocol_number(htp_tx_t *tx, int protocol_number) { + if (tx == NULL) return; + tx->response_protocol_number = protocol_number; +} + +void htp_tx_res_set_status_code(htp_tx_t *tx, int status_code) { + if (tx == NULL) return; + tx->response_status_number = status_code; +} + +htp_status_t htp_tx_res_set_status_message(htp_tx_t *tx, const char *msg, size_t msg_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (msg == NULL)) return HTP_ERROR; + + if (tx->response_message != NULL) { + bstr_free(tx->response_message); + } + + tx->response_message = copy_or_wrap_mem(msg, msg_len, alloc); + if (tx->response_message == NULL) return HTP_ERROR; + + return HTP_OK; +} + +htp_status_t htp_tx_state_response_line(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + #if 0 + // Commented-out until we determine which fields can be + // unavailable in real-life. + + // Unless we're dealing with HTTP/0.9, check that + // the minimum amount of data has been provided. + if (tx->is_protocol_0_9 != 0) { + if ((tx->response_protocol == NULL) || (tx->response_status_number == -1) || (tx->response_message == NULL)) { + return HTP_ERROR; + } + } + #endif + + // Is the response line valid? + if (tx->response_protocol_number == HTP_PROTOCOL_INVALID) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Invalid response line: invalid protocol"); + tx->flags |= HTP_STATUS_LINE_INVALID; + } + if ((tx->response_status_number == HTP_STATUS_INVALID) + || (tx->response_status_number < HTP_VALID_STATUS_MIN) + || (tx->response_status_number > HTP_VALID_STATUS_MAX)) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Invalid response line: invalid response status %d.", + tx->response_status_number); + tx->response_status_number = HTP_STATUS_INVALID; + tx->flags |= HTP_STATUS_LINE_INVALID; + } + + // Run hook HTP_RESPONSE_LINE + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_line, tx); + if (rc != HTP_OK) return rc; + + return HTP_OK; +} + +htp_status_t htp_tx_res_set_header(htp_tx_t *tx, const char *name, size_t name_len, + const char *value, size_t value_len, enum htp_alloc_strategy_t alloc) { + if ((tx == NULL) || (name == NULL) || (value == NULL)) return HTP_ERROR; + + + htp_header_t *h = calloc(1, sizeof (htp_header_t)); + if (h == NULL) return HTP_ERROR; + + h->name = copy_or_wrap_mem(name, name_len, alloc); + if (h->name == NULL) { + free(h); + return HTP_ERROR; + } + + h->value = copy_or_wrap_mem(value, value_len, alloc); + if (h->value == NULL) { + bstr_free(h->name); + free(h); + return HTP_ERROR; + } + + if (htp_table_add(tx->response_headers, h->name, h) != HTP_OK) { + bstr_free(h->name); + bstr_free(h->value); + free(h); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_res_set_headers_clear(htp_tx_t *tx) { + if ((tx == NULL) || (tx->response_headers == NULL)) return HTP_ERROR; + + htp_header_t *h = NULL; + for (size_t i = 0, n = htp_table_size(tx->response_headers); i < n; i++) { + h = htp_table_get_index(tx->response_headers, i, NULL); + bstr_free(h->name); + bstr_free(h->value); + free(h); + } + + htp_table_destroy(tx->response_headers); + + tx->response_headers = htp_table_create(32); + if (tx->response_headers == NULL) return HTP_ERROR; + + return HTP_OK; +} + +/** \internal + * + * Clean up decompressor(s). + * + * @param[in] tx + */ +static void htp_tx_res_destroy_decompressors(htp_connp_t *connp) { + htp_decompressor_t *comp = connp->out_decompressor; + while (comp) { + htp_decompressor_t *next = comp->next; + htp_gzip_decompressor_destroy(comp); + comp = next; + } + connp->out_decompressor = NULL; +} + +static void htp_tx_req_destroy_decompressors(htp_connp_t *connp) { + htp_decompressor_t *comp = connp->req_decompressor; + while (comp) { + htp_decompressor_t *next = comp->next; + htp_gzip_decompressor_destroy(comp); + comp = next; + } + connp->req_decompressor = NULL; +} + +void htp_connp_destroy_decompressors(htp_connp_t *connp) { + htp_tx_res_destroy_decompressors(connp); + htp_tx_req_destroy_decompressors(connp); +} + +static htp_status_t htp_timer_track(int32_t *time_spent, struct timeval * after, struct timeval *before) { + if (after->tv_sec < before->tv_sec) { + return HTP_ERROR; + } else if (after->tv_sec == before->tv_sec) { + if (after->tv_usec < before->tv_usec) { + return HTP_ERROR; + } + *time_spent += after->tv_usec - before->tv_usec; + } else { + *time_spent += (after->tv_sec - before->tv_sec) * 1000000 + after->tv_usec - before->tv_usec; + } + return HTP_OK; +} + +static htp_status_t htp_tx_req_process_body_data_decompressor_callback(htp_tx_data_t *d) { + if (d == NULL) return HTP_ERROR; + + #if HTP_DEBUG + fprint_raw_data(stderr, __func__, d->data, d->len); + #endif + + // Keep track of actual request body length. + d->tx->request_entity_len += d->len; + + // Invoke all callbacks. + htp_status_t rc = htp_req_run_hook_body_data(d->tx->connp, d); + if (rc != HTP_OK) return HTP_ERROR; + d->tx->connp->req_decompressor->nb_callbacks++; + if (d->tx->connp->req_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) { + struct timeval after; + gettimeofday(&after, NULL); + // sanity check for race condition if system time changed + if ( htp_timer_track(&d->tx->connp->req_decompressor->time_spent, &after, &d->tx->connp->req_decompressor->time_before) == HTP_OK) { + // updates last tracked time + d->tx->connp->req_decompressor->time_before = after; + if (d->tx->connp->req_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: spent %"PRId32" us decompressing", + d->tx->connp->req_decompressor->time_spent); + d->tx->connp->req_decompressor->passthrough = 1; + } + } + + } + if (d->tx->request_entity_len > d->tx->connp->cfg->compression_bomb_limit && + d->tx->request_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->request_message_len) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: decompressed %"PRId64" bytes out of %"PRId64, + d->tx->request_entity_len, d->tx->request_message_len); + return HTP_ERROR; + } + + return HTP_OK; +} + +static htp_status_t htp_tx_res_process_body_data_decompressor_callback(htp_tx_data_t *d) { + if (d == NULL) return HTP_ERROR; + + #if HTP_DEBUG + fprint_raw_data(stderr, __func__, d->data, d->len); + #endif + + // Keep track of actual response body length. + d->tx->response_entity_len += d->len; + + // Invoke all callbacks. + htp_status_t rc = htp_res_run_hook_body_data(d->tx->connp, d); + if (rc != HTP_OK) return HTP_ERROR; + d->tx->connp->out_decompressor->nb_callbacks++; + if (d->tx->connp->out_decompressor->nb_callbacks % HTP_COMPRESSION_TIME_FREQ_TEST == 0) { + struct timeval after; + gettimeofday(&after, NULL); + // sanity check for race condition if system time changed + if ( htp_timer_track(&d->tx->connp->out_decompressor->time_spent, &after, &d->tx->connp->out_decompressor->time_before) == HTP_OK) { + // updates last tracked time + d->tx->connp->out_decompressor->time_before = after; + if (d->tx->connp->out_decompressor->time_spent > d->tx->connp->cfg->compression_time_limit ) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: spent %"PRId32" us decompressing", + d->tx->connp->out_decompressor->time_spent); + d->tx->connp->out_decompressor->passthrough = 1; + } + } + + } + if (d->tx->response_entity_len > d->tx->connp->cfg->compression_bomb_limit && + d->tx->response_entity_len > HTP_COMPRESSION_BOMB_RATIO * d->tx->response_message_len) { + htp_log(d->tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: decompressed %"PRId64" bytes out of %"PRId64, + d->tx->response_entity_len, d->tx->response_message_len); + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_res_process_body_data(htp_tx_t *tx, const void *data, size_t len) { + if ((tx == NULL) || (data == NULL)) return HTP_ERROR; + if (len == 0) return HTP_OK; + return htp_tx_res_process_body_data_ex(tx, data, len); +} + +htp_status_t htp_tx_res_process_body_data_ex(htp_tx_t *tx, const void *data, size_t len) { + if (tx == NULL) return HTP_ERROR; + + // NULL data is allowed in this private function; it's + // used to indicate the end of response body. + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, __func__, data, len); + #endif + + htp_tx_data_t d; + + d.tx = tx; + d.data = (unsigned char *) data; + d.len = len; + d.is_last = 0; + + // Keep track of body size before decompression. + tx->response_message_len += d.len; + + switch (tx->response_content_encoding_processing) { + case HTP_COMPRESSION_GZIP: + case HTP_COMPRESSION_DEFLATE: + case HTP_COMPRESSION_LZMA: + // In severe memory stress these could be NULL + if (tx->connp->out_decompressor == NULL) + return HTP_ERROR; + + struct timeval after; + gettimeofday(&tx->connp->out_decompressor->time_before, NULL); + // Send data buffer to the decompressor. + tx->connp->out_decompressor->nb_callbacks=0; + htp_gzip_decompressor_decompress(tx->connp->out_decompressor, &d); + gettimeofday(&after, NULL); + // sanity check for race condition if system time changed + if ( htp_timer_track(&tx->connp->out_decompressor->time_spent, &after, &tx->connp->out_decompressor->time_before) == HTP_OK) { + if ( tx->connp->out_decompressor->time_spent > tx->connp->cfg->compression_time_limit ) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: spent %"PRId32" us decompressing", + tx->connp->out_decompressor->time_spent); + tx->connp->out_decompressor->passthrough = 1; + } + } + + if (data == NULL) { + // Shut down the decompressor, if we used one. + htp_tx_res_destroy_decompressors(tx->connp); + } + break; + + case HTP_COMPRESSION_NONE: + // When there's no decompression, response_entity_len. + // is identical to response_message_len. + tx->response_entity_len += d.len; + + htp_status_t rc = htp_res_run_hook_body_data(tx->connp, &d); + if (rc != HTP_OK) return HTP_ERROR; + break; + + default: + // Internal error. + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "[Internal Error] Invalid tx->response_content_encoding_processing value: %d", + tx->response_content_encoding_processing); + return HTP_ERROR; + break; + } + + return HTP_OK; +} + +htp_status_t htp_tx_state_request_complete_partial(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // Finalize request body. + if (htp_tx_req_has_body(tx)) { + htp_status_t rc = htp_tx_req_process_body_data_ex(tx, NULL, 0); + if (rc != HTP_OK) return rc; + } + + tx->request_progress = HTP_REQUEST_COMPLETE; + + // Run hook REQUEST_COMPLETE. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_complete, tx); + if (rc != HTP_OK) return rc; + rc = htp_connp_req_receiver_finalize_clear(tx->connp); + if (rc != HTP_OK) return rc; + + // Clean-up. + if (tx->connp->put_file != NULL) { + bstr_free(tx->connp->put_file->filename); + free(tx->connp->put_file); + tx->connp->put_file = NULL; + } + + return HTP_OK; +} + +htp_status_t htp_tx_state_request_complete(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + if (tx->request_progress != HTP_REQUEST_COMPLETE) { + htp_status_t rc = htp_tx_state_request_complete_partial(tx); + if (rc != HTP_OK) return rc; + } + + // Make a copy of the connection parser pointer, so that + // we don't have to reference it via tx, which may be + // destroyed later. + htp_connp_t *connp = tx->connp; + + // Determine what happens next, and remove this transaction from the parser. + if (tx->is_protocol_0_9) { + connp->in_state = htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9; + } else { + connp->in_state = htp_connp_REQ_IDLE; + } + + // Check if the entire transaction is complete. This call may + // destroy the transaction, if auto-destroy is enabled. + htp_tx_finalize(tx); + + // At this point, tx may no longer be valid. + + connp->in_tx = NULL; + + return HTP_OK; +} + +htp_status_t htp_tx_state_request_start(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // Run hook REQUEST_START. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_start, tx); + if (rc != HTP_OK) return rc; + + // Change state into request line parsing. + tx->connp->in_state = htp_connp_REQ_LINE; + tx->connp->in_tx->request_progress = HTP_REQUEST_LINE; + + return HTP_OK; +} + +htp_status_t htp_tx_state_request_headers(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // If we're in HTP_REQ_HEADERS that means that this is the + // first time we're processing headers in a request. Otherwise, + // we're dealing with trailing headers. + if (tx->request_progress > HTP_REQUEST_HEADERS) { + // Request trailers. + + // Run hook HTP_REQUEST_TRAILER. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_trailer, tx); + if (rc != HTP_OK) return rc; + + // Finalize sending raw header data. + rc = htp_connp_req_receiver_finalize_clear(tx->connp); + if (rc != HTP_OK) return rc; + + // Completed parsing this request; finalize it now. + tx->connp->in_state = htp_connp_REQ_FINALIZE; + } else if (tx->request_progress >= HTP_REQUEST_LINE) { + // Request headers. + + // Did this request arrive in multiple data chunks? + if (tx->connp->in_chunk_count != tx->connp->in_chunk_request_index) { + tx->flags |= HTP_MULTI_PACKET_HEAD; + } + + htp_status_t rc = htp_tx_process_request_headers(tx); + if (rc != HTP_OK) return rc; + + tx->connp->in_state = htp_connp_REQ_CONNECT_CHECK; + } else { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "[Internal Error] Invalid tx progress: %d", tx->request_progress); + + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_state_request_line(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // Determine how to process the request URI. + + if (tx->request_method_number == HTP_M_CONNECT) { + // When CONNECT is used, the request URI contains an authority string. + if (htp_parse_uri_hostport(tx->connp, tx->request_uri, tx->parsed_uri_raw) != HTP_OK) { + return HTP_ERROR; + } + } else { + // Parse the request URI into htp_tx_t::parsed_uri_raw. + if (htp_parse_uri(tx->request_uri, &(tx->parsed_uri_raw)) != HTP_OK) { + return HTP_ERROR; + } + } + + // Build htp_tx_t::parsed_uri, but only if it was not explicitly set already. + if (tx->parsed_uri == NULL) { + tx->parsed_uri = htp_uri_alloc(); + if (tx->parsed_uri == NULL) return HTP_ERROR; + + // Keep the original URI components, but create a copy which we can normalize and use internally. + if (htp_normalize_parsed_uri(tx, tx->parsed_uri_raw, tx->parsed_uri) != HTP_OK) { + return HTP_ERROR; + } + } + + // Check parsed_uri hostname. + if (tx->parsed_uri->hostname != NULL) { + if (htp_validate_hostname(tx->parsed_uri->hostname) == 0) { + tx->flags |= HTP_HOSTU_INVALID; + } + } + + // Run hook REQUEST_URI_NORMALIZE. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_request_uri_normalize, tx); + if (rc != HTP_OK) return rc; + + + // Run hook REQUEST_LINE. + rc = htp_hook_run_all(tx->connp->cfg->hook_request_line, tx); + if (rc != HTP_OK) return rc; + + // Move on to the next phase. + tx->connp->in_state = htp_connp_REQ_PROTOCOL; + + return HTP_OK; +} + +htp_status_t htp_tx_state_response_complete(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + return htp_tx_state_response_complete_ex(tx, 1 /* hybrid mode */); +} + +htp_status_t htp_tx_finalize(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + if (!htp_tx_is_complete(tx)) return HTP_OK; + + // Run hook TRANSACTION_COMPLETE. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_transaction_complete, tx); + if (rc != HTP_OK) return rc; + + // In streaming processing, we destroy the transaction because it will not be needed any more. + if (tx->connp->cfg->tx_auto_destroy) { + htp_tx_destroy(tx); + } + + return HTP_OK; +} + +htp_status_t htp_tx_state_response_complete_ex(htp_tx_t *tx, int hybrid_mode) { + if (tx == NULL) return HTP_ERROR; + + if (tx->response_progress != HTP_RESPONSE_COMPLETE) { + tx->response_progress = HTP_RESPONSE_COMPLETE; + + // Run the last RESPONSE_BODY_DATA HOOK, but only if there was a response body present. + if (tx->response_transfer_coding != HTP_CODING_NO_BODY) { + htp_tx_res_process_body_data_ex(tx, NULL, 0); + } + + // Run hook RESPONSE_COMPLETE. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_complete, tx); + if (rc != HTP_OK) return rc; + + // Clear the data receivers hook if any + rc = htp_connp_res_receiver_finalize_clear(tx->connp); + if (rc != HTP_OK) return rc; + } + + if (!hybrid_mode) { + // Check if the inbound parser is waiting on us. If it is, that means that + // there might be request data that the inbound parser hasn't consumed yet. + // If we don't stop parsing we might encounter a response without a request, + // which is why we want to return straight away before processing any data. + // + // This situation will occur any time the parser needs to see the server + // respond to a particular situation before it can decide how to proceed. For + // example, when a CONNECT is sent, different paths are used when it is accepted + // and when it is not accepted. + // + // It is not enough to check only in_status here. Because of pipelining, it's possible + // that many inbound transactions have been processed, and that the parser is + // waiting on a response that we have not seen yet. + if ((tx->connp->in_status == HTP_STREAM_DATA_OTHER) && (tx->connp->in_tx == tx->connp->out_tx)) { + return HTP_DATA_OTHER; + } + + // Do we have a signal to yield to inbound processing at + // the end of the next transaction? + if (tx->connp->out_data_other_at_tx_end) { + // We do. Let's yield then. + tx->connp->out_data_other_at_tx_end = 0; + return HTP_DATA_OTHER; + } + } + + // Make a copy of the connection parser pointer, so that + // we don't have to reference it via tx, which may be destroyed later. + htp_connp_t *connp = tx->connp; + + // Finalize the transaction. This may call may destroy the transaction, if auto-destroy is enabled. + htp_status_t rc = htp_tx_finalize(tx); + if (rc != HTP_OK) return rc; + + // Disconnect transaction from the parser. + connp->out_tx = NULL; + + connp->out_state = htp_connp_RES_IDLE; + + return HTP_OK; +} + +/** + * @internal + * @brief split input into tokens separated by "seps" + * @param seps nul-terminated string: each character is a separator + */ +static int get_token(const unsigned char *in, size_t in_len, const char *seps, + unsigned char **ret_tok_ptr, size_t *ret_tok_len) +{ + #if HTP_DEBUG + fprintf(stderr, "INPUT %"PRIuMAX, (uintmax_t)in_len); + fprint_raw_data(stderr, __func__, in, in_len); + #endif + + size_t i = 0; + + /* skip leading 'separators' */ + while (i < in_len) + { + int match = 0; + for (const char *s = seps; *s != '\0'; s++) { + if (in[i] == *s) { + match++; + break; + } + } + if (!match) + break; + + i++; + } + if (i >= in_len) + return 0; + + in += i; + in_len -= i; + + #if HTP_DEBUG + fprintf(stderr, "INPUT (POST SEP STRIP) %"PRIuMAX, (uintmax_t)in_len); + fprint_raw_data(stderr, __func__, in, in_len); + #endif + + for (i = 0; i < in_len; i++) + { + for (const char *s = seps; *s != '\0'; s++) { + if (in[i] == *s) { + *ret_tok_ptr = (unsigned char *)in; + *ret_tok_len = i; + return 1; + } + } + } + + *ret_tok_ptr = (unsigned char *)in; + *ret_tok_len = in_len; + return 1; +} + +htp_status_t htp_tx_state_response_headers(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + // Check for compression. + + // Determine content encoding. + + int ce_multi_comp = 0; + tx->response_content_encoding = HTP_COMPRESSION_NONE; + htp_header_t *ce = htp_table_get_c(tx->response_headers, "content-encoding"); + if (ce != NULL) { + /* fast paths: regular gzip and friends */ + if ((bstr_cmp_c_nocasenorzero(ce->value, "gzip") == 0) || + (bstr_cmp_c_nocasenorzero(ce->value, "x-gzip") == 0)) { + tx->response_content_encoding = HTP_COMPRESSION_GZIP; + } else if ((bstr_cmp_c_nocasenorzero(ce->value, "deflate") == 0) || + (bstr_cmp_c_nocasenorzero(ce->value, "x-deflate") == 0)) { + tx->response_content_encoding = HTP_COMPRESSION_DEFLATE; + } else if (bstr_cmp_c_nocasenorzero(ce->value, "lzma") == 0) { + tx->response_content_encoding = HTP_COMPRESSION_LZMA; + } else if (bstr_cmp_c_nocasenorzero(ce->value, "inflate") == 0) { + // ignore + } else { + /* exceptional cases: enter slow path */ + ce_multi_comp = 1; + } + } + + // Configure decompression, if enabled in the configuration. + if (tx->connp->cfg->response_decompression_enabled) { + tx->response_content_encoding_processing = tx->response_content_encoding; + } else { + tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; + ce_multi_comp = 0; + } + + // Finalize sending raw header data. + htp_status_t rc = htp_connp_res_receiver_finalize_clear(tx->connp); + if (rc != HTP_OK) return rc; + + // Run hook RESPONSE_HEADERS. + rc = htp_hook_run_all(tx->connp->cfg->hook_response_headers, tx); + if (rc != HTP_OK) return rc; + + // Initialize the decompression engine as necessary. We can deal with three + // scenarios: + // + // 1. Decompression is enabled, compression indicated in headers, and we decompress. + // + // 2. As above, but the user disables decompression by setting response_content_encoding + // to COMPRESSION_NONE. + // + // 3. Decompression is disabled and we do not attempt to enable it, but the user + // forces decompression by setting response_content_encoding to one of the + // supported algorithms. + if ((tx->response_content_encoding_processing == HTP_COMPRESSION_GZIP) || + (tx->response_content_encoding_processing == HTP_COMPRESSION_DEFLATE) || + (tx->response_content_encoding_processing == HTP_COMPRESSION_LZMA) || + ce_multi_comp) + { + if (tx->connp->out_decompressor != NULL) { + htp_tx_res_destroy_decompressors(tx->connp); + } + + /* normal case */ + if (!ce_multi_comp) { + tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing); + if (tx->connp->out_decompressor == NULL) return HTP_ERROR; + + tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback; + + /* multiple ce value case */ + } else { + int layers = 0; + htp_decompressor_t *comp = NULL; + int nblzma = 0; + + uint8_t *tok = NULL; + size_t tok_len = 0; + + uint8_t *input = bstr_ptr(ce->value); + size_t input_len = bstr_len(ce->value); + + #if HTP_DEBUG + fprintf(stderr, "INPUT %"PRIuMAX, (uintmax_t)input_len); + fprint_raw_data(stderr, __func__, input, input_len); + #endif + + while (input_len > 0 && + get_token(input, input_len, ", ", &tok, &tok_len)) + { + #if HTP_DEBUG + fprintf(stderr, "TOKEN %"PRIuMAX, (uintmax_t)tok_len); + fprint_raw_data(stderr, __func__, tok, tok_len); + #endif + enum htp_content_encoding_t cetype = HTP_COMPRESSION_NONE; + + /* check depth limit (0 means no limit) */ + if ((tx->connp->cfg->response_decompression_layer_limit != 0) && + ((++layers) > tx->connp->cfg->response_decompression_layer_limit)) + { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "Too many response content encoding layers"); + break; + } + + nblzma++; + if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "gzip") != -1) { + if (!(bstr_util_cmp_mem(tok, tok_len, "gzip", 4) == 0 || + bstr_util_cmp_mem(tok, tok_len, "x-gzip", 6) == 0)) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "C-E gzip has abnormal value"); + } + cetype = HTP_COMPRESSION_GZIP; + } else if (bstr_util_mem_index_of_c_nocase(tok, tok_len, "deflate") != -1) { + if (!(bstr_util_cmp_mem(tok, tok_len, "deflate", 7) == 0 || + bstr_util_cmp_mem(tok, tok_len, "x-deflate", 9) == 0)) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "C-E deflate has abnormal value"); + } + cetype = HTP_COMPRESSION_DEFLATE; + } else if (bstr_util_cmp_mem(tok, tok_len, "lzma", 4) == 0) { + cetype = HTP_COMPRESSION_LZMA; + if (nblzma > tx->connp->cfg->response_lzma_layer_limit) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, + "Compression bomb: multiple encoding with lzma"); + break; + } + } else if (bstr_util_cmp_mem(tok, tok_len, "inflate", 7) == 0 || bstr_util_cmp_mem(tok, tok_len, "none", 4) == 0) { + cetype = HTP_COMPRESSION_NONE; + } else { + // continue + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "C-E unknown setting"); + } + + if (cetype != HTP_COMPRESSION_NONE) { + if (comp == NULL) { + tx->response_content_encoding_processing = cetype; + tx->connp->out_decompressor = htp_gzip_decompressor_create(tx->connp, tx->response_content_encoding_processing); + if (tx->connp->out_decompressor == NULL) { + return HTP_ERROR; + } + tx->connp->out_decompressor->callback = htp_tx_res_process_body_data_decompressor_callback; + comp = tx->connp->out_decompressor; + } else { + comp->next = htp_gzip_decompressor_create(tx->connp, cetype); + if (comp->next == NULL) { + return HTP_ERROR; + } + comp->next->callback = htp_tx_res_process_body_data_decompressor_callback; + comp = comp->next; + } + } + + if ((tok_len + 1) >= input_len) + break; + input += (tok_len + 1); + input_len -= (tok_len + 1); + } + } + } else if (tx->response_content_encoding_processing != HTP_COMPRESSION_NONE) { + return HTP_ERROR; + } + + return HTP_OK; +} + +htp_status_t htp_tx_state_response_start(htp_tx_t *tx) { + if (tx == NULL) return HTP_ERROR; + + tx->connp->out_tx = tx; + + // Run hook RESPONSE_START. + htp_status_t rc = htp_hook_run_all(tx->connp->cfg->hook_response_start, tx); + if (rc != HTP_OK) return rc; + + // Change state into response line parsing, except if we're following + // a HTTP/0.9 request (no status line or response headers). + if (tx->is_protocol_0_9) { + tx->response_transfer_coding = HTP_CODING_IDENTITY; + tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; + tx->response_progress = HTP_RESPONSE_BODY; + tx->connp->out_state = htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE; + tx->connp->out_body_data_left = -1; + } else { + tx->connp->out_state = htp_connp_RES_LINE; + tx->response_progress = HTP_RESPONSE_LINE; + } + + /* If at this point we have no method and no uri and our status + * is still htp_connp_REQ_LINE, we likely have timed out request + * or a overly long request */ + if (tx->request_method == HTP_M_UNKNOWN && tx->request_uri == NULL && tx->connp->in_state == htp_connp_REQ_LINE) { + htp_log(tx->connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Request line incomplete"); + } + + return HTP_OK; +} + +/** + * Register callback for the transaction-specific REQUEST_BODY_DATA hook. + * + * @param[in] tx + * @param[in] callback_fn + */ +void htp_tx_register_request_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)) { + if ((tx == NULL) || (callback_fn == NULL)) return; + htp_hook_register(&tx->hook_request_body_data, (htp_callback_fn_t) callback_fn); +} + +/** + * Register callback for the transaction-specific RESPONSE_BODY_DATA hook. + * + * @param[in] tx + * @param[in] callback_fn + */ +void htp_tx_register_response_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)) { + if ((tx == NULL) || (callback_fn == NULL)) return; + htp_hook_register(&tx->hook_response_body_data, (htp_callback_fn_t) callback_fn); +} + +int htp_tx_is_complete(htp_tx_t *tx) { + if (tx == NULL) return -1; + + // A transaction is considered complete only when both the request and + // response are complete. (Sometimes a complete response can be seen + // even while the request is ongoing.) + if ((tx->request_progress != HTP_REQUEST_COMPLETE) || (tx->response_progress != HTP_RESPONSE_COMPLETE)) { + return 0; + } else { + return 1; + } +} diff --git a/htp/htp_transaction.h b/htp/htp_transaction.h new file mode 100644 index 0000000..32d6773 --- /dev/null +++ b/htp/htp_transaction.h @@ -0,0 +1,529 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/* + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef HTP_TRANSACTION_H +#define HTP_TRANSACTION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "htp.h" + +/** + * Enumerate possible data handling strategies in hybrid parsing + * mode. The two possibilities are to make copies of all data and + * use bstr instances to wrap already available data. + */ +enum htp_alloc_strategy_t { + /** + * Make copies of all data. This strategy should be used when + * the supplied buffers are transient and will go away after + * the invoked function returns. + */ + HTP_ALLOC_COPY = 1, + + /** + * Reuse buffers, without a change of ownership. We assume the + * buffers will continue to be available until the transaction + * is deleted by the container. + */ + HTP_ALLOC_REUSE = 2 +}; + +/** + * Possible states of a progressing transaction. Internally, progress will change + * to the next state when the processing activities associated with that state + * begin. For example, when we start to process request line bytes, the request + * state will change from HTP_REQUEST_NOT_STARTED to HTP_REQUEST_LINE.* + */ +enum htp_tx_req_progress_t { + HTP_REQUEST_NOT_STARTED = 0, + HTP_REQUEST_LINE = 1, + HTP_REQUEST_HEADERS = 2, + HTP_REQUEST_BODY = 3, + HTP_REQUEST_TRAILER = 4, + HTP_REQUEST_COMPLETE = 5 +}; + +enum htp_tx_res_progress_t { + HTP_RESPONSE_NOT_STARTED = 0, + HTP_RESPONSE_LINE = 1, + HTP_RESPONSE_HEADERS = 2, + HTP_RESPONSE_BODY = 3, + HTP_RESPONSE_TRAILER = 4, + HTP_RESPONSE_COMPLETE = 5 +}; + +#define HTP_CONFIG_PRIVATE 0 +#define HTP_CONFIG_SHARED 1 + +/** + * Creates a new transaction structure. + * + * @param[in] connp Connection parser pointer. Must not be NULL. + * @return The newly created transaction, or NULL on memory allocation failure. + */ +htp_tx_t *htp_tx_create(htp_connp_t *connp); + +/** + * Destroys the supplied transaction. + * + * @param[in] tx Transaction pointer. Must not be NULL. + */ +htp_status_t htp_tx_destroy(htp_tx_t *tx); + +/** + * Determines if the transaction used a shared configuration structure. See the + * documentation for htp_tx_set_config() for more information why you might want + * to know that. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_CFG_SHARED or HTP_CFG_PRIVATE. + */ +int htp_tx_get_is_config_shared(const htp_tx_t *tx); + +/** + * Returns the user data associated with this transaction. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return A pointer to user data or NULL. + */ +void *htp_tx_get_user_data(const htp_tx_t *tx); + +/** + * Registers a callback that will be invoked to process the transaction's request body data. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] callback_fn Callback function pointer. Must not be NULL. + */ +void htp_tx_register_request_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Registers a callback that will be invoked to process the transaction's response body data. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] callback_fn Callback function pointer. Must not be NULL. + */ +void htp_tx_register_response_body_data(htp_tx_t *tx, int (*callback_fn)(htp_tx_data_t *)); + +/** + * Adds one parameter to the request. THis function will take over the + * responsibility for the provided htp_param_t structure. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] param Parameter pointer. Must not be NULL. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_add_param(htp_tx_t *tx, htp_param_t *param); + +/** + * Returns the first request parameter that matches the given name, using case-insensitive matching. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] name Name data pointer. Must not be NULL. + * @param[in] name_len Name data length. + * @return htp_param_t instance, or NULL if parameter not found. + */ +htp_param_t *htp_tx_req_get_param(htp_tx_t *tx, const char *name, size_t name_len); + +/** + * Returns the first request parameter from the given source that matches the given name, + * using case-insensitive matching. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] source Parameter source (where in request the parameter was located). + * @param[in] name Name data pointer. Must not be NULL. + * @param[in] name_len Name data length. + * @return htp_param_t instance, or NULL if parameter not found. + */ +htp_param_t *htp_tx_req_get_param_ex(htp_tx_t *tx, enum htp_data_source_t source, const char *name, size_t name_len); + +/** + * Determine if the request has a body. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return 1 if there is a body, 0 otherwise. + */ +int htp_tx_req_has_body(const htp_tx_t *tx); + +/** + * Process a chunk of request body data. This function assumes that + * handling of chunked encoding is implemented by the container. When + * you're done submitting body data, invoke a state change (to REQUEST) + * to finalize any processing that might be pending. The supplied data is + * fully consumed and there is no expectation that it will be available + * afterwards. The protocol parsing code makes no copies of the data, + * but some parsers might. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] data Data pointer. Must not be NULL. + * @param[in] len Data length. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_process_body_data(htp_tx_t *tx, const void *data, size_t len); + +/** + * Set one request header. This function should be invoked once for + * each available header, and in the order in which headers were + * seen in the request. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] name Name data pointer. Must not be NULL. + * @param[in] name_len Name data length. + * @param[in] value Value data pointer. Must not be NULL. + * @param[in] value_len Value data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_header(htp_tx_t *tx, const char *name, size_t name_len, + const char *value, size_t value_len, enum htp_alloc_strategy_t alloc); + +/** + * Removes all request headers associated with this transaction. This + * function is needed because in some cases the container does not + * differentiate between standard and trailing headers. In that case, + * you set request headers once at the beginning of the transaction, + * read the body (at this point the request headers should contain the + * mix of regular and trailing headers), clear all headers, and then set + * them all again. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_headers_clear(htp_tx_t *tx); + +/** + * Set request line. When used, this function should always be called first, + * with more specific functions following. Must not contain line terminators. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] line Line data pointer. Must not be NULL. + * @param[in] line_len Line data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc); + +/** + * Set transaction request method. This function will enable you to keep + * track of the text representation of the method. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] method Method data pointer. Must not be NULL. + * @param[in] method_len Method data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_method(htp_tx_t *tx, const char *method, size_t method_len, enum htp_alloc_strategy_t alloc); + +/** + * Set transaction request method number. This function enables you to + * keep track how a particular method string is interpreted. This function + * is useful with web servers that ignore invalid methods; for example, some + * web servers will treat them as a GET. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] method_number Method number. + */ +void htp_tx_req_set_method_number(htp_tx_t *tx, enum htp_method_t method_number); + +/** + * Set parsed request URI. You don't need to use this function if you are already providing + * the request line or request URI. But if your container already has this data available, + * feeding it to LibHTP will minimize any potential data differences. This function assumes + * management of the data provided in parsed_uri. This function will not change htp_tx_t::parsed_uri_raw + * (which may have data in it from the parsing of the request URI). + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] parsed_uri URI pointer. Must not be NULL. + */ +void htp_tx_req_set_parsed_uri(htp_tx_t *tx, htp_uri_t *parsed_uri); + +/** + * Forces HTTP/0.9 as the transaction protocol. This method exists to ensure + * that both LibHTP and the container treat the transaction as HTTP/0.9, despite + * potential differences in how the protocol version is determined. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] is_protocol_0_9 Zero if protocol is not HTTP/0.9, or 1 if it is. + */ +void htp_tx_req_set_protocol_0_9(htp_tx_t *tx, int is_protocol_0_9); + +/** + * Sets the request protocol string (e.g., "HTTP/1.0"). The information provided + * is only stored, not parsed. Use htp_tx_req_set_protocol_number() to set the + * actual protocol number, as interpreted by the container. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] protocol Protocol data pointer. Must not be NULL. + * @param[in] protocol_len Protocol data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_protocol(htp_tx_t *tx, const char *protocol, size_t protocol_len, enum htp_alloc_strategy_t alloc); + +/** + * Set request protocol version number. Must be invoked after + * htp_txh_set_req_protocol(), because it will overwrite the previously + * extracted version number. Convert the protocol version number to an integer + * by multiplying it with 100. For example, 1.1 becomes 110. Alternatively, + * use the HTP_PROTOCOL_0_9, HTP_PROTOCOL_1_0, and HTP_PROTOCOL_1_1 constants. + * Note: setting protocol to HTP_PROTOCOL_0_9 alone will _not_ get the library to + * treat the transaction as HTTP/0.9. You need to also invoke htp_tx_req_set_protocol_0_9(). + * This is because HTTP 0.9 is used only when protocol information is absent from the + * request line, and not when it is explicitly stated (as "HTTP/0.9"). This behavior is + * consistent with that of Apache httpd. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] protocol_number Protocol number. + */ +void htp_tx_req_set_protocol_number(htp_tx_t *tx, int protocol_number); + +/** + * Set transaction request URI. The value provided here will be stored in htp_tx_t::request_uri + * and subsequently parsed. If htp_tx_req_set_line() was previously used, the uri provided + * when calling this function will overwrite any previously parsed value. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] uri URI data pointer. Must not be NULL. + * @param[in] uri_len URI data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_req_set_uri(htp_tx_t *tx, const char *uri, size_t uri_len, enum htp_alloc_strategy_t alloc); + +/** + * Process a chunk of response body data. This function assumes that + * handling of chunked encoding is implemented by the container. When + * you're done submitting body data, invoking a state change (to RESPONSE) + * will finalize any processing that might be pending. + * + * The response body data will be decompressed if two conditions are met: one, + * decompression is enabled in configuration and two, if the response headers + * indicate compression. Alternatively, you can control decompression from + * a RESPONSE_HEADERS callback, by setting tx->response_content_encoding either + * to COMPRESSION_NONE (to disable compression), or to one of the supported + * decompression algorithms. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] data Data pointer. Must not be NULL. + * @param[in] len Data length. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_res_process_body_data(htp_tx_t *tx, const void *data, size_t len); + +/** + * Set one response header. This function should be invoked once for + * each available header, and in the order in which headers were + * seen in the response. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] name Name data pointer. Must not be NULL. + * @param[in] name_len Name data length. + * @param[in] value Value data pointer. Must not be NULL. + * @param[in] value_len Value length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_res_set_header(htp_tx_t *tx, const char *name, size_t name_len, + const char *value, size_t value_len, enum htp_alloc_strategy_t alloc); + +/** + * Removes all response headers associated with this transaction. This + * function is needed because in some cases the container does not + * differentiate between standard and trailing headers. In that case, + * you set response headers once at the beginning of the transaction, + * read the body, clear all headers, and then set them all again. After + * the headers are set for the second time, they will potentially contain + * a mixture of standard and trailing headers. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_res_set_headers_clear(htp_tx_t *tx); + +/** + * Set response protocol number. See htp_tx_res_set_protocol_number() for more information + * about the correct format of the protocol_parameter parameter. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] protocol_number Protocol number. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +void htp_tx_res_set_protocol_number(htp_tx_t *tx, int protocol_number); + +/** + * Set response line. Use this function is you have a single buffer containing + * the entire line. If you have individual request line pieces, use the other + * available functions. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] line Line data pointer. Must not be NULL. + * @param[in] line_len Line data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_res_set_status_line(htp_tx_t *tx, const char *line, size_t line_len, enum htp_alloc_strategy_t alloc); + +/** + * Set response status code. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] status_code Response status code. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +void htp_tx_res_set_status_code(htp_tx_t *tx, int status_code); + +/** + * Set response status message, which is the part of the response + * line that comes after the status code. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] msg Message data pointer. Must not be NULL. + * @param[in] msg_len Message data length. + * @param[in] alloc Desired allocation strategy. + * @return HTP_OK on success, HTP_ERROR on failure. + */ +htp_status_t htp_tx_res_set_status_message(htp_tx_t *tx, const char *msg, size_t msg_len, enum htp_alloc_strategy_t alloc); + +/** + * Sets the configuration that is to be used for this transaction. If the + * second parameter is set to HTP_CFG_PRIVATE, the transaction will adopt + * the configuration structure and destroy it when appropriate. This function is + * useful if you need to make changes to configuration on per-transaction basis. + * Initially, all transactions will share the configuration with that of the + * connection; if you were to make changes on it, they would affect all + * current and future connections. To work around that, you make a copy of the + * configuration object, call this function with the second parameter set to + * HTP_CFG_PRIVATE, and modify configuration at will. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] cfg Configuration pointer. Must not be NULL. + * @param[in] is_cfg_shared HTP_CFG_SHARED or HTP_CFG_PRIVATE + */ +void htp_tx_set_config(htp_tx_t *tx, htp_cfg_t *cfg, int is_cfg_shared); + +/** + * Associates user data with this transaction. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @param[in] user_data Opaque user data pointer. + */ +void htp_tx_set_user_data(htp_tx_t *tx, void *user_data); + +/** + * Change transaction state to REQUEST and invoke registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_request_complete(htp_tx_t *tx); + +/** + * Change transaction state to REQUEST_HEADERS and invoke all + * registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_request_headers(htp_tx_t *tx); + +/** + * Change transaction state to REQUEST_LINE and invoke all + * registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_request_line(htp_tx_t *tx); + +/** + * Initialize hybrid parsing mode, change state to TRANSACTION_START, + * and invoke all registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_request_start(htp_tx_t *tx); + +/** + * Change transaction state to RESPONSE and invoke registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_response_complete(htp_tx_t *tx); + +/** + * Change transaction state to RESPONSE_HEADERS and invoke registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_response_headers(htp_tx_t *tx); + +/** + * Change transaction state to HTP_RESPONSE_LINE and invoke registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_response_line(htp_tx_t *tx); + +/** + * Change transaction state to RESPONSE_START and invoke registered callbacks. + * + * @param[in] tx Transaction pointer. Must not be NULL. + * @return HTP_OK on success; HTP_ERROR on error, HTP_STOP if one of the + * callbacks does not want to follow the transaction any more. + */ +htp_status_t htp_tx_state_response_start(htp_tx_t *tx); + +#ifdef __cplusplus +} +#endif + +#endif /* HTP_HYBRID_H */ diff --git a/htp/htp_transcoder.c b/htp/htp_transcoder.c new file mode 100644 index 0000000..03d49ed --- /dev/null +++ b/htp/htp_transcoder.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * Transcode all parameters supplied in the table. + * + * @param[in] connp + * @param[in] params + * @param[in] destroy_old + */ +int htp_transcode_params(htp_connp_t *connp, htp_table_t **params, int destroy_old) { + htp_table_t *input_params = *params; + + // No transcoding unless necessary + if ((connp->cfg->internal_encoding == NULL)||(connp->cfg->request_encoding == NULL)) return HTP_OK; + + // Create a new table that will hold transcoded parameters + htp_table_t *output_params = htp_table_create(htp_table_size(input_params)); + if (output_params == NULL) return HTP_ERROR; + + // Initialize iconv + iconv_t cd = iconv_open(connp->cfg->internal_encoding, connp->cfg->request_encoding); + if (cd == (iconv_t) -1) { + htp_table_destroy(output_params); + return HTP_ERROR; + } + + #if (_LIBICONV_VERSION >= 0x0108 && HAVE_ICONVCTL) + int iconv_param = 0; + iconvctl(cd, ICONV_SET_TRANSLITERATE, &iconv_param); + iconv_param = 1; + iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &iconv_param); + #endif + + // Convert the parameters, one by one + bstr *name = NULL; + bstr *value = NULL; + for (size_t i = 0, n = htp_table_size(input_params); i < n; i++) { + value = htp_table_get_index(input_params, i, &name); + + bstr *new_name = NULL, *new_value = NULL; + + // Convert name + htp_transcode_bstr(cd, name, &new_name); + if (new_name == NULL) { + iconv_close(cd); + + bstr *b = NULL; + for (size_t j = 0, k = htp_table_size(output_params); j < k; j++) { + b = htp_table_get_index(output_params, j, NULL); + bstr_free(b); + } + + htp_table_destroy(output_params); + return HTP_ERROR; + } + + // Convert value + htp_transcode_bstr(cd, value, &new_value); + if (new_value == NULL) { + bstr_free(new_name); + iconv_close(cd); + + bstr *b = NULL; + for (size_t j = 0, k = htp_table_size(output_params); j < k; j++) { + b = htp_table_get_index(output_params, j, NULL); + bstr_free(b); + } + + htp_table_destroy(output_params); + return HTP_ERROR; + } + + // Add to new table + htp_table_addn(output_params, new_name, new_value); + } + + // Replace the old parameter table + *params = output_params; + + // Destroy the old parameter table if necessary + if (destroy_old) { + bstr *b = NULL; + for (size_t i = 0, n = htp_table_size(input_params); i < n; i++) { + b = htp_table_get_index(input_params, i, NULL); + bstr_free(b); + } + + htp_table_destroy(input_params); + } + + iconv_close(cd); + + return HTP_OK; +} + +/** + * Transcode one bstr. + * + * @param[in] cd + * @param[in] input + * @param[in] output + */ +int htp_transcode_bstr(iconv_t cd, bstr *input, bstr **output) { + // Reset conversion state for every new string + iconv(cd, NULL, 0, NULL, 0); + + bstr_builder_t *bb = NULL; + + const size_t buflen = 10; + unsigned char *buf = malloc(buflen); + if (buf == NULL) { + return HTP_ERROR; + } + + const char *inbuf = (const char *)bstr_ptr(input); + size_t inleft = bstr_len(input); + char *outbuf = (char *)buf; + size_t outleft = buflen; + + int loop = 1; + while (loop) { + loop = 0; + + if (iconv(cd, (ICONV_CONST char **)&inbuf, &inleft, (char **)&outbuf, &outleft) == (size_t) - 1) { + if (errno == E2BIG) { + // Create bstr builder on-demand + if (bb == NULL) { + bb = bstr_builder_create(); + if (bb == NULL) { + free(buf); + return HTP_ERROR; + } + } + + // The output buffer is full + bstr_builder_append_mem(bb, buf, buflen - outleft); + + outbuf = (char *)buf; + outleft = buflen; + + // Continue in the loop, as there's more work to do + loop = 1; + } else { + // Error + if (bb != NULL) bstr_builder_destroy(bb); + free(buf); + return HTP_ERROR; + } + } + } + + if (bb != NULL) { + bstr_builder_append_mem(bb, buf, buflen - outleft); + *output = bstr_builder_to_str(bb); + bstr_builder_destroy(bb); + if (*output == NULL) { + free(buf); + return HTP_ERROR; + } + } else { + *output = bstr_dup_mem(buf, buflen - outleft); + if (*output == NULL) { + free(buf); + return HTP_ERROR; + } + } + + free(buf); + + return HTP_OK; +} diff --git a/htp/htp_urlencoded.c b/htp/htp_urlencoded.c new file mode 100644 index 0000000..5ad3886 --- /dev/null +++ b/htp/htp_urlencoded.c @@ -0,0 +1,332 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +/** + * This method is invoked whenever a piece of data, belonging to a single field (name or value) + * becomes available. It will either create a new parameter or store the transient information + * until a parameter can be created. + * + * @param[in] urlenp + * @param[in] data + * @param[in] startpos + * @param[in] endpos + * @param[in] c Should contain -1 if the reason this function is called is because the end of + * the current data chunk is reached. + */ +static void htp_urlenp_add_field_piece(htp_urlenp_t *urlenp, const unsigned char *data, size_t startpos, size_t endpos, int last_char) { + // Add field if we know it ended (last_char is something other than -1) + // or if we know that there won't be any more input data (urlenp->_complete is true). + if ((last_char != -1) || (urlenp->_complete)) { + // Prepare the field value, assembling from multiple pieces as necessary. + + bstr *field = NULL; + + // Did we use the string builder for this field? + if (bstr_builder_size(urlenp->_bb) > 0) { + // The current field consists of more than once piece, we have to use the string builder. + + // Add current piece to string builder. + if ((data != NULL) && (endpos - startpos > 0)) { + bstr_builder_append_mem(urlenp->_bb, data + startpos, endpos - startpos); + } + + // Generate the field and clear the string builder. + field = bstr_builder_to_str(urlenp->_bb); + if (field == NULL) return; + + bstr_builder_clear(urlenp->_bb); + } else { + // We only have the current piece to work with, so no need to involve the string builder. + if ((data != NULL) && (endpos - startpos > 0)) { + field = bstr_dup_mem(data + startpos, endpos - startpos); + if (field == NULL) return; + } + } + + // Process field as key or value, as appropriate. + + if (urlenp->_state == HTP_URLENP_STATE_KEY) { + // Key. + + // If there is no more work left to do, then we have a single key. Add it. + if ((urlenp->_complete)||(last_char == urlenp->argument_separator)) { + + // Handling empty pairs is tricky. We don't want to create a pair for + // an entirely empty input, but in some cases it may be appropriate + // (e.g., /index.php?&q=2). + if ((field != NULL)||(last_char == urlenp->argument_separator)) { + // Add one pair, with an empty value and possibly empty key too. + + bstr *name = field; + if (name == NULL) { + name = bstr_dup_c(""); + if (name == NULL) return; + } + + bstr *value = bstr_dup_c(""); + if (value == NULL) { + bstr_free(name); + return; + } + + if (urlenp->decode_url_encoding) { + htp_tx_urldecode_params_inplace(urlenp->tx, name); + } + + htp_table_addn(urlenp->params, name, value); + + urlenp->_name = NULL; + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "NAME", bstr_ptr(name), bstr_len(name)); + fprint_raw_data(stderr, "VALUE", bstr_ptr(value), bstr_len(value)); + #endif + } + } else { + // This key will possibly be followed by a value, so keep it for later. + urlenp->_name = field; + } + } else { + // Value (with a key remembered from before). + + bstr *name = urlenp->_name; + urlenp->_name = NULL; + + if (name == NULL) { + name = bstr_dup_c(""); + if (name == NULL) { + bstr_free(field); + return; + } + } + + bstr *value = field; + if (value == NULL) { + value = bstr_dup_c(""); + if (value == NULL) { + bstr_free(name); + return; + } + } + + if (urlenp->decode_url_encoding) { + htp_tx_urldecode_params_inplace(urlenp->tx, name); + htp_tx_urldecode_params_inplace(urlenp->tx, value); + } + + htp_table_addn(urlenp->params, name, value); + + #ifdef HTP_DEBUG + fprint_raw_data(stderr, "NAME", bstr_ptr(name), bstr_len(name)); + fprint_raw_data(stderr, "VALUE", bstr_ptr(value), bstr_len(value)); + #endif + } + } else { + // The field has not ended. We'll make a copy of of the available data for later. + if ((data != NULL) && (endpos - startpos > 0)) { + bstr_builder_append_mem(urlenp->_bb, data + startpos, endpos - startpos); + } + } +} + +/** + * Creates a new URLENCODED parser. + * + * @return New parser, or NULL on memory allocation failure. + */ +htp_urlenp_t *htp_urlenp_create(htp_tx_t *tx) { + htp_urlenp_t *urlenp = calloc(1, sizeof (htp_urlenp_t)); + if (urlenp == NULL) return NULL; + + urlenp->tx = tx; + + urlenp->params = htp_table_create(HTP_URLENP_DEFAULT_PARAMS_SIZE); + if (urlenp->params == NULL) { + free(urlenp); + return NULL; + } + + urlenp->_bb = bstr_builder_create(); + if (urlenp->_bb == NULL) { + htp_table_destroy(urlenp->params); + free(urlenp); + return NULL; + } + + urlenp->argument_separator = '&'; + urlenp->decode_url_encoding = 1; + urlenp->_state = HTP_URLENP_STATE_KEY; + + return urlenp; +} + +/** + * Destroys an existing URLENCODED parser. + * + * @param[in] urlenp + */ +void htp_urlenp_destroy(htp_urlenp_t *urlenp) { + if (urlenp == NULL) return; + + if (urlenp->_name != NULL) { + bstr_free(urlenp->_name); + } + + bstr_builder_destroy(urlenp->_bb); + + if (urlenp->params != NULL) { + // Destroy parameters. + for (size_t i = 0, n = htp_table_size(urlenp->params); i < n; i++) { + bstr *b = htp_table_get_index(urlenp->params, i, NULL); + // Parameter name will be freed by the table code. + bstr_free(b); + } + + htp_table_destroy(urlenp->params); + } + + free(urlenp); +} + +/** + * Finalizes parsing, forcing the parser to convert any outstanding + * data into parameters. This method should be invoked at the end + * of a parsing operation that used htp_urlenp_parse_partial(). + * + * @param[in] urlenp + * @return Success indication + */ +htp_status_t htp_urlenp_finalize(htp_urlenp_t *urlenp) { + urlenp->_complete = 1; + return htp_urlenp_parse_partial(urlenp, NULL, 0); +} + +/** + * Parses the provided data chunk under the assumption + * that it contains all the data that will be parsed. When this + * method is used for parsing the finalization method should not + * be invoked. + * + * @param[in] urlenp + * @param[in] data + * @param[in] len + * @return + */ +htp_status_t htp_urlenp_parse_complete(htp_urlenp_t *urlenp, const void *data, size_t len) { + htp_urlenp_parse_partial(urlenp, data, len); + return htp_urlenp_finalize(urlenp); +} + +/** + * Parses the provided data chunk, keeping state to allow streaming parsing, i.e., the + * parsing where only partial information is available at any one time. The method + * htp_urlenp_finalize() must be invoked at the end to finalize parsing. + * + * @param[in] urlenp + * @param[in] _data + * @param[in] len + * @return + */ +htp_status_t htp_urlenp_parse_partial(htp_urlenp_t *urlenp, const void *_data, size_t len) { + unsigned char *data = (unsigned char *) _data; + size_t startpos = 0; + size_t pos = 0; + int c; + + if (data == NULL) len = 0; + + do { + // Get the next character, or use -1 to indicate end of input. + if (pos < len) c = data[pos]; + else c = -1; + + switch (urlenp->_state) { + + case HTP_URLENP_STATE_KEY: + // Look for =, argument separator, or end of input. + if ((c == '=') || (c == urlenp->argument_separator) || (c == -1)) { + // Data from startpos to pos. + htp_urlenp_add_field_piece(urlenp, data, startpos, pos, c); + + // If it's not the end of input, then it must be the end of this field. + if (c != -1) { + // Next state. + startpos = pos + 1; + + if (c == urlenp->argument_separator) { + urlenp->_state = HTP_URLENP_STATE_KEY; + } else { + urlenp->_state = HTP_URLENP_STATE_VALUE; + } + } + } + + pos++; + + break; + + case HTP_URLENP_STATE_VALUE: + // Look for argument separator or end of input. + if ((c == urlenp->argument_separator) || (c == -1)) { + // Data from startpos to pos. + htp_urlenp_add_field_piece(urlenp, data, startpos, pos, c); + + // If it's not the end of input, then it must be the end of this field. + if (c != -1) { + // Next state. + startpos = pos + 1; + urlenp->_state = HTP_URLENP_STATE_KEY; + } + } + + pos++; + + break; + + default: + // Invalid state. + return HTP_ERROR; + } + } while (c != -1); + + return HTP_OK; +} diff --git a/htp/htp_urlencoded.h b/htp/htp_urlencoded.h new file mode 100644 index 0000000..bc4697c --- /dev/null +++ b/htp/htp_urlencoded.h @@ -0,0 +1,111 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _HTP_URLENCODED_H +#define _HTP_URLENCODED_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct htp_urlenp_t htp_urlenp_t; +typedef struct htp_urlen_param_t htp_urlen_param_t; + +#define HTP_URLENP_DEFAULT_PARAMS_SIZE 32 + +#define HTP_URLENP_STATE_KEY 1 +#define HTP_URLENP_STATE_VALUE 2 + +// The MIME type that triggers the parser. Must be lowercase. +#define HTP_URLENCODED_MIME_TYPE "application/x-www-form-urlencoded" + +#include "htp.h" + +/** + * This is the main URLENCODED parser structure. It is used to store + * parser configuration, temporary parsing data, as well as the parameters. + */ +struct htp_urlenp_t { + /** The transaction this parser belongs to. */ + htp_tx_t *tx; + + /** The character used to separate parameters. Defaults to & and should + * not be changed without good reason. + */ + unsigned char argument_separator; + + /** Whether to perform URL-decoding on parameters. */ + int decode_url_encoding; + + /** This table contains the list of parameters, indexed by name. */ + htp_table_t *params; + + // Private fields; these are used during the parsing process only + int _state; + int _complete; + bstr *_name; + bstr_builder_t *_bb; +}; + +/** + * Holds one application/x-www-form-urlencoded parameter. + */ +struct htp_urlen_param_t { + /** Parameter name. */ + bstr *name; + + /** Parameter value. */ + bstr *value; +}; + +htp_urlenp_t *htp_urlenp_create(htp_tx_t *tx); +void htp_urlenp_destroy(htp_urlenp_t *urlenp); + +void htp_urlenp_set_argument_separator(htp_urlenp_t *urlenp, unsigned char argument_separator); +void htp_urlenp_set_decode_url_encoding(htp_urlenp_t *urlenp, int decode_url_encoding); + +htp_status_t htp_urlenp_parse_partial(htp_urlenp_t *urlenp, const void *data, size_t len); +htp_status_t htp_urlenp_parse_complete(htp_urlenp_t *urlenp, const void *data, size_t len); +htp_status_t htp_urlenp_finalize(htp_urlenp_t *urlenp); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTP_URLENCODED_H */ + diff --git a/htp/htp_utf8_decoder.c b/htp/htp_utf8_decoder.c new file mode 100644 index 0000000..6017a18 --- /dev/null +++ b/htp/htp_utf8_decoder.c @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +/* +Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> +// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + +#include "htp_config_auto.h" + +#include "htp_private.h" + +static const uint8_t utf8d[] = { + 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, // 00..1f + 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, // 20..3f + 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, // 40..5f + 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, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +static const uint8_t utf8d_allow_overlong[] = { + 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, // 00..1f + 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, // 20..3f + 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, // 40..5f + 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, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df; changed c0 and c1 + 0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef; changed e0 + 0x6,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff; changed f0 + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +/** + * Process one byte of UTF-8 data and return a code point if one is available. Allows + * overlong characters in input. + * + * @param[in] state + * @param[in] codep + * @param[in] byte + * @return HTP_UTF8_ACCEPT for a valid character, HTP_UTF8_REJECT for an invalid character, + * or something else if the character has not yet been formed + */ +uint32_t htp_utf8_decode_allow_overlong(uint32_t* state, uint32_t* codep, uint32_t byte) { + uint32_t type = utf8d_allow_overlong[byte]; + + *codep = (*state != HTP_UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state*16 + type]; + return *state; +} diff --git a/htp/htp_utf8_decoder.h b/htp/htp_utf8_decoder.h new file mode 100644 index 0000000..b39abdd --- /dev/null +++ b/htp/htp_utf8_decoder.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +/* LibHTP changes: + * + * - Changed the name of the function from "decode" to "utf8_decode" + * - Created a separate header file + * - Copied the license from the web page + * - Created a copy of the data and function "utf8_decode_allow_overlong", which + * does not treat overlong characters as invalid. + */ + +/* +Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _UTF8_DECODER_H +#define _UTF8_DECODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#define HTP_UTF8_ACCEPT 0 +#define HTP_UTF8_REJECT 1 + +uint32_t htp_utf8_decode_allow_overlong(uint32_t* state, uint32_t* codep, uint32_t byte); + +#ifdef __cplusplus +} +#endif + +#endif /* _UTF8_DECODER_H */ diff --git a/htp/htp_util.c b/htp/htp_util.c new file mode 100644 index 0000000..936e22b --- /dev/null +++ b/htp/htp_util.c @@ -0,0 +1,2602 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include "htp_config_auto.h" + +//inet_pton +#if _WIN32 +#include <ws2tcpip.h> +#else // mac, linux, freebsd +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +#include "htp_private.h" + +/** + * Is character a linear white space character? + * + * @param[in] c + * @return 0 or 1 + */ +int htp_is_lws(int c) { + if ((c == ' ') || (c == '\t')) return 1; + else return 0; +} + +/** + * Is character a separator character? + * + * @param[in] c + * @return 0 or 1 + */ +int htp_is_separator(int c) { + /* separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT */ + switch (c) { + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case ':': + case '\\': + case '"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + case ' ': + case '\t': + return 1; + break; + default: + return 0; + } +} + +/** + * Is character a text character? + * + * @param[in] c + * @return 0 or 1 + */ +int htp_is_text(int c) { + if (c == '\t') return 1; + if (c < 32) return 0; + return 1; +} + +/** + * Is character a token character? + * + * @param[in] c + * @return 0 or 1 + */ +int htp_is_token(int c) { + /* token = 1*<any CHAR except CTLs or separators> */ + /* CHAR = <any US-ASCII character (octets 0 - 127)> */ + if ((c < 32) || (c > 126)) return 0; + if (htp_is_separator(c)) return 0; + return 1; +} + +/** + * Remove all line terminators (LF, CR or CRLF) from + * the end of the line provided as input. + * + * @return 0 if nothing was removed, 1 if one or more LF characters were removed, or + * 2 if one or more CR and/or LF characters were removed. + */ +int htp_chomp(unsigned char *data, size_t *len) { + int r = 0; + + // Loop until there's no more stuff in the buffer + while (*len > 0) { + // Try one LF first + if (data[*len - 1] == LF) { + (*len)--; + r = 1; + + if (*len == 0) return r; + + // A CR is allowed before LF + if (data[*len - 1] == CR) { + (*len)--; + r = 2; + } + } else if (data[*len - 1] == CR) { + (*len)--; + r = 1; + } else return r; + } + + return r; +} + +/** + * Is character a white space character? + * + * @param[in] c + * @return 0 or 1 + */ +int htp_is_space(int c) { + switch (c) { + case ' ': + case '\f': + case '\v': + case '\t': + case '\r': + case '\n': + return 1; + default: + return 0; + } +} + +/** + * Converts request method, given as a string, into a number. + * + * @param[in] method + * @return Method number of M_UNKNOWN + */ +int htp_convert_method_to_number(bstr *method) { + if (method == NULL) return HTP_M_UNKNOWN; + + // TODO Optimize using parallel matching, or something similar. + + if (bstr_cmp_c(method, "GET") == 0) return HTP_M_GET; + if (bstr_cmp_c(method, "PUT") == 0) return HTP_M_PUT; + if (bstr_cmp_c(method, "POST") == 0) return HTP_M_POST; + if (bstr_cmp_c(method, "DELETE") == 0) return HTP_M_DELETE; + if (bstr_cmp_c(method, "CONNECT") == 0) return HTP_M_CONNECT; + if (bstr_cmp_c(method, "OPTIONS") == 0) return HTP_M_OPTIONS; + if (bstr_cmp_c(method, "TRACE") == 0) return HTP_M_TRACE; + if (bstr_cmp_c(method, "PATCH") == 0) return HTP_M_PATCH; + if (bstr_cmp_c(method, "PROPFIND") == 0) return HTP_M_PROPFIND; + if (bstr_cmp_c(method, "PROPPATCH") == 0) return HTP_M_PROPPATCH; + if (bstr_cmp_c(method, "MKCOL") == 0) return HTP_M_MKCOL; + if (bstr_cmp_c(method, "COPY") == 0) return HTP_M_COPY; + if (bstr_cmp_c(method, "MOVE") == 0) return HTP_M_MOVE; + if (bstr_cmp_c(method, "LOCK") == 0) return HTP_M_LOCK; + if (bstr_cmp_c(method, "UNLOCK") == 0) return HTP_M_UNLOCK; + if (bstr_cmp_c(method, "VERSION-CONTROL") == 0) return HTP_M_VERSION_CONTROL; + if (bstr_cmp_c(method, "CHECKOUT") == 0) return HTP_M_CHECKOUT; + if (bstr_cmp_c(method, "UNCHECKOUT") == 0) return HTP_M_UNCHECKOUT; + if (bstr_cmp_c(method, "CHECKIN") == 0) return HTP_M_CHECKIN; + if (bstr_cmp_c(method, "UPDATE") == 0) return HTP_M_UPDATE; + if (bstr_cmp_c(method, "LABEL") == 0) return HTP_M_LABEL; + if (bstr_cmp_c(method, "REPORT") == 0) return HTP_M_REPORT; + if (bstr_cmp_c(method, "MKWORKSPACE") == 0) return HTP_M_MKWORKSPACE; + if (bstr_cmp_c(method, "MKACTIVITY") == 0) return HTP_M_MKACTIVITY; + if (bstr_cmp_c(method, "BASELINE-CONTROL") == 0) return HTP_M_BASELINE_CONTROL; + if (bstr_cmp_c(method, "MERGE") == 0) return HTP_M_MERGE; + if (bstr_cmp_c(method, "INVALID") == 0) return HTP_M_INVALID; + if (bstr_cmp_c(method, "HEAD") == 0) return HTP_M_HEAD; + + return HTP_M_UNKNOWN; +} + +/** + * Is the given line empty? + * + * @param[in] data + * @param[in] len + * @return 0 or 1 + */ +int htp_is_line_empty(unsigned char *data, size_t len) { + if (((len == 1) && ((data[0] == CR) || (data[0] == LF))) || + ((len == 2) && (data[0] == CR) && (data[1] == LF))) { + return 1; + } + + return 0; +} + +/** + * Does line consist entirely of whitespace characters? + * + * @param[in] data + * @param[in] len + * @return 0 or 1 + */ +int htp_is_line_whitespace(unsigned char *data, size_t len) { + size_t i; + + for (i = 0; i < len; i++) { + if (!isspace(data[i])) { + return 0; + } + } + + return 1; +} + +/** + * Parses Content-Length string (positive decimal number). + * White space is allowed before and after the number. + * + * @param[in] b + * @return Content-Length as a number, or -1 on error. + */ +int64_t htp_parse_content_length(bstr *b, htp_connp_t *connp) { + size_t len = bstr_len(b); + unsigned char * data = (unsigned char *) bstr_ptr(b); + size_t pos = 0; + int64_t r = 0; + + if (len == 0) return -1003; + + // Ignore junk before + while ((pos < len) && (data[pos] < '0' || data[pos] > '9')) { + if (!htp_is_lws(data[pos]) && connp != NULL && r == 0) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "C-L value with extra data in the beginning"); + r = -1; + } + pos++; + } + if (pos == len) return -1001; + + r = bstr_util_mem_to_pint(data + pos, len - pos, 10, &pos); + // Ok to have junk afterwards + if (pos < len && connp != NULL) { + htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, + "C-L value with extra data in the end"); + } + return r; +} + +/** + * Parses chunk length (positive hexadecimal number). White space is allowed before + * and after the number. An error will be returned if the chunk length is greater than + * INT32_MAX. + * + * @param[in] data + * @param[in] len + * @return Chunk length, or a negative number on error. + */ +int64_t htp_parse_chunked_length(unsigned char *data, size_t len, int *extension) { + // skip leading line feeds and other control chars + while (len) { + unsigned char c = *data; + if (!(c == 0x0d || c == 0x0a || c == 0x20 || c == 0x09 || c == 0x0b || c == 0x0c)) + break; + data++; + len--; + } + if (len == 0) + return -1004; + + // find how much of the data is correctly formatted + size_t i = 0; + while (i < len) { + unsigned char c = data[i]; + if (!(isdigit(c) || + (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) + break; + i++; + } + // cut off trailing junk + if (i != len) { + if (extension) { + size_t j = i; + while (j < len) { + if (data[j] == ';') { + *extension = 1; + break; + } + j++; + } + } + len = i; + } + + int64_t chunk_len = htp_parse_positive_integer_whitespace(data, len, 16); + if (chunk_len < 0) return chunk_len; + if (chunk_len > INT32_MAX) return -1; + return chunk_len; +} + +/** + * A somewhat forgiving parser for a positive integer in a given base. + * Only LWS is allowed before and after the number. + * + * @param[in] data + * @param[in] len + * @param[in] base + * @return The parsed number on success; a negative number on error. + */ +int64_t htp_parse_positive_integer_whitespace(unsigned char *data, size_t len, int base) { + if (len == 0) return -1003; + + size_t last_pos; + size_t pos = 0; + + // Ignore LWS before + while ((pos < len) && (htp_is_lws(data[pos]))) pos++; + if (pos == len) return -1001; + + int64_t r = bstr_util_mem_to_pint(data + pos, len - pos, base, &last_pos); + if (r < 0) return r; + + // Move after the last digit + pos += last_pos; + + // Ignore LWS after + while (pos < len) { + if (!htp_is_lws(data[pos])) { + return -1002; + } + + pos++; + } + + return r; +} + +#ifdef HTP_DEBUG + +/** + * Prints one log message to stderr. + * + * @param[in] stream + * @param[in] log + */ +void htp_print_log(FILE *stream, htp_log_t *log) { + if (log->code != 0) { + fprintf(stream, "[%d][code %d][file %s][line %d] %s\n", log->level, + log->code, log->file, log->line, log->msg); + } else { + fprintf(stream, "[%d][file %s][line %d] %s\n", log->level, + log->file, log->line, log->msg); + } +} +#endif + +/** + * Records one log message. + * + * @param[in] connp + * @param[in] file + * @param[in] line + * @param[in] level + * @param[in] code + * @param[in] fmt + */ +void htp_log(htp_connp_t *connp, const char *file, int line, enum htp_log_level_t level, int code, const char *fmt, ...) { + if (connp == NULL) return; + + char buf[1024]; + va_list args; + + // Ignore messages below our log level. + if (connp->cfg->log_level < level) { + return; + } + + va_start(args, fmt); + + int r = vsnprintf(buf, 1024, fmt, args); + + va_end(args); + + if (r < 0) { + snprintf(buf, 1024, "[vnsprintf returned error %d]", r); + } else if (r >= 1024) { + // Indicate overflow with a '+' at the end. + buf[1022] = '+'; + buf[1023] = '\0'; + } + + // Create a new log entry. + + htp_log_t *log = calloc(1, sizeof (htp_log_t)); + if (log == NULL) return; + + log->connp = connp; + log->file = file; + log->line = line; + log->level = level; + log->code = code; + log->msg = strdup(buf); + + if (htp_list_add(connp->conn->messages, log) != HTP_OK) { + free((void *) log->msg); + free(log); + return; + } + + if (level == HTP_LOG_ERROR) { + connp->last_error = log; + } + + #ifdef HTP_DEBUG + fprintf(stderr, "[LOG] %s\n", log->msg); + #endif + + /* coverity[check_return] */ + htp_hook_run_all(connp->cfg->hook_log, log); +} + +/** + * Determines if the given line is a continuation (of some previous line). + * + * @param[in] data + * @param[in] len + * @return 0 or 1 for false and true, respectively. Returns -1 on error (NULL pointer or length zero). + */ +int htp_connp_is_line_folded(unsigned char *data, size_t len) { + if ((data == NULL) || (len == 0)) return -1; + return htp_is_folding_char(data[0]); +} + +int htp_is_folding_char(int c) { + if (htp_is_lws(c) || c == 0) return 1; + else return 0; +} + +/** + * Determines if the given line is a request terminator. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return 0 or 1 + */ +int htp_connp_is_line_terminator(htp_connp_t *connp, unsigned char *data, size_t len, int next_no_lf) { + // Is this the end of request headers? + switch (connp->cfg->server_personality) { + case HTP_SERVER_IIS_5_1: + // IIS 5 will accept a whitespace line as a terminator + if (htp_is_line_whitespace(data, len)) { + return 1; + } + + // Fall through + default: + // Treat an empty line as terminator + if (htp_is_line_empty(data, len)) { + return 1; + } + // Only space is terminator if terminator does not follow right away + if (len == 2 && htp_is_lws(data[0]) && data[1] == LF) { + return next_no_lf; + } + break; + } + + return 0; +} + +/** + * Determines if the given line can be ignored when it appears before a request. + * + * @param[in] connp + * @param[in] data + * @param[in] len + * @return 0 or 1 + */ +int htp_connp_is_line_ignorable(htp_connp_t *connp, unsigned char *data, size_t len) { + return htp_connp_is_line_terminator(connp, data, len, 0); +} + +static htp_status_t htp_parse_port(unsigned char *data, size_t len, int *port, int *invalid) { + if (len == 0) { + *port = -1; + *invalid = 1; + return HTP_OK; + } + + int64_t port_parsed = htp_parse_positive_integer_whitespace(data, len, 10); + + if (port_parsed < 0) { + // Failed to parse the port number. + *port = -1; + *invalid = 1; + } else if ((port_parsed > 0) && (port_parsed < 65536)) { + // Valid port number. + *port = (int) port_parsed; + } else { + // Port number out of range. + *port = -1; + *invalid = 1; + } + + return HTP_OK; +} + +/** + * Parses an authority string, which consists of a hostname with an optional port number; username + * and password are not allowed and will not be handled. + * + * @param[in] hostport + * @param[out] hostname A bstring containing the hostname, or NULL if the hostname is invalid. If this value + * is not NULL, the caller assumes responsibility for memory management. + * @param[out] port Port as text, or NULL if not provided. + * @param[out] port_number Port number, or -1 if the port is not present or invalid. + * @param[out] invalid Set to 1 if any part of the authority is invalid. + * @return HTP_OK on success, HTP_ERROR on memory allocation failure. + */ +htp_status_t htp_parse_hostport(bstr *hostport, bstr **hostname, bstr **port, int *port_number, int *invalid) { + if ((hostport == NULL) || (hostname == NULL) || (port_number == NULL) || (invalid == NULL)) return HTP_ERROR; + + *hostname = NULL; + if (port != NULL) { + *port = NULL; + } + *port_number = -1; + *invalid = 0; + + unsigned char *data = bstr_ptr(hostport); + size_t len = bstr_len(hostport); + + bstr_util_mem_trim(&data, &len); + + if (len == 0) { + *invalid = 1; + return HTP_OK; + } + + // Check for an IPv6 address. + if (data[0] == '[') { + // IPv6 host. + + // Find the end of the IPv6 address. + size_t pos = 0; + while ((pos < len) && (data[pos] != ']')) pos++; + if (pos == len) { + *invalid = 1; + return HTP_OK; + } + + *hostname = bstr_dup_mem(data, pos + 1); + if (*hostname == NULL) return HTP_ERROR; + + // Over the ']'. + pos++; + if (pos == len) return HTP_OK; + + // Handle port. + if (data[pos] == ':') { + if (port != NULL) { + *port = bstr_dup_mem(data + pos + 1, len - pos - 1); + if (*port == NULL) { + bstr_free(*hostname); + return HTP_ERROR; + } + } + + return htp_parse_port(data + pos + 1, len - pos - 1, port_number, invalid); + } else { + *invalid = 1; + return HTP_OK; + } + } else { + // Not IPv6 host. + + // Is there a colon? + unsigned char *colon = memchr(data, ':', len); + if (colon == NULL) { + // Hostname alone, no port. + + *hostname = bstr_dup_mem(data, len); + if (*hostname == NULL) return HTP_ERROR; + + bstr_to_lowercase(*hostname); + } else { + // Hostname and port. + + // Ignore whitespace at the end of hostname. + unsigned char *hostend = colon; + while ((hostend > data) && (isspace(*(hostend - 1)))) hostend--; + + *hostname = bstr_dup_mem(data, hostend - data); + if (*hostname == NULL) return HTP_ERROR; + + if (port != NULL) { + *port = bstr_dup_mem(colon + 1, len - (colon + 1 - data)); + if (*port == NULL) { + bstr_free(*hostname); + return HTP_ERROR; + } + } + + return htp_parse_port(colon + 1, len - (colon + 1 - data), port_number, invalid); + } + } + + return HTP_OK; +} + +/** + * Parses hostport provided in the URI. + * + * @param[in] connp + * @param[in] hostport + * @param[in] uri + * @return HTP_OK on success or HTP_ERROR error. + */ +int htp_parse_uri_hostport(htp_connp_t *connp, bstr *hostport, htp_uri_t *uri) { + int invalid; + + htp_status_t rc = htp_parse_hostport(hostport, &(uri->hostname), &(uri->port), &(uri->port_number), &invalid); + if (rc != HTP_OK) return rc; + + if (invalid) { + connp->in_tx->flags |= HTP_HOSTU_INVALID; + } + + if (uri->hostname != NULL) { + if (htp_validate_hostname(uri->hostname) == 0) { + connp->in_tx->flags |= HTP_HOSTU_INVALID; + } + } + + return HTP_OK; +} + +/** + * Parses hostport provided in the Host header. + * + * @param[in] hostport + * @param[out] hostname + * @param[out] port + * @param[out] port_number + * @param[out] flags + * @return HTP_OK on success or HTP_ERROR error. + */ +htp_status_t htp_parse_header_hostport(bstr *hostport, bstr **hostname, bstr **port, int *port_number, uint64_t *flags) { + int invalid; + + htp_status_t rc = htp_parse_hostport(hostport, hostname, port, port_number, &invalid); + if (rc != HTP_OK) return rc; + + if (invalid) { + *flags |= HTP_HOSTH_INVALID; + } + + if (*hostname != NULL) { + if (htp_validate_hostname(*hostname) == 0) { + *flags |= HTP_HOSTH_INVALID; + } + } + + return HTP_OK; +} + +/** + * Parses request URI, making no attempt to validate the contents. + * + * @param[in] input + * @param[in] uri + * @return HTP_ERROR on memory allocation failure, HTP_OK otherwise + */ +int htp_parse_uri(bstr *input, htp_uri_t **uri) { + // Allow a htp_uri_t structure to be provided on input, + // but allocate a new one if the structure is NULL. + if (*uri == NULL) { + *uri = calloc(1, sizeof (htp_uri_t)); + if (*uri == NULL) return HTP_ERROR; + } + + if (input == NULL) { + // The input might be NULL on requests that don't actually + // contain the URI. We allow that. + return HTP_OK; + } + + unsigned char *data = bstr_ptr(input); + size_t len = bstr_len(input); + // remove trailing spaces + while (len > 0) { + if (data[len-1] != ' ') { + break; + } + len--; + } + size_t start, pos; + + if (len == 0) { + // Empty string. + return HTP_OK; + } + + pos = 0; + + // Scheme test: if it doesn't start with a forward slash character (which it must + // for the contents to be a path or an authority, then it must be the scheme part + if (data[0] != '/') { + // Parse scheme + + // Find the colon, which marks the end of the scheme part + start = pos; + while ((pos < len) && (data[pos] != ':')) pos++; + + if (pos >= len) { + // We haven't found a colon, which means that the URI + // is invalid. Apache will ignore this problem and assume + // the URI contains an invalid path so, for the time being, + // we are going to do the same. + pos = 0; + } else { + // Make a copy of the scheme + (*uri)->scheme = bstr_dup_mem(data + start, pos - start); + if ((*uri)->scheme == NULL) return HTP_ERROR; + + // Go over the colon + pos++; + } + } + + // Authority test: two forward slash characters and it's an authority. + // One, three or more slash characters, and it's a path. We, however, + // only attempt to parse authority if we've seen a scheme. + if ((*uri)->scheme != NULL) + if ((pos + 2 < len) && (data[pos] == '/') && (data[pos + 1] == '/') && (data[pos + 2] != '/')) { + // Parse authority + + // Go over the two slash characters + start = pos = pos + 2; + + // Authority ends with a question mark, forward slash or hash + while ((pos < len) && (data[pos] != '?') && (data[pos] != '/') && (data[pos] != '#')) pos++; + + unsigned char *hostname_start; + size_t hostname_len; + + // Are the credentials included in the authority? + unsigned char *m = memchr(data + start, '@', pos - start); + if (m != NULL) { + // Credentials present + unsigned char *credentials_start = data + start; + size_t credentials_len = m - data - start; + + // Figure out just the hostname part + hostname_start = data + start + credentials_len + 1; + hostname_len = pos - start - credentials_len - 1; + + // Extract the username and the password + m = memchr(credentials_start, ':', credentials_len); + if (m != NULL) { + // Username and password + (*uri)->username = bstr_dup_mem(credentials_start, m - credentials_start); + if ((*uri)->username == NULL) return HTP_ERROR; + (*uri)->password = bstr_dup_mem(m + 1, credentials_len - (m - credentials_start) - 1); + if ((*uri)->password == NULL) return HTP_ERROR; + } else { + // Username alone + (*uri)->username = bstr_dup_mem(credentials_start, credentials_len); + if ((*uri)->username == NULL) return HTP_ERROR; + } + } else { + // No credentials + hostname_start = data + start; + hostname_len = pos - start; + } + + // Parsing authority without credentials. + if ((hostname_len > 0) && (hostname_start[0] == '[')) { + // IPv6 address. + + m = memchr(hostname_start, ']', hostname_len); + if (m == NULL) { + // Invalid IPv6 address; use the entire string as hostname. + (*uri)->hostname = bstr_dup_mem(hostname_start, hostname_len); + if ((*uri)->hostname == NULL) return HTP_ERROR; + } else { + (*uri)->hostname = bstr_dup_mem(hostname_start, m - hostname_start + 1); + if ((*uri)->hostname == NULL) return HTP_ERROR; + + // Is there a port? + hostname_len = hostname_len - (m - hostname_start + 1); + hostname_start = m + 1; + + // Port string + m = memchr(hostname_start, ':', hostname_len); + if (m != NULL) { + size_t port_len = hostname_len - (m - hostname_start) - 1; + (*uri)->port = bstr_dup_mem(m + 1, port_len); + if ((*uri)->port == NULL) return HTP_ERROR; + } + } + } else { + // Not IPv6 address. + + m = memchr(hostname_start, ':', hostname_len); + if (m != NULL) { + size_t port_len = hostname_len - (m - hostname_start) - 1; + hostname_len = hostname_len - port_len - 1; + + // Port string + (*uri)->port = bstr_dup_mem(m + 1, port_len); + if ((*uri)->port == NULL) return HTP_ERROR; + } + + // Hostname + (*uri)->hostname = bstr_dup_mem(hostname_start, hostname_len); + if ((*uri)->hostname == NULL) return HTP_ERROR; + } + } + + // Path + start = pos; + + // The path part will end with a question mark or a hash character, which + // mark the beginning of the query part or the fragment part, respectively. + while ((pos < len) && (data[pos] != '?') && (data[pos] != '#')) pos++; + + // Path + (*uri)->path = bstr_dup_mem(data + start, pos - start); + if ((*uri)->path == NULL) return HTP_ERROR; + + if (pos == len) return HTP_OK; + + // Query + if (data[pos] == '?') { + // Step over the question mark + start = pos + 1; + + // The query part will end with the end of the input + // or the beginning of the fragment part + while ((pos < len) && (data[pos] != '#')) pos++; + + // Query string + (*uri)->query = bstr_dup_mem(data + start, pos - start); + if ((*uri)->query == NULL) return HTP_ERROR; + + if (pos == len) return HTP_OK; + } + + // Fragment + if (data[pos] == '#') { + // Step over the hash character + start = pos + 1; + + // Fragment; ends with the end of the input + (*uri)->fragment = bstr_dup_mem(data + start, len - start); + if ((*uri)->fragment == NULL) return HTP_ERROR; + } + + return HTP_OK; +} + +/** + * Convert two input bytes, pointed to by the pointer parameter, + * into a single byte by assuming the input consists of hexadecimal + * characters. This function will happily convert invalid input. + * + * @param[in] what + * @return hex-decoded byte + */ +static unsigned char x2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); + + return digit; +} + +/** + * Convert a Unicode codepoint into a single-byte, using best-fit + * mapping (as specified in the provided configuration structure). + * + * @param[in] cfg + * @param[in] codepoint + * @return converted single byte + */ +static uint8_t bestfit_codepoint(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, uint32_t codepoint) { + // Is it a single-byte codepoint? + if (codepoint < 0x100) { + return (uint8_t) codepoint; + } + + // Our current implementation converts only the 2-byte codepoints. + if (codepoint > 0xffff) { + return cfg->decoder_cfgs[ctx].bestfit_replacement_byte; + } + + uint8_t *p = cfg->decoder_cfgs[ctx].bestfit_map; + + // TODO Optimize lookup. + + for (;;) { + uint32_t x = (p[0] << 8) + p[1]; + + if (x == 0) { + return cfg->decoder_cfgs[ctx].bestfit_replacement_byte; + } + + if (x == codepoint) { + return p[2]; + } + + // Move to the next triplet + p += 3; + } +} + +/** + * Decode a UTF-8 encoded path. Overlong characters will be decoded, invalid + * characters will be left as-is. Best-fit mapping will be used to convert + * UTF-8 into a single-byte stream. + * + * @param[in] cfg + * @param[in] tx + * @param[in] path + */ +void htp_utf8_decode_path_inplace(htp_cfg_t *cfg, htp_tx_t *tx, bstr *path) { + if (path == NULL) return; + + uint8_t *data = bstr_ptr(path); + if (data == NULL) return; + + size_t len = bstr_len(path); + size_t rpos = 0; + size_t wpos = 0; + uint32_t codepoint = 0; + uint32_t state = HTP_UTF8_ACCEPT; + uint32_t counter = 0; + uint8_t seen_valid = 0; + + while ((rpos < len)&&(wpos < len)) { + counter++; + + switch (htp_utf8_decode_allow_overlong(&state, &codepoint, data[rpos])) { + case HTP_UTF8_ACCEPT: + if (counter == 1) { + // ASCII character, which we just copy. + data[wpos++] = (uint8_t) codepoint; + } else { + // A valid UTF-8 character, which we need to convert. + + seen_valid = 1; + + // Check for overlong characters and set the flag accordingly. + switch (counter) { + case 2: + if (codepoint < 0x80) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + case 3: + if (codepoint < 0x800) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + case 4: + if (codepoint < 0x10000) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + } + + // Special flag for half-width/full-width evasion. + if ((codepoint >= 0xff00) && (codepoint <= 0xffef)) { + tx->flags |= HTP_PATH_HALF_FULL_RANGE; + } + + // Use best-fit mapping to convert to a single byte. + data[wpos++] = bestfit_codepoint(cfg, HTP_DECODER_URL_PATH, codepoint); + } + + // Advance over the consumed byte and reset the byte counter. + rpos++; + counter = 0; + + break; + + case HTP_UTF8_REJECT: + // Invalid UTF-8 character. + + tx->flags |= HTP_PATH_UTF8_INVALID; + + // Is the server expected to respond with 400? + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].utf8_invalid_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].utf8_invalid_unwanted; + } + + // Output the replacement byte, replacing one or more invalid bytes. + data[wpos++] = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].bestfit_replacement_byte; + + // If the invalid byte was first in a sequence, consume it. Otherwise, + // assume it's the starting byte of the next character. + if (counter == 1) { + rpos++; + } + + // Reset the decoder state and continue decoding. + state = HTP_UTF8_ACCEPT; + codepoint = 0; + counter = 0; + + break; + + default: + // Keep going; the character is not yet formed. + rpos++; + break; + } + } + + // Did the input stream seem like a valid UTF-8 string? + if ((seen_valid) && (!(tx->flags & HTP_PATH_UTF8_INVALID))) { + tx->flags |= HTP_PATH_UTF8_VALID; + } + + // Adjust the length of the string, because + // we're doing in-place decoding. + bstr_adjust_len(path, wpos); +} + +/** + * Validate a path that is quite possibly UTF-8 encoded. + * + * @param[in] tx + * @param[in] path + */ +void htp_utf8_validate_path(htp_tx_t *tx, bstr *path) { + unsigned char *data = bstr_ptr(path); + size_t len = bstr_len(path); + size_t rpos = 0; + uint32_t codepoint = 0; + uint32_t state = HTP_UTF8_ACCEPT; + uint32_t counter = 0; // How many bytes used by a UTF-8 character. + uint8_t seen_valid = 0; + + while (rpos < len) { + counter++; + + switch (htp_utf8_decode_allow_overlong(&state, &codepoint, data[rpos])) { + case HTP_UTF8_ACCEPT: + // We have a valid character. + + if (counter > 1) { + // A valid UTF-8 character, consisting of 2 or more bytes. + + seen_valid = 1; + + // Check for overlong characters and set the flag accordingly. + switch (counter) { + case 2: + if (codepoint < 0x80) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + case 3: + if (codepoint < 0x800) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + case 4: + if (codepoint < 0x10000) { + tx->flags |= HTP_PATH_UTF8_OVERLONG; + } + break; + } + } + + // Special flag for half-width/full-width evasion. + if ((codepoint > 0xfeff) && (codepoint < 0x010000)) { + tx->flags |= HTP_PATH_HALF_FULL_RANGE; + } + + // Advance over the consumed byte and reset the byte counter. + rpos++; + counter = 0; + + break; + + case HTP_UTF8_REJECT: + // Invalid UTF-8 character. + + tx->flags |= HTP_PATH_UTF8_INVALID; + + // Override the decoder state because we want to continue decoding. + state = HTP_UTF8_ACCEPT; + + // Advance over the consumed byte and reset the byte counter. + rpos++; + counter = 0; + + break; + + default: + // Keep going; the character is not yet formed. + rpos++; + break; + } + } + + // Did the input stream seem like a valid UTF-8 string? + if ((seen_valid) && (!(tx->flags & HTP_PATH_UTF8_INVALID))) { + tx->flags |= HTP_PATH_UTF8_VALID; + } +} + +/** + * Decode a %u-encoded character, using best-fit mapping as necessary. Path version. + * + * @param[in] cfg + * @param[in] tx + * @param[in] data + * @return decoded byte + */ +static uint8_t decode_u_encoding_path(htp_cfg_t *cfg, htp_tx_t *tx, unsigned char *data) { + uint8_t c1 = x2c(data); + uint8_t c2 = x2c(data + 2); + uint8_t r = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].bestfit_replacement_byte; + + if (c1 == 0x00) { + r = c2; + tx->flags |= HTP_PATH_OVERLONG_U; + } else { + // Check for fullwidth form evasion + if (c1 == 0xff) { + tx->flags |= HTP_PATH_HALF_FULL_RANGE; + } + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].u_encoding_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].u_encoding_unwanted; + } + + // Use best-fit mapping + unsigned char *p = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].bestfit_map; + + // TODO Optimize lookup. + + for (;;) { + // Have we reached the end of the map? + if ((p[0] == 0) && (p[1] == 0)) { + break; + } + + // Have we found the mapping we're looking for? + if ((p[0] == c1) && (p[1] == c2)) { + r = p[2]; + break; + } + + // Move to the next triplet + p += 3; + } + } + + // Check for encoded path separators + if ((r == '/') || ((cfg->decoder_cfgs[HTP_DECODER_URL_PATH].backslash_convert_slashes) && (r == '\\'))) { + tx->flags |= HTP_PATH_ENCODED_SEPARATOR; + } + + return r; +} + +/** + * Decode a %u-encoded character, using best-fit mapping as necessary. Params version. + * + * @param[in] cfg + * @param[in] tx + * @param[in] data + * @return decoded byte + */ +static uint8_t decode_u_encoding_params(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, unsigned char *data, uint64_t *flags) { + uint8_t c1 = x2c(data); + uint8_t c2 = x2c(data + 2); + + // Check for overlong usage first. + if (c1 == 0) { + (*flags) |= HTP_URLEN_OVERLONG_U; + return c2; + } + + // Both bytes were used. + + // Detect half-width and full-width range. + if ((c1 == 0xff) && (c2 <= 0xef)) { + (*flags) |= HTP_URLEN_HALF_FULL_RANGE; + } + + // Use best-fit mapping. + unsigned char *p = cfg->decoder_cfgs[ctx].bestfit_map; + uint8_t r = cfg->decoder_cfgs[ctx].bestfit_replacement_byte; + + // TODO Optimize lookup. + + for (;;) { + // Have we reached the end of the map? + if ((p[0] == 0) && (p[1] == 0)) { + break; + } + + // Have we found the mapping we're looking for? + if ((p[0] == c1) && (p[1] == c2)) { + r = p[2]; + break; + } + + // Move to the next triplet + p += 3; + } + + return r; +} + +/** + * Decode a request path according to the settings in the + * provided configuration structure. + * + * @param[in] cfg + * @param[in] tx + * @param[in] path + */ +htp_status_t htp_decode_path_inplace(htp_tx_t *tx, bstr *path) { + if (path == NULL) return HTP_ERROR; + unsigned char *data = bstr_ptr(path); + if (data == NULL) return HTP_ERROR; + + size_t len = bstr_len(path); + + htp_cfg_t *cfg = tx->cfg; + + size_t rpos = 0; + size_t wpos = 0; + int previous_was_separator = 0; + + while ((rpos < len) && (wpos < len)) { + uint8_t c = data[rpos]; + + // Decode encoded characters + if (c == '%') { + if (rpos + 2 < len) { + int handled = 0; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].u_encoding_decode) { + // Check for the %u encoding + if ((data[rpos + 1] == 'u') || (data[rpos + 1] == 'U')) { + handled = 1; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].u_encoding_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].u_encoding_unwanted; + } + + if (rpos + 5 < len) { + if (isxdigit(data[rpos + 2]) && (isxdigit(data[rpos + 3])) + && isxdigit(data[rpos + 4]) && (isxdigit(data[rpos + 5]))) { + // Decode a valid %u encoding + c = decode_u_encoding_path(cfg, tx, &data[rpos + 2]); + rpos += 6; + + if (c == 0) { + tx->flags |= HTP_PATH_ENCODED_NUL; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_encoded_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_encoded_unwanted; + } + } + } else { + // Invalid %u encoding + tx->flags |= HTP_PATH_INVALID_ENCODING; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; eat + // the percent character + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the percent character in output + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Decode invalid %u encoding + c = decode_u_encoding_path(cfg, tx, &data[rpos + 2]); + rpos += 6; + break; + } + } + } else { + // Invalid %u encoding (not enough data) + tx->flags |= HTP_PATH_INVALID_ENCODING; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; eat + // the percent character + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the percent character in output + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Cannot decode, because there's not enough data. + // Leave the percent character in output + rpos++; + // TODO Configurable handling. + break; + } + } + } + } + + // Handle standard URL encoding + if (!handled) { + if ((isxdigit(data[rpos + 1])) && (isxdigit(data[rpos + 2]))) { + c = x2c(&data[rpos + 1]); + + if (c == 0) { + tx->flags |= HTP_PATH_ENCODED_NUL; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_encoded_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_encoded_unwanted; + } + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_encoded_terminates) { + bstr_adjust_len(path, wpos); + return HTP_OK; + } + } + + if ((c == '/') || ((cfg->decoder_cfgs[HTP_DECODER_URL_PATH].backslash_convert_slashes) && (c == '\\'))) { + tx->flags |= HTP_PATH_ENCODED_SEPARATOR; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].path_separators_encoded_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].path_separators_encoded_unwanted; + } + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].path_separators_decode) { + // Decode + rpos += 3; + } else { + // Leave encoded + c = '%'; + rpos++; + } + } else { + // Decode + rpos += 3; + } + } else { + // Invalid encoding + tx->flags |= HTP_PATH_INVALID_ENCODING; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; eat + // the percent character + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the percent character in output + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Decode + c = x2c(&data[rpos + 1]); + rpos += 3; + // Note: What if an invalid encoding decodes into a path + // separator? This is theoretical at the moment, because + // the only platform we know doesn't convert separators is + // Apache, who will also respond with 400 if invalid encoding + // is encountered. Thus no check for a separator here. + break; + default: + // Unknown setting + return HTP_ERROR; + break; + } + } + } + } else { + // Invalid URL encoding (not enough data) + tx->flags |= HTP_PATH_INVALID_ENCODING; + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; eat + // the percent character + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the percent character in output + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Cannot decode, because there's not enough data. + // Leave the percent character in output. + // TODO Configurable handling. + rpos++; + break; + } + } + } else { + // One non-encoded character + + // Is it a NUL byte? + if (c == 0) { + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_raw_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_raw_unwanted; + } + + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].nul_raw_terminates) { + // Terminate path with a raw NUL byte + bstr_adjust_len(path, wpos); + return HTP_OK; + break; + } + } + + rpos++; + } + + // Place the character into output + + // Check for control characters + if (c < 0x20) { + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].control_chars_unwanted != HTP_UNWANTED_IGNORE) { + tx->response_status_expected_number = cfg->decoder_cfgs[HTP_DECODER_URL_PATH].control_chars_unwanted; + } + } + + // Convert backslashes to forward slashes, if necessary + if ((c == '\\') && (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].backslash_convert_slashes)) { + c = '/'; + } + + // Lowercase characters, if necessary + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].convert_lowercase) { + c = (uint8_t) tolower(c); + } + + // If we're compressing separators then we need + // to track if the previous character was a separator + if (cfg->decoder_cfgs[HTP_DECODER_URL_PATH].path_separators_compress) { + if (c == '/') { + if (!previous_was_separator) { + data[wpos++] = c; + previous_was_separator = 1; + } else { + // Do nothing; we don't want + // another separator in output + } + } else { + data[wpos++] = c; + previous_was_separator = 0; + } + } else { + data[wpos++] = c; + } + } + + bstr_adjust_len(path, wpos); + + return HTP_OK; +} + +htp_status_t htp_tx_urldecode_uri_inplace(htp_tx_t *tx, bstr *input) { + uint64_t flags = 0; + + htp_status_t rc = htp_urldecode_inplace_ex(tx->cfg, HTP_DECODER_URL_PATH, input, &flags, &(tx->response_status_expected_number)); + + if (flags & HTP_URLEN_INVALID_ENCODING) { + tx->flags |= HTP_PATH_INVALID_ENCODING; + } + + if (flags & HTP_URLEN_ENCODED_NUL) { + tx->flags |= HTP_PATH_ENCODED_NUL; + } + + if (flags & HTP_URLEN_RAW_NUL) { + tx->flags |= HTP_PATH_RAW_NUL; + } + + return rc; +} + +htp_status_t htp_tx_urldecode_params_inplace(htp_tx_t *tx, bstr *input) { + return htp_urldecode_inplace_ex(tx->cfg, HTP_DECODER_URLENCODED, input, &(tx->flags), &(tx->response_status_expected_number)); +} + +htp_status_t htp_urldecode_inplace(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags) { + int expected_status_code = 0; + return htp_urldecode_inplace_ex(cfg, ctx, input, flags, &expected_status_code); +} + +htp_status_t htp_urldecode_inplace_ex(htp_cfg_t *cfg, enum htp_decoder_ctx_t ctx, bstr *input, uint64_t *flags, int *expected_status_code) { + if (input == NULL) return HTP_ERROR; + + unsigned char *data = bstr_ptr(input); + if (data == NULL) return HTP_ERROR; + size_t len = bstr_len(input); + + size_t rpos = 0; + size_t wpos = 0; + + while ((rpos < len) && (wpos < len)) { + uint8_t c = data[rpos]; + + // Decode encoded characters. + if (c == '%') { + // Need at least 2 additional bytes for %HH. + if (rpos + 2 < len) { + int handled = 0; + + // Decode %uHHHH encoding, but only if allowed in configuration. + if (cfg->decoder_cfgs[ctx].u_encoding_decode) { + // The next character must be a case-insensitive u. + if ((data[rpos + 1] == 'u') || (data[rpos + 1] == 'U')) { + handled = 1; + + if (cfg->decoder_cfgs[ctx].u_encoding_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].u_encoding_unwanted; + } + + // Need at least 5 additional bytes for %uHHHH. + if (rpos + 5 < len) { + if (isxdigit(data[rpos + 2]) && (isxdigit(data[rpos + 3])) + && isxdigit(data[rpos + 4]) && (isxdigit(data[rpos + 5]))) { + // Decode a valid %u encoding. + c = decode_u_encoding_params(cfg, ctx, &(data[rpos + 2]), flags); + rpos += 6; + } else { + // Invalid %u encoding (could not find 4 xdigits). + (*flags) |= HTP_URLEN_INVALID_ENCODING; + + if (cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[ctx].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; consume the %. + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the % in output. + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Decode invalid %u encoding. + c = decode_u_encoding_params(cfg, ctx, &(data[rpos + 2]), flags); + rpos += 6; + break; + } + } + } else { + // Invalid %u encoding; not enough data. + (*flags) |= HTP_URLEN_INVALID_ENCODING; + + if (cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[ctx].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; consume the %. + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the % in output. + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Cannot decode because there's not enough data. + // Leave the % in output. + // TODO Configurable handling of %, u, etc. + rpos++; + break; + } + } + } + } + + // Handle standard URL encoding. + if (!handled) { + // Need 2 hexadecimal digits. + if ((isxdigit(data[rpos + 1])) && (isxdigit(data[rpos + 2]))) { + // Decode %HH encoding. + c = x2c(&(data[rpos + 1])); + rpos += 3; + } else { + // Invalid encoding (enough bytes, but not hexadecimal digits). + (*flags) |= HTP_URLEN_INVALID_ENCODING; + + if (cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[ctx].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; consume the %. + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the % in output. + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Decode. + c = x2c(&(data[rpos + 1])); + rpos += 3; + break; + } + } + } + } else { + // Invalid encoding; not enough data (at least 2 bytes required). + (*flags) |= HTP_URLEN_INVALID_ENCODING; + + if (cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].url_encoding_invalid_unwanted; + } + + switch (cfg->decoder_cfgs[ctx].url_encoding_invalid_handling) { + case HTP_URL_DECODE_REMOVE_PERCENT: + // Do not place anything in output; consume the %. + rpos++; + continue; + break; + case HTP_URL_DECODE_PRESERVE_PERCENT: + // Leave the % in output. + rpos++; + break; + case HTP_URL_DECODE_PROCESS_INVALID: + // Cannot decode because there's not enough data. + // Leave the % in output. + // TODO Configurable handling of %, etc. + rpos++; + break; + } + } + + // Did we get an encoded NUL byte? + if (c == 0) { + if (cfg->decoder_cfgs[ctx].nul_encoded_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].nul_encoded_unwanted; + } + + (*flags) |= HTP_URLEN_ENCODED_NUL; + + if (cfg->decoder_cfgs[ctx].nul_encoded_terminates) { + // Terminate the path at the raw NUL byte. + bstr_adjust_len(input, wpos); + return 1; + } + } + + data[wpos++] = c; + } else if (c == '+') { + // Decoding of the plus character is conditional on the configuration. + + if (cfg->decoder_cfgs[ctx].plusspace_decode) { + c = 0x20; + } + + rpos++; + data[wpos++] = c; + } else { + // One non-encoded byte. + + // Did we get a raw NUL byte? + if (c == 0) { + if (cfg->decoder_cfgs[ctx].nul_raw_unwanted != HTP_UNWANTED_IGNORE) { + (*expected_status_code) = cfg->decoder_cfgs[ctx].nul_raw_unwanted; + } + + (*flags) |= HTP_URLEN_RAW_NUL; + + if (cfg->decoder_cfgs[ctx].nul_raw_terminates) { + // Terminate the path at the encoded NUL byte. + bstr_adjust_len(input, wpos); + return HTP_OK; + } + } + + rpos++; + data[wpos++] = c; + } + } + + bstr_adjust_len(input, wpos); + + return HTP_OK; +} + +/** + * Normalize a previously-parsed request URI. + * + * @param[in] connp + * @param[in] incomplete + * @param[in] normalized + * @return HTP_OK or HTP_ERROR + */ +int htp_normalize_parsed_uri(htp_tx_t *tx, htp_uri_t *incomplete, htp_uri_t *normalized) { + // Scheme. + if (incomplete->scheme != NULL) { + // Duplicate and convert to lowercase. + normalized->scheme = bstr_dup_lower(incomplete->scheme); + if (normalized->scheme == NULL) return HTP_ERROR; + } + + // Username. + if (incomplete->username != NULL) { + normalized->username = bstr_dup(incomplete->username); + if (normalized->username == NULL) return HTP_ERROR; + htp_tx_urldecode_uri_inplace(tx, normalized->username); + } + + // Password. + if (incomplete->password != NULL) { + normalized->password = bstr_dup(incomplete->password); + if (normalized->password == NULL) return HTP_ERROR; + htp_tx_urldecode_uri_inplace(tx, normalized->password); + } + + // Hostname. + if (incomplete->hostname != NULL) { + // We know that incomplete->hostname does not contain + // port information, so no need to check for it here. + normalized->hostname = bstr_dup(incomplete->hostname); + if (normalized->hostname == NULL) return HTP_ERROR; + htp_tx_urldecode_uri_inplace(tx, normalized->hostname); + htp_normalize_hostname_inplace(normalized->hostname); + } + + // Port. + if (incomplete->port != NULL) { + int64_t port_parsed = htp_parse_positive_integer_whitespace( + bstr_ptr(incomplete->port), bstr_len(incomplete->port), 10); + + if (port_parsed < 0) { + // Failed to parse the port number. + normalized->port_number = -1; + tx->flags |= HTP_HOSTU_INVALID; + } else if ((port_parsed > 0) && (port_parsed < 65536)) { + // Valid port number. + normalized->port_number = (int) port_parsed; + } else { + // Port number out of range. + normalized->port_number = -1; + tx->flags |= HTP_HOSTU_INVALID; + } + } else { + normalized->port_number = -1; + } + + // Path. + if (incomplete->path != NULL) { + // Make a copy of the path, so that we can work on it. + normalized->path = bstr_dup(incomplete->path); + if (normalized->path == NULL) return HTP_ERROR; + + // Decode URL-encoded (and %u-encoded) characters, as well as lowercase, + // compress separators and convert backslashes. + htp_decode_path_inplace(tx, normalized->path); + + // Handle UTF-8 in the path. + if (tx->cfg->decoder_cfgs[HTP_DECODER_URL_PATH].utf8_convert_bestfit) { + // Decode Unicode characters into a single-byte stream, using best-fit mapping. + htp_utf8_decode_path_inplace(tx->cfg, tx, normalized->path); + } else { + // No decoding, but try to validate the path as a UTF-8 stream. + htp_utf8_validate_path(tx, normalized->path); + } + + // RFC normalization. + htp_normalize_uri_path_inplace(normalized->path); + } + + // Query string. + if (incomplete->query != NULL) { + normalized->query = bstr_dup(incomplete->query); + if (normalized->query == NULL) return HTP_ERROR; + } + + // Fragment. + if (incomplete->fragment != NULL) { + normalized->fragment = bstr_dup(incomplete->fragment); + if (normalized->fragment == NULL) return HTP_ERROR; + htp_tx_urldecode_uri_inplace(tx, normalized->fragment); + } + + return HTP_OK; +} + +/** + * Normalize request hostname. Convert all characters to lowercase and + * remove trailing dots from the end, if present. + * + * @param[in] hostname + * @return Normalized hostname. + */ +bstr *htp_normalize_hostname_inplace(bstr *hostname) { + if (hostname == NULL) return NULL; + + bstr_to_lowercase(hostname); + + // Remove dots from the end of the string. + while (bstr_char_at_end(hostname, 0) == '.') bstr_chop(hostname); + + return hostname; +} + +/** + * Normalize URL path. This function implements the remove dot segments algorithm + * specified in RFC 3986, section 5.2.4. + * + * @param[in] s + */ +void htp_normalize_uri_path_inplace(bstr *s) { + if (s == NULL) return; + + unsigned char *data = bstr_ptr(s); + if (data == NULL) return; + size_t len = bstr_len(s); + + size_t rpos = 0; + size_t wpos = 0; + + int c = -1; + while ((rpos < len)&&(wpos < len)) { + if (c == -1) { + c = data[rpos++]; + } + + // A. If the input buffer begins with a prefix of "../" or "./", + // then remove that prefix from the input buffer; otherwise, + if (c == '.') { + if ((rpos + 1 < len) && (data[rpos] == '.') && (data[rpos + 1] == '/')) { + c = -1; + rpos += 2; + continue; + } else if ((rpos < len) && (data[rpos] == '/')) { + c = -1; + rpos += 1; + continue; + } + } + + if (c == '/') { + // B. if the input buffer begins with a prefix of "/./" or "/.", + // where "." is a complete path segment, then replace that + // prefix with "/" in the input buffer; otherwise, + if ((rpos + 1 < len) && (data[rpos] == '.') && (data[rpos + 1] == '/')) { + c = '/'; + rpos += 2; + continue; + } else if ((rpos + 1 == len) && (data[rpos] == '.')) { + c = '/'; + rpos += 1; + continue; + } + + // C. if the input buffer begins with a prefix of "/../" or "/..", + // where ".." is a complete path segment, then replace that + // prefix with "/" in the input buffer and remove the last + // segment and its preceding "/" (if any) from the output + // buffer; otherwise, + if ((rpos + 2 < len) && (data[rpos] == '.') && (data[rpos + 1] == '.') && (data[rpos + 2] == '/')) { + c = '/'; + rpos += 3; + + // Remove the last segment + while ((wpos > 0) && (data[wpos - 1] != '/')) wpos--; + if (wpos > 0) wpos--; + continue; + } else if ((rpos + 2 == len) && (data[rpos] == '.') && (data[rpos + 1] == '.')) { + c = '/'; + rpos += 2; + + // Remove the last segment + while ((wpos > 0) && (data[wpos - 1] != '/')) wpos--; + if (wpos > 0) wpos--; + continue; + } + } + + // D. if the input buffer consists only of "." or "..", then remove + // that from the input buffer; otherwise, + if ((c == '.') && (rpos == len)) { + rpos++; + continue; + } + + if ((c == '.') && (rpos + 1 == len) && (data[rpos] == '.')) { + rpos += 2; + continue; + } + + // E. move the first path segment in the input buffer to the end of + // the output buffer, including the initial "/" character (if + // any) and any subsequent characters up to, but not including, + // the next "/" character or the end of the input buffer. + data[wpos++] = (uint8_t) c; + + while ((rpos < len) && (data[rpos] != '/') && (wpos < len)) { + data[wpos++] = data[rpos++]; + } + + c = -1; + } + + bstr_adjust_len(s, wpos); +} + +/** + * + */ +void fprint_bstr(FILE *stream, const char *name, bstr *b) { + if (b == NULL) { + fprint_raw_data_ex(stream, name, "(null)", 0, 6); + return; + } + + fprint_raw_data_ex(stream, name, bstr_ptr(b), 0, bstr_len(b)); +} + +/** + * + */ +void fprint_raw_data(FILE *stream, const char *name, const void *data, size_t len) { + // may happen for gaps + if (data == NULL) { + fprintf(stream, "\n%s: ptr NULL len %u\n", name, (unsigned int)len); + } else { + fprint_raw_data_ex(stream, name, data, 0, len); + } +} + +/** + * + */ +void fprint_raw_data_ex(FILE *stream, const char *name, const void *_data, size_t offset, size_t printlen) { + const unsigned char *data = (const unsigned char *) _data; + char buf[160]; + size_t len = offset + printlen; + + fprintf(stream, "\n%s: ptr %p offset %u len %u\n", name, (void*) data, (unsigned int)offset, (unsigned int)len); + + while (offset < len) { + size_t i; + + snprintf(buf, sizeof(buf), "%x" PRIx64, (unsigned int) offset); + strlcat(buf, " ", sizeof(buf)); + + i = 0; + while (i < 8) { + if (offset + i < len) { + char step[4]; + snprintf(step, sizeof(step), "%02x ", data[offset + i]); + strlcat(buf, step, sizeof(buf)); + } else { + strlcat(buf, " ", sizeof(buf)); + } + + i++; + } + + strlcat(buf, " ", sizeof(buf)); + + i = 8; + while (i < 16) { + if (offset + i < len) { + char step[4]; + snprintf(step, sizeof(step), "%02x ", data[offset + i]); + strlcat(buf, step, sizeof(buf)); + } else { + strlcat(buf, " ", sizeof(buf)); + } + + i++; + } + + strlcat(buf, " |", sizeof(buf)); + + i = 0; + char *p = buf + strlen(buf); + while ((offset + i < len) && (i < 16)) { + uint8_t c = data[offset + i]; + + if (isprint(c)) { + *p++ = c; + } else { + *p++ = '.'; + } + + i++; + } + + *p++ = '|'; + *p++ = '\n'; + *p = '\0'; + + fprintf(stream, "%s", buf); + offset += 16; + } + + fprintf(stream, "\n"); +} + +/** + * + */ +char *htp_connp_in_state_as_string(htp_connp_t *connp) { + if (connp == NULL) return "NULL"; + + if (connp->in_state == htp_connp_REQ_IDLE) return "REQ_IDLE"; + if (connp->in_state == htp_connp_REQ_LINE) return "REQ_LINE"; + if (connp->in_state == htp_connp_REQ_PROTOCOL) return "REQ_PROTOCOL"; + if (connp->in_state == htp_connp_REQ_HEADERS) return "REQ_HEADERS"; + if (connp->in_state == htp_connp_REQ_CONNECT_CHECK) return "REQ_CONNECT_CHECK"; + if (connp->in_state == htp_connp_REQ_CONNECT_WAIT_RESPONSE) return "REQ_CONNECT_WAIT_RESPONSE"; + if (connp->in_state == htp_connp_REQ_BODY_DETERMINE) return "REQ_BODY_DETERMINE"; + if (connp->in_state == htp_connp_REQ_BODY_IDENTITY) return "REQ_BODY_IDENTITY"; + if (connp->in_state == htp_connp_REQ_BODY_CHUNKED_LENGTH) return "REQ_BODY_CHUNKED_LENGTH"; + if (connp->in_state == htp_connp_REQ_BODY_CHUNKED_DATA) return "REQ_BODY_CHUNKED_DATA"; + if (connp->in_state == htp_connp_REQ_BODY_CHUNKED_DATA_END) return "REQ_BODY_CHUNKED_DATA_END"; + if (connp->in_state == htp_connp_REQ_FINALIZE) return "REQ_FINALIZE"; + if (connp->in_state == htp_connp_REQ_IGNORE_DATA_AFTER_HTTP_0_9) return "REQ_IGNORE_DATA_AFTER_HTTP_0_9"; + + return "UNKNOWN"; +} + +/** + * + */ +char *htp_connp_out_state_as_string(htp_connp_t *connp) { + if (connp == NULL) return "NULL"; + + if (connp->out_state == htp_connp_RES_IDLE) return "RES_IDLE"; + if (connp->out_state == htp_connp_RES_LINE) return "RES_LINE"; + if (connp->out_state == htp_connp_RES_HEADERS) return "RES_HEADERS"; + if (connp->out_state == htp_connp_RES_BODY_DETERMINE) return "RES_BODY_DETERMINE"; + if (connp->out_state == htp_connp_RES_BODY_IDENTITY_CL_KNOWN) return "RES_BODY_IDENTITY_CL_KNOWN"; + if (connp->out_state == htp_connp_RES_BODY_IDENTITY_STREAM_CLOSE) return "RES_BODY_IDENTITY_STREAM_CLOSE"; + if (connp->out_state == htp_connp_RES_BODY_CHUNKED_LENGTH) return "RES_BODY_CHUNKED_LENGTH"; + if (connp->out_state == htp_connp_RES_BODY_CHUNKED_DATA) return "RES_BODY_CHUNKED_DATA"; + if (connp->out_state == htp_connp_RES_BODY_CHUNKED_DATA_END) return "RES_BODY_CHUNKED_DATA_END"; + if (connp->out_state == htp_connp_RES_FINALIZE) return "RES_BODY_FINALIZE"; + + return "UNKNOWN"; +} + +/** + * + */ +char *htp_tx_request_progress_as_string(htp_tx_t *tx) { + if (tx == NULL) return "NULL"; + + switch (tx->request_progress) { + case HTP_REQUEST_NOT_STARTED: + return "NOT_STARTED"; + case HTP_REQUEST_LINE: + return "REQ_LINE"; + case HTP_REQUEST_HEADERS: + return "REQ_HEADERS"; + case HTP_REQUEST_BODY: + return "REQ_BODY"; + case HTP_REQUEST_TRAILER: + return "REQ_TRAILER"; + case HTP_REQUEST_COMPLETE: + return "COMPLETE"; + } + + return "INVALID"; +} + +/** + * + */ +char *htp_tx_response_progress_as_string(htp_tx_t *tx) { + if (tx == NULL) return "NULL"; + + switch (tx->response_progress) { + case HTP_RESPONSE_NOT_STARTED: + return "NOT_STARTED"; + case HTP_RESPONSE_LINE: + return "RES_LINE"; + case HTP_RESPONSE_HEADERS: + return "RES_HEADERS"; + case HTP_RESPONSE_BODY: + return "RES_BODY"; + case HTP_RESPONSE_TRAILER: + return "RES_TRAILER"; + case HTP_RESPONSE_COMPLETE: + return "COMPLETE"; + } + + return "INVALID"; +} + +bstr *htp_unparse_uri_noencode(htp_uri_t *uri) { + if (uri == NULL) return NULL; + + // On the first pass determine the length of the final string + size_t len = 0; + + if (uri->scheme != NULL) { + len += bstr_len(uri->scheme); + len += 3; // "://" + } + + if ((uri->username != NULL) || (uri->password != NULL)) { + if (uri->username != NULL) { + len += bstr_len(uri->username); + } + + len += 1; // ":" + + if (uri->password != NULL) { + len += bstr_len(uri->password); + } + + len += 1; // "@" + } + + if (uri->hostname != NULL) { + len += bstr_len(uri->hostname); + } + + if (uri->port != NULL) { + len += 1; // ":" + len += bstr_len(uri->port); + } + + if (uri->path != NULL) { + len += bstr_len(uri->path); + } + + if (uri->query != NULL) { + len += 1; // "?" + len += bstr_len(uri->query); + } + + if (uri->fragment != NULL) { + len += 1; // "#" + len += bstr_len(uri->fragment); + } + + // On the second pass construct the string + bstr *r = bstr_alloc(len); + if (r == NULL) return NULL; + + if (uri->scheme != NULL) { + bstr_add_noex(r, uri->scheme); + bstr_add_c_noex(r, "://"); + } + + if ((uri->username != NULL) || (uri->password != NULL)) { + if (uri->username != NULL) { + bstr_add_noex(r, uri->username); + } + + bstr_add_c_noex(r, ":"); + + if (uri->password != NULL) { + bstr_add_noex(r, uri->password); + } + + bstr_add_c_noex(r, "@"); + } + + if (uri->hostname != NULL) { + bstr_add_noex(r, uri->hostname); + } + + if (uri->port != NULL) { + bstr_add_c_noex(r, ":"); + bstr_add_noex(r, uri->port); + } + + if (uri->path != NULL) { + bstr_add_noex(r, uri->path); + } + + if (uri->query != NULL) { + bstr_add_c_noex(r, "?"); + bstr_add_noex(r, uri->query); + } + + if (uri->fragment != NULL) { + bstr_add_c_noex(r, "#"); + bstr_add_noex(r, uri->fragment); + } + + return r; +} + +/** + * Determine if the information provided on the response line + * is good enough. Browsers are lax when it comes to response + * line parsing. In most cases they will only look for the + * words "http" at the beginning. + * + * @param[in] data pointer to bytearray + * @param[in] len length in bytes of data + * @return 1 for good enough or 0 for not good enough + */ +int htp_treat_response_line_as_body(const uint8_t *data, size_t len) { + // Browser behavior: + // Firefox 3.5.x: (?i)^\s*http + // IE: (?i)^\s*http\s*/ + // Safari: ^HTTP/\d+\.\d+\s+\d{3} + size_t pos = 0; + + if (data == NULL) return 1; + while ((pos < len) && (htp_is_space(data[pos]) || data[pos] == 0)) pos++; + + if (len < pos + 4) return 1; + + if ((data[pos] != 'H') && (data[pos] != 'h')) return 1; + if ((data[pos+1] != 'T') && (data[pos+1] != 't')) return 1; + if ((data[pos+2] != 'T') && (data[pos+2] != 't')) return 1; + if ((data[pos+3] != 'P') && (data[pos+3] != 'p')) return 1; + + return 0; +} + +/** + * Run the REQUEST_BODY_DATA hook. + * + * @param[in] connp + * @param[in] d + */ +htp_status_t htp_req_run_hook_body_data(htp_connp_t *connp, htp_tx_data_t *d) { + // Do not invoke callbacks with an empty data chunk + if ((d->data != NULL) && (d->len == 0)) return HTP_OK; + + // Do not invoke callbacks without a transaction. + if (connp->in_tx == NULL) return HTP_OK; + + // Run transaction hooks first + htp_status_t rc = htp_hook_run_all(connp->in_tx->hook_request_body_data, d); + if (rc != HTP_OK) return rc; + + // Run configuration hooks second + rc = htp_hook_run_all(connp->cfg->hook_request_body_data, d); + if (rc != HTP_OK) return rc; + + // On PUT requests, treat request body as file + if (connp->put_file != NULL) { + htp_file_data_t file_data; + + file_data.data = d->data; + file_data.len = d->len; + file_data.file = connp->put_file; + file_data.file->len += d->len; + + rc = htp_hook_run_all(connp->cfg->hook_request_file_data, &file_data); + if (rc != HTP_OK) return rc; + } + + return HTP_OK; +} + +/** + * Run the RESPONSE_BODY_DATA hook. + * + * @param[in] connp + * @param[in] d + */ +htp_status_t htp_res_run_hook_body_data(htp_connp_t *connp, htp_tx_data_t *d) { + // Do not invoke callbacks with an empty data chunk. + if ((d->data != NULL) && (d->len == 0)) return HTP_OK; + + // Run transaction hooks first + htp_status_t rc = htp_hook_run_all(connp->out_tx->hook_response_body_data, d); + if (rc != HTP_OK) return rc; + + // Run configuration hooks second + rc = htp_hook_run_all(connp->cfg->hook_response_body_data, d); + if (rc != HTP_OK) return rc; + + return HTP_OK; +} + +/** + * Parses the provided memory region, extracting the double-quoted string. + * + * @param[in] data + * @param[in] len + * @param[out] out + * @param[out] endoffset + * @return HTP_OK on success, HTP_DECLINED if the input is not well formed, and HTP_ERROR on fatal errors. + */ +htp_status_t htp_extract_quoted_string_as_bstr(unsigned char *data, size_t len, bstr **out, size_t *endoffset) { + if ((data == NULL) || (out == NULL)) return HTP_ERROR; + + if (len == 0) return HTP_DECLINED; + + size_t pos = 0; + + // Check that the first character is a double quote. + if (data[pos] != '"') return HTP_DECLINED; + + // Step over the double quote. + pos++; + if (pos == len) return HTP_DECLINED; + + // Calculate the length of the resulting string. + size_t escaped_chars = 0; + while (pos < len) { + if (data[pos] == '\\') { + if (pos + 1 < len) { + escaped_chars++; + pos += 2; + continue; + } + } else if (data[pos] == '"') { + break; + } + + pos++; + } + + // Have we reached the end of input without seeing the terminating double quote? + if (pos == len) return HTP_DECLINED; + + // Copy the data and unescape it as necessary. + size_t outlen = pos - 1 - escaped_chars; + *out = bstr_alloc(outlen); + if (*out == NULL) return HTP_ERROR; + unsigned char *outptr = bstr_ptr(*out); + size_t outpos = 0; + + pos = 1; + while ((pos < len) && (outpos < outlen)) { + // TODO We are not properly unescaping test here, we're only + // handling escaped double quotes. + if (data[pos] == '\\') { + if (pos + 1 < len) { + outptr[outpos++] = data[pos + 1]; + pos += 2; + continue; + } + } else if (data[pos] == '"') { + break; + } + + outptr[outpos++] = data[pos++]; + } + + bstr_adjust_len(*out, outlen); + + if (endoffset != NULL) { + *endoffset = pos; + } + + return HTP_OK; +} + +htp_status_t htp_parse_ct_header(bstr *header, bstr **ct) { + if ((header == NULL) || (ct == NULL)) return HTP_ERROR; + + unsigned char *data = bstr_ptr(header); + size_t len = bstr_len(header); + + // The assumption here is that the header value we receive + // here has been left-trimmed, which means the starting position + // is on the media type. On some platforms that may not be the + // case, and we may need to do the left-trim ourselves. + + // Find the end of the MIME type, using the same approach PHP 5.4.3 uses. + size_t pos = 0; + while ((pos < len) && (data[pos] != ';') && (data[pos] != ',') && (data[pos] != ' ')) pos++; + + *ct = bstr_dup_ex(header, 0, pos); + if (*ct == NULL) return HTP_ERROR; + + bstr_to_lowercase(*ct); + + return HTP_OK; +} + +/** + * Implements relaxed (not strictly RFC) hostname validation. + * + * @param[in] hostname + * @return 1 if the supplied hostname is valid; 0 if it is not. + */ +int htp_validate_hostname(bstr *hostname) { + unsigned char *data = bstr_ptr(hostname); + size_t len = bstr_len(hostname); + size_t startpos = 0; + size_t pos = 0; + + if ((len == 0) || (len > 255)) return 0; + + if (data[0] == '[') { + // only ipv6 possible + if (len < 2 || len - 2 >= INET6_ADDRSTRLEN) { + return 0; + } + char dst[sizeof(struct in6_addr)]; + char str[INET6_ADDRSTRLEN]; + memcpy(str, data+1, len-2); + str[len-2] = 0; + return inet_pton(AF_INET6, str, dst); + } + while (pos < len) { + // Validate label characters. + startpos = pos; + while ((pos < len) && (data[pos] != '.')) { + unsigned char c = data[pos]; + // According to the RFC, the underscore is not allowed in a label, but + // we allow it here because we think it's often seen in practice. + if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')) || + (c == '-') || (c == '_'))) + { + return 0; + } + + pos++; + } + + // Validate label length. + if ((pos - startpos == 0) || (pos - startpos > 63)) return 0; + + if (pos >= len) return 1; // No more data after label. + + // How many dots are there? + startpos = pos; + while ((pos < len) && (data[pos] == '.')) pos++; + + if (pos - startpos != 1) return 0; // Exactly one dot expected. + } + + return 1; +} + +void htp_uri_free(htp_uri_t *uri) { + if (uri == NULL) return; + + bstr_free(uri->scheme); + bstr_free(uri->username); + bstr_free(uri->password); + bstr_free(uri->hostname); + bstr_free(uri->port); + bstr_free(uri->path); + bstr_free(uri->query); + bstr_free(uri->fragment); + + free(uri); +} + +htp_uri_t *htp_uri_alloc(void) { + htp_uri_t *u = calloc(1, sizeof (htp_uri_t)); + if (u == NULL) return NULL; + + u->port_number = -1; + + return u; +} + +char *htp_get_version(void) { + return HTP_VERSION_STRING_FULL; +} + +/** + * Tells if a header value (haystack) contains a token (needle) + * This is done with a caseless comparison + * + * @param[in] hvp header value pointer + * @param[in] hvlen length of header value buffer + * @param[in] value token to look for (null-terminated string), should be a lowercase constant + * @return HTP_OK if the header has the token; HTP_ERROR if it has not. + */ +htp_status_t htp_header_has_token(const unsigned char *hvp, size_t hvlen, const unsigned char *value) { + int state = 0; + // offset to compare in value + size_t v_off = 0; + // The header value is a list of comma-separated tokens (with additional spaces) + for (size_t i = 0; i < hvlen; i++) { + switch (state) { + case 0: + if (v_off == 0 && htp_is_space(hvp[i])) { + // skip leading space + continue; + } + if (tolower(hvp[i]) == value[v_off]) { + v_off++; + if (value[v_off] == 0) { + // finish validation if end of token + state = 2; + } + continue; + } else { + // wait for a new token + v_off = 0; + state = 1; + } + // fallthrough + case 1: + if (hvp[i] == ',') { + // start of next token + state = 0; + } + break; + case 2: + if (hvp[i] == ',') { + return HTP_OK; + } + if (!htp_is_space(hvp[i])) { + // trailing junk in token, wait for a next one + v_off = 0; + state = 1; + } + } + } + if (state == 2) { + return HTP_OK; + } + return HTP_ERROR; +} diff --git a/htp/htp_version.h.in b/htp/htp_version.h.in new file mode 100644 index 0000000..89a503f --- /dev/null +++ b/htp/htp_version.h.in @@ -0,0 +1,53 @@ +/***************************************************************************
+ * Copyright (c) 2009-2010 Open Information Security Foundation
+ * Copyright (c) 2010-2013 Qualys, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * - Neither the name of the Qualys, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***************************************************************************/
+
+/**
+ * @file
+ * @author Ivan Ristic <ivanr@webkreator.com>
+ */
+
+#ifndef HTP_VERSION_H
+#define HTP_VERSION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HTP_VERSION_STRING "@PACKAGE_VERSION@"
+#define HTP_VERSION_STRING_FULL "LibHTP v" HTP_VERSION_STRING
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HTP_VERSION_H */
diff --git a/htp/lzma/7zTypes.h b/htp/lzma/7zTypes.h new file mode 100644 index 0000000..f5d7505 --- /dev/null +++ b/htp/lzma/7zTypes.h @@ -0,0 +1,375 @@ +/* 7zTypes.h -- Basic types
+2018-08-04 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_TYPES_H
+#define __7Z_TYPES_H
+
+#ifdef _WIN32
+/* #include <windows.h> */
+#endif
+
+#include <stddef.h>
+#include <zconf.h>
+
+#ifndef EXTERN_C_BEGIN
+#ifdef __cplusplus
+#define EXTERN_C_BEGIN extern "C" {
+#define EXTERN_C_END }
+#else
+#define EXTERN_C_BEGIN
+#define EXTERN_C_END
+#endif
+#endif
+
+EXTERN_C_BEGIN
+
+#define SZ_OK 0
+
+#define SZ_ERROR_DATA 1
+#define SZ_ERROR_MEM 2
+#define SZ_ERROR_CRC 3
+#define SZ_ERROR_UNSUPPORTED 4
+#define SZ_ERROR_PARAM 5
+#define SZ_ERROR_INPUT_EOF 6
+#define SZ_ERROR_OUTPUT_EOF 7
+#define SZ_ERROR_READ 8
+#define SZ_ERROR_WRITE 9
+#define SZ_ERROR_PROGRESS 10
+#define SZ_ERROR_FAIL 11
+#define SZ_ERROR_THREAD 12
+
+#define SZ_ERROR_ARCHIVE 16
+#define SZ_ERROR_NO_ARCHIVE 17
+
+typedef int SRes;
+
+
+#ifdef _WIN32
+
+/* typedef DWORD WRes; */
+typedef unsigned WRes;
+#define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x)
+
+#else
+
+typedef int WRes;
+#define MY__FACILITY_WIN32 7
+#define MY__FACILITY__WRes MY__FACILITY_WIN32
+#define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000)))
+
+#endif
+
+
+#ifndef RINOK
+#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
+#endif
+
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+ NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+#ifdef _LZMA_NO_SYSTEM_SIZE_T
+typedef UInt32 SizeT;
+#else
+typedef size_t SizeT;
+#endif
+
+typedef int BoolInt;
+/* typedef BoolInt Bool; */
+#define True 1
+#define False 0
+
+
+#ifdef _WIN32
+#define MY_STD_CALL __stdcall
+#else
+#define MY_STD_CALL
+#endif
+
+#ifdef _MSC_VER
+
+#if _MSC_VER >= 1300
+#define MY_NO_INLINE __declspec(noinline)
+#else
+#define MY_NO_INLINE
+#endif
+
+#define MY_FORCE_INLINE __forceinline
+
+#define MY_CDECL __cdecl
+#define MY_FAST_CALL __fastcall
+
+#else
+
+#define MY_NO_INLINE
+#define MY_FORCE_INLINE
+#define MY_CDECL
+#define MY_FAST_CALL
+
+/* inline keyword : for C++ / C99 */
+
+/* GCC, clang: */
+/*
+#if defined (__GNUC__) && (__GNUC__ >= 4)
+#define MY_FORCE_INLINE __attribute__((always_inline))
+#define MY_NO_INLINE __attribute__((noinline))
+#endif
+*/
+
+#endif
+
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct IByteIn IByteIn;
+struct IByteIn
+{
+ Byte (*Read)(const IByteIn *p); /* reads one byte, returns 0 in case of EOF or error */
+};
+#define IByteIn_Read(p) (p)->Read(p)
+
+
+typedef struct IByteOut IByteOut;
+struct IByteOut
+{
+ void (*Write)(const IByteOut *p, Byte b);
+};
+#define IByteOut_Write(p, b) (p)->Write(p, b)
+
+
+typedef struct ISeqInStream ISeqInStream;
+struct ISeqInStream
+{
+ SRes (*Read)(const ISeqInStream *p, void *buf, size_t *size);
+ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+ (output(*size) < input(*size)) is allowed */
+};
+#define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+
+/* it can return SZ_ERROR_INPUT_EOF */
+SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size);
+SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType);
+SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf);
+
+
+typedef struct ISeqOutStream ISeqOutStream;
+struct ISeqOutStream
+{
+ size_t (*Write)(const ISeqOutStream *p, const void *buf, size_t size);
+ /* Returns: result - the number of actually written bytes.
+ (result < size) means error */
+};
+#define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size)
+
+typedef enum
+{
+ SZ_SEEK_SET = 0,
+ SZ_SEEK_CUR = 1,
+ SZ_SEEK_END = 2
+} ESzSeek;
+
+
+typedef struct ISeekInStream ISeekInStream;
+struct ISeekInStream
+{
+ SRes (*Read)(const ISeekInStream *p, void *buf, size_t *size); /* same as ISeqInStream::Read */
+ SRes (*Seek)(const ISeekInStream *p, Int64 *pos, ESzSeek origin);
+};
+#define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+#define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
+
+
+typedef struct ILookInStream ILookInStream;
+struct ILookInStream
+{
+ SRes (*Look)(const ILookInStream *p, const void **buf, size_t *size);
+ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+ (output(*size) > input(*size)) is not allowed
+ (output(*size) < input(*size)) is allowed */
+ SRes (*Skip)(const ILookInStream *p, size_t offset);
+ /* offset must be <= output(*size) of Look */
+
+ SRes (*Read)(const ILookInStream *p, void *buf, size_t *size);
+ /* reads directly (without buffer). It's same as ISeqInStream::Read */
+ SRes (*Seek)(const ILookInStream *p, Int64 *pos, ESzSeek origin);
+};
+
+#define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size)
+#define ILookInStream_Skip(p, offset) (p)->Skip(p, offset)
+#define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size)
+#define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin)
+
+
+SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size);
+SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset);
+
+/* reads via ILookInStream::Read */
+SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType);
+SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size);
+
+
+
+typedef struct
+{
+ ILookInStream vt;
+ const ISeekInStream *realStream;
+
+ size_t pos;
+ size_t size; /* it's data size */
+
+ /* the following variables must be set outside */
+ Byte *buf;
+ size_t bufSize;
+} CLookToRead2;
+
+void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead);
+
+#define LookToRead2_Init(p) { (p)->pos = (p)->size = 0; }
+
+
+typedef struct
+{
+ ISeqInStream vt;
+ const ILookInStream *realStream;
+} CSecToLook;
+
+void SecToLook_CreateVTable(CSecToLook *p);
+
+
+
+typedef struct
+{
+ ISeqInStream vt;
+ const ILookInStream *realStream;
+} CSecToRead;
+
+void SecToRead_CreateVTable(CSecToRead *p);
+
+
+typedef struct ICompressProgress ICompressProgress;
+
+struct ICompressProgress
+{
+ SRes (*Progress)(const ICompressProgress *p, UInt64 inSize, UInt64 outSize);
+ /* Returns: result. (result != SZ_OK) means break.
+ Value (UInt64)(Int64)-1 for size means unknown value. */
+};
+#define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize)
+
+
+
+typedef struct ISzAlloc ISzAlloc;
+typedef const ISzAlloc * ISzAllocPtr;
+
+struct ISzAlloc
+{
+ void *(*Alloc)(ISzAllocPtr p, size_t size);
+ void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */
+};
+
+#define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size)
+#define ISzAlloc_Free(p, a) (p)->Free(p, a)
+
+/* deprecated */
+#define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size)
+#define IAlloc_Free(p, a) ISzAlloc_Free(p, a)
+
+
+
+
+
+#ifndef MY_offsetof
+ #ifdef offsetof
+ #define MY_offsetof(type, m) offsetof(type, m)
+ /*
+ #define MY_offsetof(type, m) FIELD_OFFSET(type, m)
+ */
+ #else
+ #define MY_offsetof(type, m) ((size_t)&(((type *)0)->m))
+ #endif
+#endif
+
+
+
+#ifndef MY_container_of
+
+/*
+#define MY_container_of(ptr, type, m) container_of(ptr, type, m)
+#define MY_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m)
+#define MY_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m)))
+#define MY_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m))))
+*/
+
+/*
+ GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly"
+ GCC 3.4.4 : classes with constructor
+ GCC 4.8.1 : classes with non-public variable members"
+*/
+
+#define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m)))
+
+
+#endif
+
+#define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr))
+
+/*
+#define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
+*/
+#define CONTAINER_FROM_VTBL(ptr, type, m) MY_container_of(ptr, type, m)
+
+#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m)
+/*
+#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL(ptr, type, m)
+*/
+
+
+
+#ifdef _WIN32
+
+#define CHAR_PATH_SEPARATOR '\\'
+#define WCHAR_PATH_SEPARATOR L'\\'
+#define STRING_PATH_SEPARATOR "\\"
+#define WSTRING_PATH_SEPARATOR L"\\"
+
+#else
+
+#define CHAR_PATH_SEPARATOR '/'
+#define WCHAR_PATH_SEPARATOR L'/'
+#define STRING_PATH_SEPARATOR "/"
+#define WSTRING_PATH_SEPARATOR L"/"
+
+#endif
+
+EXTERN_C_END
+
+#endif
diff --git a/htp/lzma/Compiler.h b/htp/lzma/Compiler.h new file mode 100644 index 0000000..c788648 --- /dev/null +++ b/htp/lzma/Compiler.h @@ -0,0 +1,33 @@ +/* Compiler.h
+2017-04-03 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_COMPILER_H
+#define __7Z_COMPILER_H
+
+#ifdef _MSC_VER
+
+ #ifdef UNDER_CE
+ #define RPC_NO_WINDOWS_H
+ /* #pragma warning(disable : 4115) // '_RPC_ASYNC_STATE' : named type definition in parentheses */
+ #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union
+ #pragma warning(disable : 4214) // nonstandard extension used : bit field types other than int
+ #endif
+
+ #if _MSC_VER >= 1300
+ #pragma warning(disable : 4996) // This function or variable may be unsafe
+ #else
+ #pragma warning(disable : 4511) // copy constructor could not be generated
+ #pragma warning(disable : 4512) // assignment operator could not be generated
+ #pragma warning(disable : 4514) // unreferenced inline function has been removed
+ #pragma warning(disable : 4702) // unreachable code
+ #pragma warning(disable : 4710) // not inlined
+ #pragma warning(disable : 4714) // function marked as __forceinline not inlined
+ #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information
+ #endif
+
+#endif
+
+#define UNUSED_VAR(x) (void)x;
+/* #define UNUSED_VAR(x) x=x; */
+
+#endif
diff --git a/htp/lzma/LzFind.c b/htp/lzma/LzFind.c new file mode 100644 index 0000000..ee5375c --- /dev/null +++ b/htp/lzma/LzFind.c @@ -0,0 +1,1127 @@ +/* LzFind.c -- Match finder for LZ algorithms
+2018-07-08 : Igor Pavlov : Public domain */
+
+#include "Precomp.h"
+
+#include <string.h>
+
+#include "LzFind.h"
+#include "LzHash.h"
+
+#define kEmptyHashValue 0
+#define kMaxValForNormalize ((UInt32)0xFFFFFFFF)
+#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
+#define kNormalizeMask (~(UInt32)(kNormalizeStepMin - 1))
+#define kMaxHistorySize ((UInt32)7 << 29)
+
+#define kStartMaxLen 3
+
+static void LzInWindow_Free(CMatchFinder *p, ISzAllocPtr alloc)
+{
+ if (!p->directInput)
+ {
+ ISzAlloc_Free(alloc, p->bufferBase);
+ p->bufferBase = NULL;
+ }
+}
+
+/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */
+
+static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAllocPtr alloc)
+{
+ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
+ if (p->directInput)
+ {
+ p->blockSize = blockSize;
+ return 1;
+ }
+ if (!p->bufferBase || p->blockSize != blockSize)
+ {
+ LzInWindow_Free(p, alloc);
+ p->blockSize = blockSize;
+ p->bufferBase = (Byte *)ISzAlloc_Alloc(alloc, (size_t)blockSize);
+ }
+ return (p->bufferBase != NULL);
+}
+
+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
+
+static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
+
+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
+{
+ p->posLimit -= subValue;
+ p->pos -= subValue;
+ p->streamPos -= subValue;
+}
+
+static void MatchFinder_ReadBlock(CMatchFinder *p)
+{
+ if (p->streamEndWasReached || p->result != SZ_OK)
+ return;
+
+ /* We use (p->streamPos - p->pos) value. (p->streamPos < p->pos) is allowed. */
+
+ if (p->directInput)
+ {
+ UInt32 curSize = 0xFFFFFFFF - (p->streamPos - p->pos);
+ if (curSize > p->directInputRem)
+ curSize = (UInt32)p->directInputRem;
+ p->directInputRem -= curSize;
+ p->streamPos += curSize;
+ if (p->directInputRem == 0)
+ p->streamEndWasReached = 1;
+ return;
+ }
+
+ for (;;)
+ {
+ Byte *dest = p->buffer + (p->streamPos - p->pos);
+ size_t size = (p->bufferBase + p->blockSize - dest);
+ if (size == 0)
+ return;
+
+ p->result = ISeqInStream_Read(p->stream, dest, &size);
+ if (p->result != SZ_OK)
+ return;
+ if (size == 0)
+ {
+ p->streamEndWasReached = 1;
+ return;
+ }
+ p->streamPos += (UInt32)size;
+ if (p->streamPos - p->pos > p->keepSizeAfter)
+ return;
+ }
+}
+
+void MatchFinder_MoveBlock(CMatchFinder *p)
+{
+ memmove(p->bufferBase,
+ p->buffer - p->keepSizeBefore,
+ (size_t)(p->streamPos - p->pos) + p->keepSizeBefore);
+ p->buffer = p->bufferBase + p->keepSizeBefore;
+}
+
+int MatchFinder_NeedMove(CMatchFinder *p)
+{
+ if (p->directInput)
+ return 0;
+ /* if (p->streamEndWasReached) return 0; */
+ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
+}
+
+void MatchFinder_ReadIfRequired(CMatchFinder *p)
+{
+ if (p->streamEndWasReached)
+ return;
+ if (p->keepSizeAfter >= p->streamPos - p->pos)
+ MatchFinder_ReadBlock(p);
+}
+
+static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
+{
+ if (MatchFinder_NeedMove(p))
+ MatchFinder_MoveBlock(p);
+ MatchFinder_ReadBlock(p);
+}
+
+static void MatchFinder_SetDefaultSettings(CMatchFinder *p)
+{
+ p->cutValue = 32;
+ p->btMode = 1;
+ p->numHashBytes = 4;
+ p->bigHash = 0;
+}
+
+#define kCrcPoly 0xEDB88320
+
+void MatchFinder_Construct(CMatchFinder *p)
+{
+ unsigned i;
+ p->bufferBase = NULL;
+ p->directInput = 0;
+ p->hash = NULL;
+ p->expectedDataSize = (UInt64)(Int64)-1;
+ MatchFinder_SetDefaultSettings(p);
+
+ for (i = 0; i < 256; i++)
+ {
+ UInt32 r = (UInt32)i;
+ unsigned j;
+ for (j = 0; j < 8; j++)
+ r = (r >> 1) ^ (kCrcPoly & ((UInt32)0 - (r & 1)));
+ p->crc[i] = r;
+ }
+}
+
+static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAllocPtr alloc)
+{
+ ISzAlloc_Free(alloc, p->hash);
+ p->hash = NULL;
+}
+
+void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc)
+{
+ MatchFinder_FreeThisClassMemory(p, alloc);
+ LzInWindow_Free(p, alloc);
+}
+
+static CLzRef* AllocRefs(size_t num, ISzAllocPtr alloc)
+{
+ size_t sizeInBytes = (size_t)num * sizeof(CLzRef);
+ if (sizeInBytes / sizeof(CLzRef) != num)
+ return NULL;
+ return (CLzRef *)ISzAlloc_Alloc(alloc, sizeInBytes);
+}
+
+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+ ISzAllocPtr alloc)
+{
+ UInt32 sizeReserv;
+
+ if (historySize > kMaxHistorySize)
+ {
+ MatchFinder_Free(p, alloc);
+ return 0;
+ }
+
+ sizeReserv = historySize >> 1;
+ if (historySize >= ((UInt32)3 << 30)) sizeReserv = historySize >> 3;
+ else if (historySize >= ((UInt32)2 << 30)) sizeReserv = historySize >> 2;
+
+ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19);
+
+ p->keepSizeBefore = historySize + keepAddBufferBefore + 1;
+ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter;
+
+ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */
+
+ if (LzInWindow_Create(p, sizeReserv, alloc))
+ {
+ UInt32 newCyclicBufferSize = historySize + 1;
+ UInt32 hs;
+ p->matchMaxLen = matchMaxLen;
+ {
+ p->fixedHashSize = 0;
+ if (p->numHashBytes == 2)
+ hs = (1 << 16) - 1;
+ else
+ {
+ hs = historySize;
+ if (hs > p->expectedDataSize)
+ hs = (UInt32)p->expectedDataSize;
+ if (hs != 0)
+ hs--;
+ hs |= (hs >> 1);
+ hs |= (hs >> 2);
+ hs |= (hs >> 4);
+ hs |= (hs >> 8);
+ hs >>= 1;
+ hs |= 0xFFFF; /* don't change it! It's required for Deflate */
+ if (hs > (1 << 24))
+ {
+ if (p->numHashBytes == 3)
+ hs = (1 << 24) - 1;
+ else
+ hs >>= 1;
+ /* if (bigHash) mode, GetHeads4b() in LzFindMt.c needs (hs >= ((1 << 24) - 1))) */
+ }
+ }
+ p->hashMask = hs;
+ hs++;
+ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size;
+ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size;
+ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size;
+ hs += p->fixedHashSize;
+ }
+
+ {
+ size_t newSize;
+ size_t numSons;
+ p->historySize = historySize;
+ p->hashSizeSum = hs;
+ p->cyclicBufferSize = newCyclicBufferSize;
+
+ numSons = newCyclicBufferSize;
+ if (p->btMode)
+ numSons <<= 1;
+ newSize = hs + numSons;
+
+ if (p->hash && p->numRefs == newSize)
+ return 1;
+
+ MatchFinder_FreeThisClassMemory(p, alloc);
+ p->numRefs = newSize;
+ p->hash = AllocRefs(newSize, alloc);
+
+ if (p->hash)
+ {
+ p->son = p->hash + p->hashSizeSum;
+ return 1;
+ }
+ }
+ }
+
+ MatchFinder_Free(p, alloc);
+ return 0;
+}
+
+static void MatchFinder_SetLimits(CMatchFinder *p)
+{
+ UInt32 limit = kMaxValForNormalize - p->pos;
+ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos;
+
+ if (limit2 < limit)
+ limit = limit2;
+ limit2 = p->streamPos - p->pos;
+
+ if (limit2 <= p->keepSizeAfter)
+ {
+ if (limit2 > 0)
+ limit2 = 1;
+ }
+ else
+ limit2 -= p->keepSizeAfter;
+
+ if (limit2 < limit)
+ limit = limit2;
+
+ {
+ UInt32 lenLimit = p->streamPos - p->pos;
+ if (lenLimit > p->matchMaxLen)
+ lenLimit = p->matchMaxLen;
+ p->lenLimit = lenLimit;
+ }
+ p->posLimit = p->pos + limit;
+}
+
+
+void MatchFinder_Init_LowHash(CMatchFinder *p)
+{
+ size_t i;
+ CLzRef *items = p->hash;
+ size_t numItems = p->fixedHashSize;
+ for (i = 0; i < numItems; i++)
+ items[i] = kEmptyHashValue;
+}
+
+
+void MatchFinder_Init_HighHash(CMatchFinder *p)
+{
+ size_t i;
+ CLzRef *items = p->hash + p->fixedHashSize;
+ size_t numItems = (size_t)p->hashMask + 1;
+ for (i = 0; i < numItems; i++)
+ items[i] = kEmptyHashValue;
+}
+
+
+void MatchFinder_Init_3(CMatchFinder *p, int readData)
+{
+ p->cyclicBufferPos = 0;
+ p->buffer = p->bufferBase;
+ p->pos =
+ p->streamPos = p->cyclicBufferSize;
+ p->result = SZ_OK;
+ p->streamEndWasReached = 0;
+
+ if (readData)
+ MatchFinder_ReadBlock(p);
+
+ MatchFinder_SetLimits(p);
+}
+
+
+void MatchFinder_Init(CMatchFinder *p)
+{
+ MatchFinder_Init_HighHash(p);
+ MatchFinder_Init_LowHash(p);
+ MatchFinder_Init_3(p, True);
+}
+
+
+static UInt32 MatchFinder_GetSubValue(CMatchFinder *p)
+{
+ return (p->pos - p->historySize - 1) & kNormalizeMask;
+}
+
+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems)
+{
+ size_t i;
+ for (i = 0; i < numItems; i++)
+ {
+ UInt32 value = items[i];
+ if (value <= subValue)
+ value = kEmptyHashValue;
+ else
+ value -= subValue;
+ items[i] = value;
+ }
+}
+
+static void MatchFinder_Normalize(CMatchFinder *p)
+{
+ UInt32 subValue = MatchFinder_GetSubValue(p);
+ MatchFinder_Normalize3(subValue, p->hash, p->numRefs);
+ MatchFinder_ReduceOffsets(p, subValue);
+}
+
+
+MY_NO_INLINE
+static void MatchFinder_CheckLimits(CMatchFinder *p)
+{
+ if (p->pos == kMaxValForNormalize)
+ MatchFinder_Normalize(p);
+ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos)
+ MatchFinder_CheckAndMoveAndRead(p);
+ if (p->cyclicBufferPos == p->cyclicBufferSize)
+ p->cyclicBufferPos = 0;
+ MatchFinder_SetLimits(p);
+}
+
+
+/*
+ (lenLimit > maxLen)
+*/
+MY_FORCE_INLINE
+static UInt32 * Hc_GetMatchesSpec(unsigned lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+ UInt32 *distances, unsigned maxLen)
+{
+ /*
+ son[_cyclicBufferPos] = curMatch;
+ for (;;)
+ {
+ UInt32 delta = pos - curMatch;
+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+ return distances;
+ {
+ const Byte *pb = cur - delta;
+ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+ if (pb[maxLen] == cur[maxLen] && *pb == *cur)
+ {
+ UInt32 len = 0;
+ while (++len != lenLimit)
+ if (pb[len] != cur[len])
+ break;
+ if (maxLen < len)
+ {
+ maxLen = len;
+ *distances++ = len;
+ *distances++ = delta - 1;
+ if (len == lenLimit)
+ return distances;
+ }
+ }
+ }
+ }
+ */
+
+ const Byte *lim = cur + lenLimit;
+ son[_cyclicBufferPos] = curMatch;
+ do
+ {
+ UInt32 delta = pos - curMatch;
+ if (delta >= _cyclicBufferSize)
+ break;
+ {
+ ptrdiff_t diff;
+ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+ diff = (ptrdiff_t)0 - delta;
+ if (cur[maxLen] == cur[maxLen + diff])
+ {
+ const Byte *c = cur;
+ while (*c == c[diff])
+ {
+ if (++c == lim)
+ {
+ distances[0] = (UInt32)(lim - cur);
+ distances[1] = delta - 1;
+ return distances + 2;
+ }
+ }
+ {
+ unsigned len = (unsigned)(c - cur);
+ if (maxLen < len)
+ {
+ maxLen = len;
+ distances[0] = (UInt32)len;
+ distances[1] = delta - 1;
+ distances += 2;
+ }
+ }
+ }
+ }
+ }
+ while (--cutValue);
+
+ return distances;
+}
+
+
+MY_FORCE_INLINE
+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+ UInt32 *distances, UInt32 maxLen)
+{
+ CLzRef *ptr0 = son + ((size_t)_cyclicBufferPos << 1) + 1;
+ CLzRef *ptr1 = son + ((size_t)_cyclicBufferPos << 1);
+ unsigned len0 = 0, len1 = 0;
+ for (;;)
+ {
+ UInt32 delta = pos - curMatch;
+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+ {
+ *ptr0 = *ptr1 = kEmptyHashValue;
+ return distances;
+ }
+ {
+ CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+ const Byte *pb = cur - delta;
+ unsigned len = (len0 < len1 ? len0 : len1);
+ UInt32 pair0 = pair[0];
+ if (pb[len] == cur[len])
+ {
+ if (++len != lenLimit && pb[len] == cur[len])
+ while (++len != lenLimit)
+ if (pb[len] != cur[len])
+ break;
+ if (maxLen < len)
+ {
+ maxLen = (UInt32)len;
+ *distances++ = (UInt32)len;
+ *distances++ = delta - 1;
+ if (len == lenLimit)
+ {
+ *ptr1 = pair0;
+ *ptr0 = pair[1];
+ return distances;
+ }
+ }
+ }
+ if (pb[len] < cur[len])
+ {
+ *ptr1 = curMatch;
+ ptr1 = pair + 1;
+ curMatch = *ptr1;
+ len1 = len;
+ }
+ else
+ {
+ *ptr0 = curMatch;
+ ptr0 = pair;
+ curMatch = *ptr0;
+ len0 = len;
+ }
+ }
+ }
+}
+
+static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue)
+{
+ CLzRef *ptr0 = son + ((size_t)_cyclicBufferPos << 1) + 1;
+ CLzRef *ptr1 = son + ((size_t)_cyclicBufferPos << 1);
+ unsigned len0 = 0, len1 = 0;
+ for (;;)
+ {
+ UInt32 delta = pos - curMatch;
+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+ {
+ *ptr0 = *ptr1 = kEmptyHashValue;
+ return;
+ }
+ {
+ CLzRef *pair = son + ((size_t)(_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+ const Byte *pb = cur - delta;
+ unsigned len = (len0 < len1 ? len0 : len1);
+ if (pb[len] == cur[len])
+ {
+ while (++len != lenLimit)
+ if (pb[len] != cur[len])
+ break;
+ {
+ if (len == lenLimit)
+ {
+ *ptr1 = pair[0];
+ *ptr0 = pair[1];
+ return;
+ }
+ }
+ }
+ if (pb[len] < cur[len])
+ {
+ *ptr1 = curMatch;
+ ptr1 = pair + 1;
+ curMatch = *ptr1;
+ len1 = len;
+ }
+ else
+ {
+ *ptr0 = curMatch;
+ ptr0 = pair;
+ curMatch = *ptr0;
+ len0 = len;
+ }
+ }
+ }
+}
+
+#define MOVE_POS \
+ ++p->cyclicBufferPos; \
+ p->buffer++; \
+ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
+
+#define MOVE_POS_RET MOVE_POS return (UInt32)offset;
+
+static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
+
+#define GET_MATCHES_HEADER2(minLen, ret_op) \
+ unsigned lenLimit; UInt32 hv; const Byte *cur; UInt32 curMatch; \
+ lenLimit = (unsigned)p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
+ cur = p->buffer;
+
+#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0)
+#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue)
+
+#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue
+
+#define GET_MATCHES_FOOTER(offset, maxLen) \
+ offset = (unsigned)(GetMatchesSpec1((UInt32)lenLimit, curMatch, MF_PARAMS(p), \
+ distances + offset, (UInt32)maxLen) - distances); MOVE_POS_RET;
+
+#define SKIP_FOOTER \
+ SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
+
+#define UPDATE_maxLen { \
+ ptrdiff_t diff = (ptrdiff_t)0 - d2; \
+ const Byte *c = cur + maxLen; \
+ const Byte *lim = cur + lenLimit; \
+ for (; c != lim; c++) if (*(c + diff) != *c) break; \
+ maxLen = (unsigned)(c - cur); }
+
+static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ unsigned offset;
+ GET_MATCHES_HEADER(2)
+ HASH2_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ offset = 0;
+ GET_MATCHES_FOOTER(offset, 1)
+}
+
+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ unsigned offset;
+ GET_MATCHES_HEADER(3)
+ HASH_ZIP_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ offset = 0;
+ GET_MATCHES_FOOTER(offset, 2)
+}
+
+static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ UInt32 h2, d2, pos;
+ unsigned maxLen, offset;
+ UInt32 *hash;
+ GET_MATCHES_HEADER(3)
+
+ HASH3_CALC;
+
+ hash = p->hash;
+ pos = p->pos;
+
+ d2 = pos - hash[h2];
+
+ curMatch = (hash + kFix3HashSize)[hv];
+
+ hash[h2] = pos;
+ (hash + kFix3HashSize)[hv] = pos;
+
+ maxLen = 2;
+ offset = 0;
+
+ if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur)
+ {
+ UPDATE_maxLen
+ distances[0] = (UInt32)maxLen;
+ distances[1] = d2 - 1;
+ offset = 2;
+ if (maxLen == lenLimit)
+ {
+ SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p));
+ MOVE_POS_RET;
+ }
+ }
+
+ GET_MATCHES_FOOTER(offset, maxLen)
+}
+
+static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ UInt32 h2, h3, d2, d3, pos;
+ unsigned maxLen, offset;
+ UInt32 *hash;
+ GET_MATCHES_HEADER(4)
+
+ HASH4_CALC;
+
+ hash = p->hash;
+ pos = p->pos;
+
+ d2 = pos - hash [h2];
+ d3 = pos - (hash + kFix3HashSize)[h3];
+
+ curMatch = (hash + kFix4HashSize)[hv];
+
+ hash [h2] = pos;
+ (hash + kFix3HashSize)[h3] = pos;
+ (hash + kFix4HashSize)[hv] = pos;
+
+ maxLen = 0;
+ offset = 0;
+
+ if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur)
+ {
+ maxLen = 2;
+ distances[0] = 2;
+ distances[1] = d2 - 1;
+ offset = 2;
+ }
+
+ if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ maxLen = 3;
+ distances[(size_t)offset + 1] = d3 - 1;
+ offset += 2;
+ d2 = d3;
+ }
+
+ if (offset != 0)
+ {
+ UPDATE_maxLen
+ distances[(size_t)offset - 2] = (UInt32)maxLen;
+ if (maxLen == lenLimit)
+ {
+ SkipMatchesSpec((UInt32)lenLimit, curMatch, MF_PARAMS(p));
+ MOVE_POS_RET;
+ }
+ }
+
+ if (maxLen < 3)
+ maxLen = 3;
+
+ GET_MATCHES_FOOTER(offset, maxLen)
+}
+
+/*
+static UInt32 Bt5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos;
+ UInt32 *hash;
+ GET_MATCHES_HEADER(5)
+
+ HASH5_CALC;
+
+ hash = p->hash;
+ pos = p->pos;
+
+ d2 = pos - hash [h2];
+ d3 = pos - (hash + kFix3HashSize)[h3];
+ d4 = pos - (hash + kFix4HashSize)[h4];
+
+ curMatch = (hash + kFix5HashSize)[hv];
+
+ hash [h2] = pos;
+ (hash + kFix3HashSize)[h3] = pos;
+ (hash + kFix4HashSize)[h4] = pos;
+ (hash + kFix5HashSize)[hv] = pos;
+
+ maxLen = 0;
+ offset = 0;
+
+ if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur)
+ {
+ distances[0] = maxLen = 2;
+ distances[1] = d2 - 1;
+ offset = 2;
+ if (*(cur - d2 + 2) == cur[2])
+ distances[0] = maxLen = 3;
+ else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ distances[2] = maxLen = 3;
+ distances[3] = d3 - 1;
+ offset = 4;
+ d2 = d3;
+ }
+ }
+ else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ distances[0] = maxLen = 3;
+ distances[1] = d3 - 1;
+ offset = 2;
+ d2 = d3;
+ }
+
+ if (d2 != d4 && d4 < p->cyclicBufferSize
+ && *(cur - d4) == *cur
+ && *(cur - d4 + 3) == *(cur + 3))
+ {
+ maxLen = 4;
+ distances[(size_t)offset + 1] = d4 - 1;
+ offset += 2;
+ d2 = d4;
+ }
+
+ if (offset != 0)
+ {
+ UPDATE_maxLen
+ distances[(size_t)offset - 2] = maxLen;
+ if (maxLen == lenLimit)
+ {
+ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
+ MOVE_POS_RET;
+ }
+ }
+
+ if (maxLen < 4)
+ maxLen = 4;
+
+ GET_MATCHES_FOOTER(offset, maxLen)
+}
+*/
+
+static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ UInt32 h2, h3, d2, d3, pos;
+ unsigned maxLen, offset;
+ UInt32 *hash;
+ GET_MATCHES_HEADER(4)
+
+ HASH4_CALC;
+
+ hash = p->hash;
+ pos = p->pos;
+
+ d2 = pos - hash [h2];
+ d3 = pos - (hash + kFix3HashSize)[h3];
+ curMatch = (hash + kFix4HashSize)[hv];
+
+ hash [h2] = pos;
+ (hash + kFix3HashSize)[h3] = pos;
+ (hash + kFix4HashSize)[hv] = pos;
+
+ maxLen = 0;
+ offset = 0;
+
+ if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur)
+ {
+ maxLen = 2;
+ distances[0] = 2;
+ distances[1] = d2 - 1;
+ offset = 2;
+ }
+
+ if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ maxLen = 3;
+ distances[(size_t)offset + 1] = d3 - 1;
+ offset += 2;
+ d2 = d3;
+ }
+
+ if (offset != 0)
+ {
+ UPDATE_maxLen
+ distances[(size_t)offset - 2] = (UInt32)maxLen;
+ if (maxLen == lenLimit)
+ {
+ p->son[p->cyclicBufferPos] = curMatch;
+ MOVE_POS_RET;
+ }
+ }
+
+ if (maxLen < 3)
+ maxLen = 3;
+
+ offset = (unsigned)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+ distances + offset, maxLen) - (distances));
+ MOVE_POS_RET
+}
+
+/*
+static UInt32 Hc5_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ UInt32 h2, h3, h4, d2, d3, d4, maxLen, offset, pos
+ UInt32 *hash;
+ GET_MATCHES_HEADER(5)
+
+ HASH5_CALC;
+
+ hash = p->hash;
+ pos = p->pos;
+
+ d2 = pos - hash [h2];
+ d3 = pos - (hash + kFix3HashSize)[h3];
+ d4 = pos - (hash + kFix4HashSize)[h4];
+
+ curMatch = (hash + kFix5HashSize)[hv];
+
+ hash [h2] = pos;
+ (hash + kFix3HashSize)[h3] = pos;
+ (hash + kFix4HashSize)[h4] = pos;
+ (hash + kFix5HashSize)[hv] = pos;
+
+ maxLen = 0;
+ offset = 0;
+
+ if (d2 < p->cyclicBufferSize && *(cur - d2) == *cur)
+ {
+ distances[0] = maxLen = 2;
+ distances[1] = d2 - 1;
+ offset = 2;
+ if (*(cur - d2 + 2) == cur[2])
+ distances[0] = maxLen = 3;
+ else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ distances[2] = maxLen = 3;
+ distances[3] = d3 - 1;
+ offset = 4;
+ d2 = d3;
+ }
+ }
+ else if (d3 < p->cyclicBufferSize && *(cur - d3) == *cur)
+ {
+ distances[0] = maxLen = 3;
+ distances[1] = d3 - 1;
+ offset = 2;
+ d2 = d3;
+ }
+
+ if (d2 != d4 && d4 < p->cyclicBufferSize
+ && *(cur - d4) == *cur
+ && *(cur - d4 + 3) == *(cur + 3))
+ {
+ maxLen = 4;
+ distances[(size_t)offset + 1] = d4 - 1;
+ offset += 2;
+ d2 = d4;
+ }
+
+ if (offset != 0)
+ {
+ UPDATE_maxLen
+ distances[(size_t)offset - 2] = maxLen;
+ if (maxLen == lenLimit)
+ {
+ p->son[p->cyclicBufferPos] = curMatch;
+ MOVE_POS_RET;
+ }
+ }
+
+ if (maxLen < 4)
+ maxLen = 4;
+
+ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+ distances + offset, maxLen) - (distances));
+ MOVE_POS_RET
+}
+*/
+
+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+ unsigned offset;
+ GET_MATCHES_HEADER(3)
+ HASH_ZIP_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ offset = (unsigned)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+ distances, 2) - (distances));
+ MOVE_POS_RET
+}
+
+static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ SKIP_HEADER(2)
+ HASH2_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ SKIP_FOOTER
+ }
+ while (--num != 0);
+}
+
+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ SKIP_HEADER(3)
+ HASH_ZIP_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ SKIP_FOOTER
+ }
+ while (--num != 0);
+}
+
+static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ UInt32 h2;
+ UInt32 *hash;
+ SKIP_HEADER(3)
+ HASH3_CALC;
+ hash = p->hash;
+ curMatch = (hash + kFix3HashSize)[hv];
+ hash[h2] =
+ (hash + kFix3HashSize)[hv] = p->pos;
+ SKIP_FOOTER
+ }
+ while (--num != 0);
+}
+
+static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ UInt32 h2, h3;
+ UInt32 *hash;
+ SKIP_HEADER(4)
+ HASH4_CALC;
+ hash = p->hash;
+ curMatch = (hash + kFix4HashSize)[hv];
+ hash [h2] =
+ (hash + kFix3HashSize)[h3] =
+ (hash + kFix4HashSize)[hv] = p->pos;
+ SKIP_FOOTER
+ }
+ while (--num != 0);
+}
+
+/*
+static void Bt5_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ UInt32 h2, h3, h4;
+ UInt32 *hash;
+ SKIP_HEADER(5)
+ HASH5_CALC;
+ hash = p->hash;
+ curMatch = (hash + kFix5HashSize)[hv];
+ hash [h2] =
+ (hash + kFix3HashSize)[h3] =
+ (hash + kFix4HashSize)[h4] =
+ (hash + kFix5HashSize)[hv] = p->pos;
+ SKIP_FOOTER
+ }
+ while (--num != 0);
+}
+*/
+
+static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ UInt32 h2, h3;
+ UInt32 *hash;
+ SKIP_HEADER(4)
+ HASH4_CALC;
+ hash = p->hash;
+ curMatch = (hash + kFix4HashSize)[hv];
+ hash [h2] =
+ (hash + kFix3HashSize)[h3] =
+ (hash + kFix4HashSize)[hv] = p->pos;
+ p->son[p->cyclicBufferPos] = curMatch;
+ MOVE_POS
+ }
+ while (--num != 0);
+}
+
+/*
+static void Hc5_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ UInt32 h2, h3, h4;
+ UInt32 *hash;
+ SKIP_HEADER(5)
+ HASH5_CALC;
+ hash = p->hash;
+ curMatch = hash + kFix5HashSize)[hv];
+ hash [h2] =
+ (hash + kFix3HashSize)[h3] =
+ (hash + kFix4HashSize)[h4] =
+ (hash + kFix5HashSize)[hv] = p->pos;
+ p->son[p->cyclicBufferPos] = curMatch;
+ MOVE_POS
+ }
+ while (--num != 0);
+}
+*/
+
+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+ do
+ {
+ SKIP_HEADER(3)
+ HASH_ZIP_CALC;
+ curMatch = p->hash[hv];
+ p->hash[hv] = p->pos;
+ p->son[p->cyclicBufferPos] = curMatch;
+ MOVE_POS
+ }
+ while (--num != 0);
+}
+
+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
+{
+ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
+ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
+ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
+ if (!p->btMode)
+ {
+ /* if (p->numHashBytes <= 4) */
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
+ }
+ /*
+ else
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Hc5_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Hc5_MatchFinder_Skip;
+ }
+ */
+ }
+ else if (p->numHashBytes == 2)
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
+ }
+ else if (p->numHashBytes == 3)
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
+ }
+ else /* if (p->numHashBytes == 4) */
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+ }
+ /*
+ else
+ {
+ vTable->GetMatches = (Mf_GetMatches_Func)Bt5_MatchFinder_GetMatches;
+ vTable->Skip = (Mf_Skip_Func)Bt5_MatchFinder_Skip;
+ }
+ */
+}
diff --git a/htp/lzma/LzFind.h b/htp/lzma/LzFind.h new file mode 100644 index 0000000..c77adde --- /dev/null +++ b/htp/lzma/LzFind.h @@ -0,0 +1,121 @@ +/* LzFind.h -- Match finder for LZ algorithms
+2017-06-10 : Igor Pavlov : Public domain */
+
+#ifndef __LZ_FIND_H
+#define __LZ_FIND_H
+
+#include "7zTypes.h"
+
+EXTERN_C_BEGIN
+
+typedef UInt32 CLzRef;
+
+typedef struct _CMatchFinder
+{
+ Byte *buffer;
+ UInt32 pos;
+ UInt32 posLimit;
+ UInt32 streamPos;
+ UInt32 lenLimit;
+
+ UInt32 cyclicBufferPos;
+ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
+
+ Byte streamEndWasReached;
+ Byte btMode;
+ Byte bigHash;
+ Byte directInput;
+
+ UInt32 matchMaxLen;
+ CLzRef *hash;
+ CLzRef *son;
+ UInt32 hashMask;
+ UInt32 cutValue;
+
+ Byte *bufferBase;
+ ISeqInStream *stream;
+
+ UInt32 blockSize;
+ UInt32 keepSizeBefore;
+ UInt32 keepSizeAfter;
+
+ UInt32 numHashBytes;
+ size_t directInputRem;
+ UInt32 historySize;
+ UInt32 fixedHashSize;
+ UInt32 hashSizeSum;
+ SRes result;
+ UInt32 crc[256];
+ size_t numRefs;
+
+ UInt64 expectedDataSize;
+} CMatchFinder;
+
+#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
+
+#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
+
+#define Inline_MatchFinder_IsFinishedOK(p) \
+ ((p)->streamEndWasReached \
+ && (p)->streamPos == (p)->pos \
+ && (!(p)->directInput || (p)->directInputRem == 0))
+
+int MatchFinder_NeedMove(CMatchFinder *p);
+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
+void MatchFinder_MoveBlock(CMatchFinder *p);
+void MatchFinder_ReadIfRequired(CMatchFinder *p);
+
+void MatchFinder_Construct(CMatchFinder *p);
+
+/* Conditions:
+ historySize <= 3 GB
+ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
+*/
+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+ ISzAllocPtr alloc);
+void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc);
+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems);
+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
+
+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
+ UInt32 *distances, UInt32 maxLen);
+
+/*
+Conditions:
+ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func.
+ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function
+*/
+
+typedef void (*Mf_Init_Func)(void *object);
+typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
+typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
+typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
+typedef void (*Mf_Skip_Func)(void *object, UInt32);
+
+typedef struct _IMatchFinder
+{
+ Mf_Init_Func Init;
+ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes;
+ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos;
+ Mf_GetMatches_Func GetMatches;
+ Mf_Skip_Func Skip;
+} IMatchFinder;
+
+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
+
+void MatchFinder_Init_LowHash(CMatchFinder *p);
+void MatchFinder_Init_HighHash(CMatchFinder *p);
+void MatchFinder_Init_3(CMatchFinder *p, int readData);
+void MatchFinder_Init(CMatchFinder *p);
+
+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+
+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+
+EXTERN_C_END
+
+#endif
diff --git a/htp/lzma/LzHash.h b/htp/lzma/LzHash.h new file mode 100644 index 0000000..2191444 --- /dev/null +++ b/htp/lzma/LzHash.h @@ -0,0 +1,57 @@ +/* LzHash.h -- HASH functions for LZ algorithms
+2015-04-12 : Igor Pavlov : Public domain */
+
+#ifndef __LZ_HASH_H
+#define __LZ_HASH_H
+
+#define kHash2Size (1 << 10)
+#define kHash3Size (1 << 16)
+#define kHash4Size (1 << 20)
+
+#define kFix3HashSize (kHash2Size)
+#define kFix4HashSize (kHash2Size + kHash3Size)
+#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
+
+#define HASH2_CALC hv = cur[0] | ((UInt32)cur[1] << 8);
+
+#define HASH3_CALC { \
+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+ h2 = temp & (kHash2Size - 1); \
+ hv = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
+
+#define HASH4_CALC { \
+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+ h2 = temp & (kHash2Size - 1); \
+ temp ^= ((UInt32)cur[2] << 8); \
+ h3 = temp & (kHash3Size - 1); \
+ hv = (temp ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
+
+#define HASH5_CALC { \
+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+ h2 = temp & (kHash2Size - 1); \
+ temp ^= ((UInt32)cur[2] << 8); \
+ h3 = temp & (kHash3Size - 1); \
+ temp ^= (p->crc[cur[3]] << 5); \
+ h4 = temp & (kHash4Size - 1); \
+ hv = (temp ^ (p->crc[cur[4]] << 3)) & p->hashMask; }
+
+/* #define HASH_ZIP_CALC hv = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
+#define HASH_ZIP_CALC hv = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
+
+
+#define MT_HASH2_CALC \
+ h2 = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
+
+#define MT_HASH3_CALC { \
+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+ h2 = temp & (kHash2Size - 1); \
+ h3 = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
+
+#define MT_HASH4_CALC { \
+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+ h2 = temp & (kHash2Size - 1); \
+ temp ^= ((UInt32)cur[2] << 8); \
+ h3 = temp & (kHash3Size - 1); \
+ h4 = (temp ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
+
+#endif
diff --git a/htp/lzma/LzmaDec.c b/htp/lzma/LzmaDec.c new file mode 100644 index 0000000..138c36c --- /dev/null +++ b/htp/lzma/LzmaDec.c @@ -0,0 +1,1223 @@ +/* LzmaDec.c -- LZMA Decoder
+2018-07-04 : Igor Pavlov : Public domain */
+
+#include "Precomp.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/* #include "CpuArch.h" */
+#include "LzmaDec.h"
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_INIT_SIZE 5
+
+#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
+
+#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound)
+#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
+#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
+#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \
+ { UPDATE_0(p); i = (i + i); A0; } else \
+ { UPDATE_1(p); i = (i + i) + 1; A1; }
+
+#define TREE_GET_BIT(probs, i) { GET_BIT2(probs + i, i, ;, ;); }
+
+#define REV_BIT(p, i, A0, A1) IF_BIT_0(p + i) \
+ { UPDATE_0(p + i); A0; } else \
+ { UPDATE_1(p + i); A1; }
+#define REV_BIT_VAR( p, i, m) REV_BIT(p, i, i += m; m += m, m += m; i += m; )
+#define REV_BIT_CONST(p, i, m) REV_BIT(p, i, i += m; , i += m * 2; )
+#define REV_BIT_LAST( p, i, m) REV_BIT(p, i, i -= m , ; )
+
+#define TREE_DECODE(probs, limit, i) \
+ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }
+
+/* #define _LZMA_SIZE_OPT */
+
+#ifdef _LZMA_SIZE_OPT
+#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
+#else
+#define TREE_6_DECODE(probs, i) \
+ { i = 1; \
+ TREE_GET_BIT(probs, i); \
+ TREE_GET_BIT(probs, i); \
+ TREE_GET_BIT(probs, i); \
+ TREE_GET_BIT(probs, i); \
+ TREE_GET_BIT(probs, i); \
+ TREE_GET_BIT(probs, i); \
+ i -= 0x40; }
+#endif
+
+#define NORMAL_LITER_DEC TREE_GET_BIT(prob, symbol)
+#define MATCHED_LITER_DEC \
+ matchByte += matchByte; \
+ bit = offs; \
+ offs &= matchByte; \
+ probLit = prob + (offs + bit + symbol); \
+ GET_BIT2(probLit, symbol, offs ^= bit; , ;)
+
+
+
+#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
+
+#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * (UInt32)ttt; if (code < bound)
+#define UPDATE_0_CHECK range = bound;
+#define UPDATE_1_CHECK range -= bound; code -= bound;
+#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \
+ { UPDATE_0_CHECK; i = (i + i); A0; } else \
+ { UPDATE_1_CHECK; i = (i + i) + 1; A1; }
+#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
+#define TREE_DECODE_CHECK(probs, limit, i) \
+ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; }
+
+
+#define REV_BIT_CHECK(p, i, m) IF_BIT_0_CHECK(p + i) \
+ { UPDATE_0_CHECK; i += m; m += m; } else \
+ { UPDATE_1_CHECK; m += m; i += m; }
+
+
+#define kNumPosBitsMax 4
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define LenLow 0
+#define LenHigh (LenLow + 2 * (kNumPosStatesMax << kLenNumLowBits))
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
+
+#define LenChoice LenLow
+#define LenChoice2 (LenLow + (1 << kLenNumLowBits))
+
+#define kNumStates 12
+#define kNumStates2 16
+#define kNumLitStates 7
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
+
+#define kNumPosSlotBits 6
+#define kNumLenToPosStates 4
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+
+#define kMatchMinLen 2
+#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols * 2 + kLenNumHighSymbols)
+
+/* External ASM code needs same CLzmaProb array layout. So don't change it. */
+
+/* (probs_1664) is faster and better for code size at some platforms */
+/*
+#ifdef MY_CPU_X86_OR_AMD64
+*/
+#define kStartOffset 1664
+#define GET_PROBS p->probs_1664
+/*
+#define GET_PROBS p->probs + kStartOffset
+#else
+#define kStartOffset 0
+#define GET_PROBS p->probs
+#endif
+*/
+
+#define SpecPos (-kStartOffset)
+#define IsRep0Long (SpecPos + kNumFullDistances)
+#define RepLenCoder (IsRep0Long + (kNumStates2 << kNumPosBitsMax))
+#define LenCoder (RepLenCoder + kNumLenProbs)
+#define IsMatch (LenCoder + kNumLenProbs)
+#define Align (IsMatch + (kNumStates2 << kNumPosBitsMax))
+#define IsRep (Align + kAlignTableSize)
+#define IsRepG0 (IsRep + kNumStates)
+#define IsRepG1 (IsRepG0 + kNumStates)
+#define IsRepG2 (IsRepG1 + kNumStates)
+#define PosSlot (IsRepG2 + kNumStates)
+#define Literal (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
+#define NUM_BASE_PROBS (Literal + kStartOffset)
+
+#if Align != 0 && kStartOffset != 0
+ #error Stop_Compiling_Bad_LZMA_kAlign
+#endif
+
+#if NUM_BASE_PROBS != 1984
+ #error Stop_Compiling_Bad_LZMA_PROBS
+#endif
+
+
+#define LZMA_LIT_SIZE 0x300
+
+#define LzmaProps_GetNumProbs(p) (NUM_BASE_PROBS + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))
+
+
+#define CALC_POS_STATE(processedPos, pbMask) (((processedPos) & (pbMask)) << 4)
+#define COMBINED_PS_STATE (posState + state)
+#define GET_LEN_STATE (posState)
+
+#define LZMA_DIC_MIN (1 << 12)
+
+/*
+p->remainLen : shows status of LZMA decoder:
+ < kMatchSpecLenStart : normal remain
+ = kMatchSpecLenStart : finished
+ = kMatchSpecLenStart + 1 : need init range coder
+ = kMatchSpecLenStart + 2 : need init range coder and state
+*/
+
+/* ---------- LZMA_DECODE_REAL ---------- */
+/*
+LzmaDec_DecodeReal_3() can be implemented in external ASM file.
+3 - is the code compatibility version of that function for check at link time.
+*/
+
+#define LZMA_DECODE_REAL LzmaDec_DecodeReal_3
+
+/*
+LZMA_DECODE_REAL()
+In:
+ RangeCoder is normalized
+ if (p->dicPos == limit)
+ {
+ LzmaDec_TryDummy() was called before to exclude LITERAL and MATCH-REP cases.
+ So first symbol can be only MATCH-NON-REP. And if that MATCH-NON-REP symbol
+ is not END_OF_PAYALOAD_MARKER, then function returns error code.
+ }
+
+Processing:
+ first LZMA symbol will be decoded in any case
+ All checks for limits are at the end of main loop,
+ It will decode new LZMA-symbols while (p->buf < bufLimit && dicPos < limit),
+ RangeCoder is still without last normalization when (p->buf < bufLimit) is being checked.
+
+Out:
+ RangeCoder is normalized
+ Result:
+ SZ_OK - OK
+ SZ_ERROR_DATA - Error
+ p->remainLen:
+ < kMatchSpecLenStart : normal remain
+ = kMatchSpecLenStart : finished
+*/
+
+
+#ifdef _LZMA_DEC_OPT
+
+int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit);
+
+#else
+
+static
+int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
+{
+ CLzmaProb *probs = GET_PROBS;
+ unsigned state = (unsigned)p->state;
+ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
+ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
+ unsigned lc = p->prop.lc;
+ unsigned lpMask = ((unsigned)0x100 << p->prop.lp) - ((unsigned)0x100 >> lc);
+
+ Byte *dic = p->dic;
+ SizeT dicBufSize = p->dicBufSize;
+ SizeT dicPos = p->dicPos;
+
+ UInt32 processedPos = p->processedPos;
+ UInt32 checkDicSize = p->checkDicSize;
+ unsigned len = 0;
+
+ const Byte *buf = p->buf;
+ UInt32 range = p->range;
+ UInt32 code = p->code;
+
+ do
+ {
+ CLzmaProb *prob;
+ UInt32 bound;
+ unsigned ttt;
+ unsigned posState = CALC_POS_STATE(processedPos, pbMask);
+
+ prob = probs + IsMatch + COMBINED_PS_STATE;
+ IF_BIT_0(prob)
+ {
+ unsigned symbol;
+ UPDATE_0(prob);
+ prob = probs + Literal;
+ if (processedPos != 0 || checkDicSize != 0)
+ prob += (UInt32)3 * ((((processedPos << 8) + dic[(dicPos == 0 ? dicBufSize : dicPos) - 1]) & lpMask) << lc);
+ processedPos++;
+
+ if (state < kNumLitStates)
+ {
+ state -= (state < 4) ? state : 3;
+ symbol = 1;
+ #ifdef _LZMA_SIZE_OPT
+ do { NORMAL_LITER_DEC } while (symbol < 0x100);
+ #else
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ NORMAL_LITER_DEC
+ #endif
+ }
+ else
+ {
+ unsigned matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
+ unsigned offs = 0x100;
+ state -= (state < 10) ? 3 : 6;
+ symbol = 1;
+ #ifdef _LZMA_SIZE_OPT
+ do
+ {
+ unsigned bit;
+ CLzmaProb *probLit;
+ MATCHED_LITER_DEC
+ }
+ while (symbol < 0x100);
+ #else
+ {
+ unsigned bit;
+ CLzmaProb *probLit;
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ MATCHED_LITER_DEC
+ }
+ #endif
+ }
+
+ dic[dicPos++] = (Byte)symbol;
+ continue;
+ }
+
+ {
+ UPDATE_1(prob);
+ prob = probs + IsRep + state;
+ IF_BIT_0(prob)
+ {
+ UPDATE_0(prob);
+ state += kNumStates;
+ prob = probs + LenCoder;
+ }
+ else
+ {
+ UPDATE_1(prob);
+ /*
+ // that case was checked before with kBadRepCode
+ if (checkDicSize == 0 && processedPos == 0)
+ return SZ_ERROR_DATA;
+ */
+ prob = probs + IsRepG0 + state;
+ IF_BIT_0(prob)
+ {
+ UPDATE_0(prob);
+ prob = probs + IsRep0Long + COMBINED_PS_STATE;
+ IF_BIT_0(prob)
+ {
+ UPDATE_0(prob);
+ dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
+ dicPos++;
+ processedPos++;
+ state = state < kNumLitStates ? 9 : 11;
+ continue;
+ }
+ UPDATE_1(prob);
+ }
+ else
+ {
+ UInt32 distance;
+ UPDATE_1(prob);
+ prob = probs + IsRepG1 + state;
+ IF_BIT_0(prob)
+ {
+ UPDATE_0(prob);
+ distance = rep1;
+ }
+ else
+ {
+ UPDATE_1(prob);
+ prob = probs + IsRepG2 + state;
+ IF_BIT_0(prob)
+ {
+ UPDATE_0(prob);
+ distance = rep2;
+ }
+ else
+ {
+ UPDATE_1(prob);
+ distance = rep3;
+ rep3 = rep2;
+ }
+ rep2 = rep1;
+ }
+ rep1 = rep0;
+ rep0 = distance;
+ }
+ state = state < kNumLitStates ? 8 : 11;
+ prob = probs + RepLenCoder;
+ }
+
+ #ifdef _LZMA_SIZE_OPT
+ {
+ unsigned lim, offset;
+ CLzmaProb *probLen = prob + LenChoice;
+ IF_BIT_0(probLen)
+ {
+ UPDATE_0(probLen);
+ probLen = prob + LenLow + GET_LEN_STATE;
+ offset = 0;
+ lim = (1 << kLenNumLowBits);
+ }
+ else
+ {
+ UPDATE_1(probLen);
+ probLen = prob + LenChoice2;
+ IF_BIT_0(probLen)
+ {
+ UPDATE_0(probLen);
+ probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits);
+ offset = kLenNumLowSymbols;
+ lim = (1 << kLenNumLowBits);
+ }
+ else
+ {
+ UPDATE_1(probLen);
+ probLen = prob + LenHigh;
+ offset = kLenNumLowSymbols * 2;
+ lim = (1 << kLenNumHighBits);
+ }
+ }
+ TREE_DECODE(probLen, lim, len);
+ len += offset;
+ }
+ #else
+ {
+ CLzmaProb *probLen = prob + LenChoice;
+ IF_BIT_0(probLen)
+ {
+ UPDATE_0(probLen);
+ probLen = prob + LenLow + GET_LEN_STATE;
+ len = 1;
+ TREE_GET_BIT(probLen, len);
+ TREE_GET_BIT(probLen, len);
+ TREE_GET_BIT(probLen, len);
+ len -= 8;
+ }
+ else
+ {
+ UPDATE_1(probLen);
+ probLen = prob + LenChoice2;
+ IF_BIT_0(probLen)
+ {
+ UPDATE_0(probLen);
+ probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits);
+ len = 1;
+ TREE_GET_BIT(probLen, len);
+ TREE_GET_BIT(probLen, len);
+ TREE_GET_BIT(probLen, len);
+ }
+ else
+ {
+ UPDATE_1(probLen);
+ probLen = prob + LenHigh;
+ TREE_DECODE(probLen, (1 << kLenNumHighBits), len);
+ len += kLenNumLowSymbols * 2;
+ }
+ }
+ }
+ #endif
+
+ if (state >= kNumStates)
+ {
+ UInt32 distance;
+ prob = probs + PosSlot +
+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
+ TREE_6_DECODE(prob, distance);
+ if (distance >= kStartPosModelIndex)
+ {
+ unsigned posSlot = (unsigned)distance;
+ unsigned numDirectBits = (unsigned)(((distance >> 1) - 1));
+ distance = (2 | (distance & 1));
+ if (posSlot < kEndPosModelIndex)
+ {
+ distance <<= numDirectBits;
+ prob = probs + SpecPos;
+ {
+ UInt32 m = 1;
+ distance++;
+ do
+ {
+ REV_BIT_VAR(prob, distance, m);
+ }
+ while (--numDirectBits);
+ distance -= m;
+ }
+ }
+ else
+ {
+ numDirectBits -= kNumAlignBits;
+ do
+ {
+ NORMALIZE
+ range >>= 1;
+
+ {
+ UInt32 t;
+ code -= range;
+ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
+ distance = (distance << 1) + (t + 1);
+ code += range & t;
+ }
+ /*
+ distance <<= 1;
+ if (code >= range)
+ {
+ code -= range;
+ distance |= 1;
+ }
+ */
+ }
+ while (--numDirectBits);
+ prob = probs + Align;
+ distance <<= kNumAlignBits;
+ {
+ unsigned i = 1;
+ REV_BIT_CONST(prob, i, 1);
+ REV_BIT_CONST(prob, i, 2);
+ REV_BIT_CONST(prob, i, 4);
+ REV_BIT_LAST (prob, i, 8);
+ distance |= i;
+ }
+ if (distance == (UInt32)0xFFFFFFFF)
+ {
+ len = kMatchSpecLenStart;
+ state -= kNumStates;
+ break;
+ }
+ }
+ }
+
+ rep3 = rep2;
+ rep2 = rep1;
+ rep1 = rep0;
+ rep0 = distance + 1;
+ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
+ if (distance >= (checkDicSize == 0 ? processedPos: checkDicSize))
+ {
+ p->dicPos = dicPos;
+ return SZ_ERROR_DATA;
+ }
+ }
+
+ len += kMatchMinLen;
+
+ {
+ SizeT rem;
+ unsigned curLen;
+ SizeT pos;
+
+ if ((rem = limit - dicPos) == 0)
+ {
+ p->dicPos = dicPos;
+ return SZ_ERROR_DATA;
+ }
+
+ curLen = ((rem < len) ? (unsigned)rem : len);
+ pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0);
+
+ processedPos += (UInt32)curLen;
+
+ len -= curLen;
+ if (curLen <= dicBufSize - pos)
+ {
+ Byte *dest = dic + dicPos;
+ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
+ const Byte *lim = dest + curLen;
+ dicPos += (SizeT)curLen;
+ do
+ *(dest) = (Byte)*(dest + src);
+ while (++dest != lim);
+ }
+ else
+ {
+ do
+ {
+ dic[dicPos++] = dic[pos];
+ if (++pos == dicBufSize)
+ pos = 0;
+ }
+ while (--curLen != 0);
+ }
+ }
+ }
+ }
+ while (dicPos < limit && buf < bufLimit);
+
+ NORMALIZE;
+
+ p->buf = buf;
+ p->range = range;
+ p->code = code;
+ p->remainLen = (UInt32)len;
+ p->dicPos = dicPos;
+ p->processedPos = processedPos;
+ p->reps[0] = rep0;
+ p->reps[1] = rep1;
+ p->reps[2] = rep2;
+ p->reps[3] = rep3;
+ p->state = (UInt32)state;
+
+ return SZ_OK;
+}
+#endif
+
+static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
+{
+ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
+ {
+ Byte *dic = p->dic;
+ SizeT dicPos = p->dicPos;
+ SizeT dicBufSize = p->dicBufSize;
+ unsigned len = (unsigned)p->remainLen;
+ SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */
+ SizeT rem = limit - dicPos;
+ if (rem < len)
+ len = (unsigned)(rem);
+
+ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
+ p->checkDicSize = p->prop.dicSize;
+
+ p->processedPos += (UInt32)len;
+ p->remainLen -= (UInt32)len;
+ while (len != 0)
+ {
+ len--;
+ dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)];
+ dicPos++;
+ }
+ p->dicPos = dicPos;
+ }
+}
+
+
+#define kRange0 0xFFFFFFFF
+#define kBound0 ((kRange0 >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1))
+#define kBadRepCode (kBound0 + (((kRange0 - kBound0) >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1)))
+#if kBadRepCode != (0xC0000000 - 0x400)
+ #error Stop_Compiling_Bad_LZMA_Check
+#endif
+
+static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit, SizeT memlimit)
+{
+ do
+ {
+ SizeT limit2 = limit;
+ if (p->checkDicSize == 0)
+ {
+ UInt32 rem = p->prop.dicSize - p->processedPos;
+ if (limit - p->dicPos > rem) {
+ if (p->dicBufSize < p->prop.dicSize) {
+ p->dicBufSize = p->prop.dicSize;
+ if (p->dicBufSize > memlimit) {
+ return SZ_ERROR_MEM;
+ }
+ Byte *tmp = realloc(p->dic, p->dicBufSize);
+ if (!tmp) {
+ return SZ_ERROR_MEM;
+ }
+ p->dic = tmp;
+ }
+ limit2 = p->dicPos + rem;
+ }
+
+ if (p->processedPos == 0)
+ if (p->code >= kBadRepCode)
+ return SZ_ERROR_DATA;
+ }
+
+ RINOK(LZMA_DECODE_REAL(p, limit2, bufLimit));
+
+ if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize)
+ p->checkDicSize = p->prop.dicSize;
+
+ LzmaDec_WriteRem(p, limit);
+ }
+ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);
+
+ return 0;
+}
+
+typedef enum
+{
+ DUMMY_ERROR, /* unexpected end of input stream */
+ DUMMY_LIT,
+ DUMMY_MATCH,
+ DUMMY_REP
+} ELzmaDummy;
+
+static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
+{
+ UInt32 range = p->range;
+ UInt32 code = p->code;
+ const Byte *bufLimit = buf + inSize;
+ const CLzmaProb *probs = GET_PROBS;
+ unsigned state = (unsigned)p->state;
+ ELzmaDummy res;
+
+ {
+ const CLzmaProb *prob;
+ UInt32 bound;
+ unsigned ttt;
+ unsigned posState = CALC_POS_STATE(p->processedPos, (1 << p->prop.pb) - 1);
+
+ prob = probs + IsMatch + COMBINED_PS_STATE;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK
+
+ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
+
+ prob = probs + Literal;
+ if (p->checkDicSize != 0 || p->processedPos != 0)
+ prob += ((UInt32)LZMA_LIT_SIZE *
+ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
+ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));
+
+ if (state < kNumLitStates)
+ {
+ unsigned symbol = 1;
+ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
+ }
+ else
+ {
+ unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
+ (p->dicPos < p->reps[0] ? p->dicBufSize : 0)];
+ unsigned offs = 0x100;
+ unsigned symbol = 1;
+ do
+ {
+ unsigned bit;
+ const CLzmaProb *probLit;
+ matchByte += matchByte;
+ bit = offs;
+ offs &= matchByte;
+ probLit = prob + (offs + bit + symbol);
+ GET_BIT2_CHECK(probLit, symbol, offs ^= bit; , ; )
+ }
+ while (symbol < 0x100);
+ }
+ res = DUMMY_LIT;
+ }
+ else
+ {
+ unsigned len;
+ UPDATE_1_CHECK;
+
+ prob = probs + IsRep + state;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK;
+ state = 0;
+ prob = probs + LenCoder;
+ res = DUMMY_MATCH;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ res = DUMMY_REP;
+ prob = probs + IsRepG0 + state;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK;
+ prob = probs + IsRep0Long + COMBINED_PS_STATE;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK;
+ NORMALIZE_CHECK;
+ return DUMMY_REP;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ }
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ prob = probs + IsRepG1 + state;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ prob = probs + IsRepG2 + state;
+ IF_BIT_0_CHECK(prob)
+ {
+ UPDATE_0_CHECK;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ }
+ }
+ }
+ state = kNumStates;
+ prob = probs + RepLenCoder;
+ }
+ {
+ unsigned limit, offset;
+ const CLzmaProb *probLen = prob + LenChoice;
+ IF_BIT_0_CHECK(probLen)
+ {
+ UPDATE_0_CHECK;
+ probLen = prob + LenLow + GET_LEN_STATE;
+ offset = 0;
+ limit = 1 << kLenNumLowBits;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ probLen = prob + LenChoice2;
+ IF_BIT_0_CHECK(probLen)
+ {
+ UPDATE_0_CHECK;
+ probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits);
+ offset = kLenNumLowSymbols;
+ limit = 1 << kLenNumLowBits;
+ }
+ else
+ {
+ UPDATE_1_CHECK;
+ probLen = prob + LenHigh;
+ offset = kLenNumLowSymbols * 2;
+ limit = 1 << kLenNumHighBits;
+ }
+ }
+ TREE_DECODE_CHECK(probLen, limit, len);
+ len += offset;
+ }
+
+ if (state < 4)
+ {
+ unsigned posSlot;
+ prob = probs + PosSlot +
+ ((len < kNumLenToPosStates - 1 ? len : kNumLenToPosStates - 1) <<
+ kNumPosSlotBits);
+ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
+ if (posSlot >= kStartPosModelIndex)
+ {
+ unsigned numDirectBits = ((posSlot >> 1) - 1);
+
+ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
+
+ if (posSlot < kEndPosModelIndex)
+ {
+ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits);
+ }
+ else
+ {
+ numDirectBits -= kNumAlignBits;
+ do
+ {
+ NORMALIZE_CHECK
+ range >>= 1;
+ code -= range & (((code - range) >> 31) - 1);
+ /* if (code >= range) code -= range; */
+ }
+ while (--numDirectBits);
+ prob = probs + Align;
+ numDirectBits = kNumAlignBits;
+ }
+ {
+ unsigned i = 1;
+ unsigned m = 1;
+ do
+ {
+ REV_BIT_CHECK(prob, i, m);
+ }
+ while (--numDirectBits);
+ }
+ }
+ }
+ }
+ }
+ NORMALIZE_CHECK;
+ return res;
+}
+
+
+static void LzmaDec_InitDicAndState(CLzmaDec *p, BoolInt initDic, BoolInt initState)
+{
+ p->remainLen = kMatchSpecLenStart + 1;
+ p->tempBufSize = 0;
+
+ if (initDic)
+ {
+ p->processedPos = 0;
+ p->checkDicSize = 0;
+ p->remainLen = kMatchSpecLenStart + 2;
+ }
+ if (initState)
+ p->remainLen = kMatchSpecLenStart + 2;
+}
+
+void LzmaDec_Init(CLzmaDec *p)
+{
+ p->dicPos = 0;
+ LzmaDec_InitDicAndState(p, True, True);
+}
+
+
+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
+ ELzmaFinishMode finishMode, ELzmaStatus *status, SizeT memlimit)
+{
+ SizeT inSize = *srcLen;
+ (*srcLen) = 0;
+
+ *status = LZMA_STATUS_NOT_SPECIFIED;
+
+ if (p->remainLen > kMatchSpecLenStart)
+ {
+ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
+ p->tempBuf[p->tempBufSize++] = *src++;
+ if (p->tempBufSize != 0 && p->tempBuf[0] != 0)
+ return SZ_ERROR_DATA;
+ if (p->tempBufSize < RC_INIT_SIZE)
+ {
+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+ return SZ_OK;
+ }
+ p->code =
+ ((UInt32)p->tempBuf[1] << 24)
+ | ((UInt32)p->tempBuf[2] << 16)
+ | ((UInt32)p->tempBuf[3] << 8)
+ | ((UInt32)p->tempBuf[4]);
+ p->range = 0xFFFFFFFF;
+ p->tempBufSize = 0;
+
+ if (p->remainLen > kMatchSpecLenStart + 1)
+ {
+ SizeT numProbs = LzmaProps_GetNumProbs(&p->prop);
+ SizeT i;
+ CLzmaProb *probs = p->probs;
+ for (i = 0; i < numProbs; i++)
+ probs[i] = kBitModelTotal >> 1;
+ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
+ p->state = 0;
+ }
+
+ p->remainLen = 0;
+ }
+
+ LzmaDec_WriteRem(p, dicLimit);
+
+ while (p->remainLen != kMatchSpecLenStart)
+ {
+ int checkEndMarkNow = 0;
+
+ if (p->dicPos >= dicLimit)
+ {
+ if (p->remainLen == 0 && p->code == 0)
+ {
+ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
+ return SZ_OK;
+ }
+ if (finishMode == LZMA_FINISH_ANY)
+ {
+ *status = LZMA_STATUS_NOT_FINISHED;
+ return SZ_OK;
+ }
+ if (p->remainLen != 0)
+ {
+ *status = LZMA_STATUS_NOT_FINISHED;
+ return SZ_ERROR_DATA;
+ }
+ checkEndMarkNow = 1;
+ }
+
+ if (p->tempBufSize == 0)
+ {
+ SizeT processed;
+ const Byte *bufLimit;
+ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
+ {
+ int dummyRes = LzmaDec_TryDummy(p, src, inSize);
+ if (dummyRes == DUMMY_ERROR)
+ {
+ memcpy(p->tempBuf, src, inSize);
+ p->tempBufSize = (unsigned)inSize;
+ (*srcLen) += inSize;
+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+ return SZ_OK;
+ }
+ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
+ {
+ *status = LZMA_STATUS_NOT_FINISHED;
+ return SZ_ERROR_DATA;
+ }
+ bufLimit = src;
+ }
+ else
+ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
+ p->buf = src;
+ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit, memlimit) != 0)
+ return SZ_ERROR_DATA;
+ processed = (SizeT)(p->buf - src);
+ (*srcLen) += processed;
+ src += processed;
+ inSize -= processed;
+ }
+ else
+ {
+ unsigned rem = p->tempBufSize, lookAhead = 0;
+ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
+ p->tempBuf[rem++] = src[lookAhead++];
+ p->tempBufSize = rem;
+ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
+ {
+ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, (SizeT)rem);
+ if (dummyRes == DUMMY_ERROR)
+ {
+ (*srcLen) += (SizeT)lookAhead;
+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+ return SZ_OK;
+ }
+ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
+ {
+ *status = LZMA_STATUS_NOT_FINISHED;
+ return SZ_ERROR_DATA;
+ }
+ }
+ p->buf = p->tempBuf;
+ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf, memlimit) != 0)
+ return SZ_ERROR_DATA;
+
+ {
+ unsigned kkk = (unsigned)(p->buf - p->tempBuf);
+ if (rem < kkk)
+ return SZ_ERROR_FAIL; /* some internal error */
+ rem -= kkk;
+ if (lookAhead < rem)
+ return SZ_ERROR_FAIL; /* some internal error */
+ lookAhead -= rem;
+ }
+ (*srcLen) += (SizeT)lookAhead;
+ src += lookAhead;
+ inSize -= (SizeT)lookAhead;
+ p->tempBufSize = 0;
+ }
+ }
+
+ if (p->code != 0)
+ return SZ_ERROR_DATA;
+ *status = LZMA_STATUS_FINISHED_WITH_MARK;
+ return SZ_OK;
+}
+
+
+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status, SizeT memlimit)
+{
+ SizeT outSize = *destLen;
+ SizeT inSize = *srcLen;
+ *srcLen = *destLen = 0;
+ for (;;)
+ {
+ SizeT inSizeCur = inSize, outSizeCur, dicPos;
+ ELzmaFinishMode curFinishMode;
+ SRes res;
+ if (p->dicPos == p->dicBufSize) {
+ if (p->dicBufSize < p->prop.dicSize) {
+ if (p->dicBufSize < memlimit) {
+ p->dicBufSize = p->dicBufSize << 2;
+ if (p->dicBufSize > memlimit) {
+ p->dicBufSize = memlimit;
+ }
+ if (p->dicBufSize > p->prop.dicSize) {
+ p->dicBufSize = p->prop.dicSize;
+ }
+ Byte *tmp = realloc(p->dic, p->dicBufSize);
+ if (!tmp) {
+ return SZ_ERROR_MEM;
+ }
+ p->dic = tmp;
+ } else {
+ return SZ_ERROR_MEM;
+ }
+ } else {
+ p->dicPos = 0;
+ }
+ }
+ dicPos = p->dicPos;
+ if (outSize > p->dicBufSize - dicPos)
+ {
+ outSizeCur = p->dicBufSize;
+ curFinishMode = LZMA_FINISH_ANY;
+ }
+ else
+ {
+ outSizeCur = dicPos + outSize;
+ curFinishMode = finishMode;
+ }
+
+ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status, memlimit);
+ src += inSizeCur;
+ inSize -= inSizeCur;
+ *srcLen += inSizeCur;
+ outSizeCur = p->dicPos - dicPos;
+ memcpy(dest, p->dic + dicPos, outSizeCur);
+ dest += outSizeCur;
+ outSize -= outSizeCur;
+ *destLen += outSizeCur;
+ if (res != 0)
+ return res;
+ if (outSizeCur == 0 || outSize == 0)
+ return SZ_OK;
+ }
+}
+
+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc)
+{
+ ISzAlloc_Free(alloc, p->probs);
+ p->probs = NULL;
+}
+
+static void LzmaDec_FreeDict(CLzmaDec *p, ISzAllocPtr alloc)
+{
+ ISzAlloc_Free(alloc, p->dic);
+ p->dic = NULL;
+}
+
+void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc)
+{
+ LzmaDec_FreeProbs(p, alloc);
+ LzmaDec_FreeDict(p, alloc);
+}
+
+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
+{
+ UInt32 dicSize;
+ Byte d;
+
+ if (size < LZMA_PROPS_SIZE)
+ return SZ_ERROR_UNSUPPORTED;
+ else
+ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);
+
+ if (dicSize < LZMA_DIC_MIN)
+ dicSize = LZMA_DIC_MIN;
+ p->dicSize = dicSize;
+
+ d = data[0];
+ if (d >= (9 * 5 * 5))
+ return SZ_ERROR_UNSUPPORTED;
+
+ p->lc = (Byte)(d % 9);
+ d /= 9;
+ p->pb = (Byte)(d / 5);
+ p->lp = (Byte)(d % 5);
+ p->_pad_ = 0;
+
+ return SZ_OK;
+}
+
+static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAllocPtr alloc)
+{
+ UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
+ if (!p->probs || numProbs != p->numProbs)
+ {
+ LzmaDec_FreeProbs(p, alloc);
+ p->probs = (CLzmaProb *)ISzAlloc_Alloc(alloc, numProbs * sizeof(CLzmaProb));
+ if (!p->probs)
+ return SZ_ERROR_MEM;
+ p->probs_1664 = p->probs + 1664;
+ p->numProbs = numProbs;
+ }
+ return SZ_OK;
+}
+
+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc)
+{
+ CLzmaProps propNew;
+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+ p->prop = propNew;
+ return SZ_OK;
+}
+
+SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc)
+{
+ CLzmaProps propNew;
+ SizeT dicBufSize;
+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+
+ {
+ UInt32 dictSize = propNew.dicSize;
+ SizeT mask = ((UInt32)1 << 12) - 1;
+ if (dictSize >= ((UInt32)1 << 30)) mask = ((UInt32)1 << 22) - 1;
+ else if (dictSize >= ((UInt32)1 << 22)) mask = ((UInt32)1 << 20) - 1;;
+ dicBufSize = ((SizeT)dictSize + mask) & ~mask;
+ if (dicBufSize < dictSize)
+ dicBufSize = dictSize;
+ }
+ if (dicBufSize > LZMA_DIC_MIN) {
+ dicBufSize = LZMA_DIC_MIN;
+ }
+
+ if (!p->dic || dicBufSize != p->dicBufSize)
+ {
+ LzmaDec_FreeDict(p, alloc);
+ p->dic = (Byte *)ISzAlloc_Alloc(alloc, dicBufSize);
+ if (!p->dic)
+ {
+ LzmaDec_FreeProbs(p, alloc);
+ return SZ_ERROR_MEM;
+ }
+ }
+ p->dicBufSize = dicBufSize;
+ p->prop = propNew;
+ return SZ_OK;
+}
+
+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+ ELzmaStatus *status, ISzAllocPtr alloc)
+{
+ CLzmaDec p;
+ SRes res;
+ SizeT outSize = *destLen, inSize = *srcLen;
+ *destLen = *srcLen = 0;
+ *status = LZMA_STATUS_NOT_SPECIFIED;
+ if (inSize < RC_INIT_SIZE)
+ return SZ_ERROR_INPUT_EOF;
+ LzmaDec_Construct(&p);
+ RINOK(LzmaDec_AllocateProbs(&p, propData, propSize, alloc));
+ p.dic = dest;
+ p.dicBufSize = outSize;
+ LzmaDec_Init(&p);
+ *srcLen = inSize;
+ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status, SIZE_MAX);
+ *destLen = p.dicPos;
+ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
+ res = SZ_ERROR_INPUT_EOF;
+ LzmaDec_FreeProbs(&p, alloc);
+ return res;
+}
diff --git a/htp/lzma/LzmaDec.h b/htp/lzma/LzmaDec.h new file mode 100644 index 0000000..20b5228 --- /dev/null +++ b/htp/lzma/LzmaDec.h @@ -0,0 +1,234 @@ +/* LzmaDec.h -- LZMA Decoder
+2018-04-21 : Igor Pavlov : Public domain */
+
+#ifndef __LZMA_DEC_H
+#define __LZMA_DEC_H
+
+#include "7zTypes.h"
+
+EXTERN_C_BEGIN
+
+/* #define _LZMA_PROB32 */
+/* _LZMA_PROB32 can increase the speed on some CPUs,
+ but memory usage for CLzmaDec::probs will be doubled in that case */
+
+typedef
+#ifdef _LZMA_PROB32
+ UInt32
+#else
+ UInt16
+#endif
+ CLzmaProb;
+
+
+/* ---------- LZMA Properties ---------- */
+
+#define LZMA_PROPS_SIZE 5
+
+typedef struct _CLzmaProps
+{
+ Byte lc;
+ Byte lp;
+ Byte pb;
+ Byte _pad_;
+ UInt32 dicSize;
+} CLzmaProps;
+
+/* LzmaProps_Decode - decodes properties
+Returns:
+ SZ_OK
+ SZ_ERROR_UNSUPPORTED - Unsupported properties
+*/
+
+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
+
+
+/* ---------- LZMA Decoder state ---------- */
+
+/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
+ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
+
+#define LZMA_REQUIRED_INPUT_MAX 20
+
+typedef struct
+{
+ /* Don't change this structure. ASM code can use it. */
+ CLzmaProps prop;
+ CLzmaProb *probs;
+ CLzmaProb *probs_1664;
+ Byte *dic;
+ SizeT dicBufSize;
+ SizeT dicPos;
+ const Byte *buf;
+ UInt32 range;
+ UInt32 code;
+ UInt32 processedPos;
+ UInt32 checkDicSize;
+ UInt32 reps[4];
+ UInt32 state;
+ UInt32 remainLen;
+
+ UInt32 numProbs;
+ unsigned tempBufSize;
+ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
+} CLzmaDec;
+
+#define LzmaDec_Construct(p) { (p)->dic = NULL; (p)->probs = NULL; }
+
+void LzmaDec_Init(CLzmaDec *p);
+
+/* There are two types of LZMA streams:
+ - Stream with end mark. That end mark adds about 6 bytes to compressed size.
+ - Stream without end mark. You must know exact uncompressed size to decompress such stream. */
+
+typedef enum
+{
+ LZMA_FINISH_ANY, /* finish at any point */
+ LZMA_FINISH_END /* block must be finished at the end */
+} ELzmaFinishMode;
+
+/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
+
+ You must use LZMA_FINISH_END, when you know that current output buffer
+ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
+
+ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
+ and output value of destLen will be less than output buffer size limit.
+ You can check status result also.
+
+ You can use multiple checks to test data integrity after full decompression:
+ 1) Check Result and "status" variable.
+ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
+ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
+ You must use correct finish mode in that case. */
+
+typedef enum
+{
+ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
+ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
+ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
+ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
+} ELzmaStatus;
+
+/* ELzmaStatus is used only as output value for function call */
+
+
+/* ---------- Interfaces ---------- */
+
+/* There are 3 levels of interfaces:
+ 1) Dictionary Interface
+ 2) Buffer Interface
+ 3) One Call Interface
+ You can select any of these interfaces, but don't mix functions from different
+ groups for same object. */
+
+
+/* There are two variants to allocate state for Dictionary Interface:
+ 1) LzmaDec_Allocate / LzmaDec_Free
+ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
+ You can use variant 2, if you set dictionary buffer manually.
+ For Buffer Interface you must always use variant 1.
+
+LzmaDec_Allocate* can return:
+ SZ_OK
+ SZ_ERROR_MEM - Memory allocation error
+ SZ_ERROR_UNSUPPORTED - Unsupported properties
+*/
+
+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc);
+
+SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc);
+void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc);
+
+/* ---------- Dictionary Interface ---------- */
+
+/* You can use it, if you want to eliminate the overhead for data copying from
+ dictionary to some other external buffer.
+ You must work with CLzmaDec variables directly in this interface.
+
+ STEPS:
+ LzmaDec_Construct()
+ LzmaDec_Allocate()
+ for (each new stream)
+ {
+ LzmaDec_Init()
+ while (it needs more decompression)
+ {
+ LzmaDec_DecodeToDic()
+ use data from CLzmaDec::dic and update CLzmaDec::dicPos
+ }
+ }
+ LzmaDec_Free()
+*/
+
+/* LzmaDec_DecodeToDic
+
+ The decoding to internal dictionary buffer (CLzmaDec::dic).
+ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
+
+finishMode:
+ It has meaning only if the decoding reaches output limit (dicLimit).
+ LZMA_FINISH_ANY - Decode just dicLimit bytes.
+ LZMA_FINISH_END - Stream must be finished after dicLimit.
+
+Returns:
+ SZ_OK
+ status:
+ LZMA_STATUS_FINISHED_WITH_MARK
+ LZMA_STATUS_NOT_FINISHED
+ LZMA_STATUS_NEEDS_MORE_INPUT
+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+ SZ_ERROR_DATA - Data error
+*/
+
+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
+ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status, SizeT memlimit);
+
+
+/* ---------- Buffer Interface ---------- */
+
+/* It's zlib-like interface.
+ See LzmaDec_DecodeToDic description for information about STEPS and return results,
+ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
+ to work with CLzmaDec variables manually.
+
+finishMode:
+ It has meaning only if the decoding reaches output limit (*destLen).
+ LZMA_FINISH_ANY - Decode just destLen bytes.
+ LZMA_FINISH_END - Stream must be finished after (*destLen).
+*/
+
+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
+ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status, SizeT memlimit);
+
+
+/* ---------- One Call Interface ---------- */
+
+/* LzmaDecode
+
+finishMode:
+ It has meaning only if the decoding reaches output limit (*destLen).
+ LZMA_FINISH_ANY - Decode just destLen bytes.
+ LZMA_FINISH_END - Stream must be finished after (*destLen).
+
+Returns:
+ SZ_OK
+ status:
+ LZMA_STATUS_FINISHED_WITH_MARK
+ LZMA_STATUS_NOT_FINISHED
+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+ SZ_ERROR_DATA - Data error
+ SZ_ERROR_MEM - Memory allocation error
+ SZ_ERROR_UNSUPPORTED - Unsupported properties
+ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
+*/
+
+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+ ELzmaStatus *status, ISzAllocPtr alloc);
+
+EXTERN_C_END
+
+#endif
diff --git a/htp/lzma/Makefile.am b/htp/lzma/Makefile.am new file mode 100644 index 0000000..5fa3ec5 --- /dev/null +++ b/htp/lzma/Makefile.am @@ -0,0 +1,16 @@ + +h_sources = LzmaDec.h 7zTypes.h + +h_sources_private = LzFind.h LzHash.h Compiler.h Precomp.h + +c_sources = LzFind.c LzmaDec.c + +AM_CFLAGS = -I$(top_srcdir) -D_GNU_SOURCE -g -Wall -Wextra -std=gnu99 -pedantic \ + -Wextra -Wno-missing-field-initializers -Wshadow -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wno-unused-parameter + +library_includedir = $(includedir)/$(GENERIC_LIBRARY_NAME)/lzma +library_include_HEADERS = $(h_sources) + +noinst_LTLIBRARIES = liblzma-c.la +liblzma_c_la_SOURCES = $(h_sources) $(h_sources_private) $(c_sources) diff --git a/htp/lzma/Precomp.h b/htp/lzma/Precomp.h new file mode 100644 index 0000000..edb5814 --- /dev/null +++ b/htp/lzma/Precomp.h @@ -0,0 +1,10 @@ +/* Precomp.h -- StdAfx
+2013-11-12 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_PRECOMP_H
+#define __7Z_PRECOMP_H
+
+#include "Compiler.h"
+/* #include "7zTypes.h" */
+
+#endif
diff --git a/htp/strlcat.c b/htp/strlcat.c new file mode 100644 index 0000000..fc1776d --- /dev/null +++ b/htp/strlcat.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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 ``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. + */ + +/* $Id: strlcatu.c,v 1.4 2003/10/20 15:03:27 chrisgreen Exp $ */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +#ifndef HAVE_STRLCAT + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(initial dst) + strlen(src); if retval >= siz, + * truncation occurred. + */ +size_t strlcat(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} +#endif diff --git a/htp/strlcpy.c b/htp/strlcpy.c new file mode 100644 index 0000000..227f52a --- /dev/null +++ b/htp/strlcpy.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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 ``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. + */ + +/* $Id: strlcpyu.c,v 1.4 2003/10/20 15:03:27 chrisgreen Exp $ */ + +#include "htp_config_auto.h" + +#include "htp_private.h" + +#ifndef HAVE_STRLCPY + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t strlcpy(char *dst, const char *src, size_t siz) +{ + register char *d = dst; + register const char *s = src; + register size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif diff --git a/m4/iconv.m4 b/m4/iconv.m4 new file mode 100644 index 0000000..e2041b9 --- /dev/null +++ b/m4/iconv.m4 @@ -0,0 +1,214 @@ +# iconv.m4 serial 11 (gettext-0.18.1) +dnl Copyright (C) 2000-2002, 2007-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], +[ + dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([iconv]) +]) + +AC_DEFUN([AM_ICONV_LINK], +[ + dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and + dnl those with the standalone portable GNU libiconv installed). + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV + dnl accordingly. + AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) + + dnl Add $INCICONV to CPPFLAGS before performing the following checks, + dnl because if the user has installed libiconv and not disabled its use + dnl via --without-libiconv-prefix, he wants to use it. The first + dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. + am_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) + + AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [ + am_cv_func_iconv="no, consider installing GNU libiconv" + am_cv_lib_iconv=no + AC_TRY_LINK([#include <stdlib.h> +#include <iconv.h>], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + [am_cv_func_iconv=yes]) + if test "$am_cv_func_iconv" != yes; then + am_save_LIBS="$LIBS" + LIBS="$LIBS $LIBICONV" + AC_TRY_LINK([#include <stdlib.h> +#include <iconv.h>], + [iconv_t cd = iconv_open("",""); + iconv(cd,NULL,NULL,NULL,NULL); + iconv_close(cd);], + [am_cv_lib_iconv=yes] + [am_cv_func_iconv=yes]) + LIBS="$am_save_LIBS" + fi + ]) + if test "$am_cv_func_iconv" = yes; then + AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [ + dnl This tests against bugs in AIX 5.1, HP-UX 11.11, Solaris 10. + am_save_LIBS="$LIBS" + if test $am_cv_lib_iconv = yes; then + LIBS="$LIBS $LIBICONV" + fi + AC_TRY_RUN([ +#include <iconv.h> +#include <string.h> +int main () +{ + /* Test against AIX 5.1 bug: Failures are not distinguishable from successful + returns. */ + { + iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8"); + if (cd_utf8_to_88591 != (iconv_t)(-1)) + { + static const char input[] = "\342\202\254"; /* EURO SIGN */ + char buf[10]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_utf8_to_88591, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + return 1; + } + } + /* Test against Solaris 10 bug: Failures are not distinguishable from + successful returns. */ + { + iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646"); + if (cd_ascii_to_88591 != (iconv_t)(-1)) + { + static const char input[] = "\263"; + char buf[10]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_ascii_to_88591, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if (res == 0) + return 1; + } + } +#if 0 /* This bug could be worked around by the caller. */ + /* Test against HP-UX 11.11 bug: Positive return value instead of 0. */ + { + iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591"); + if (cd_88591_to_utf8 != (iconv_t)(-1)) + { + static const char input[] = "\304rger mit b\366sen B\374bchen ohne Augenma\337"; + char buf[50]; + const char *inptr = input; + size_t inbytesleft = strlen (input); + char *outptr = buf; + size_t outbytesleft = sizeof (buf); + size_t res = iconv (cd_88591_to_utf8, + (char **) &inptr, &inbytesleft, + &outptr, &outbytesleft); + if ((int)res > 0) + return 1; + } + } +#endif + /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is + provided. */ + if (/* Try standardized names. */ + iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1) + /* Try IRIX, OSF/1 names. */ + && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1) + /* Try AIX names. */ + && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1) + /* Try HP-UX names. */ + && iconv_open ("utf8", "eucJP") == (iconv_t)(-1)) + return 1; + return 0; +}], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no], + [case "$host_os" in + aix* | hpux*) am_cv_func_iconv_works="guessing no" ;; + *) am_cv_func_iconv_works="guessing yes" ;; + esac]) + LIBS="$am_save_LIBS" + ]) + case "$am_cv_func_iconv_works" in + *no) am_func_iconv=no am_cv_lib_iconv=no ;; + *) am_func_iconv=yes ;; + esac + else + am_func_iconv=no am_cv_lib_iconv=no + fi + if test "$am_func_iconv" = yes; then + AC_DEFINE([HAVE_ICONV], [1], + [Define if you have the iconv() function and it works.]) + fi + if test "$am_cv_lib_iconv" = yes; then + AC_MSG_CHECKING([how to link with libiconv]) + AC_MSG_RESULT([$LIBICONV]) + else + dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV + dnl either. + CPPFLAGS="$am_save_CPPFLAGS" + LIBICONV= + LTLIBICONV= + fi + AC_SUBST([LIBICONV]) + AC_SUBST([LTLIBICONV]) +]) + +dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to +dnl avoid warnings like +dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required". +dnl This is tricky because of the way 'aclocal' is implemented: +dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN. +dnl Otherwise aclocal's initial scan pass would miss the macro definition. +dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN expansions. +dnl Otherwise aclocal would emit many "Use of uninitialized value $1" +dnl warnings. +m4_define([gl_iconv_AC_DEFUN], + m4_version_prereq([2.64], + [[AC_DEFUN_ONCE( + [$1], [$2])]], + [[AC_DEFUN( + [$1], [$2])]])) +gl_iconv_AC_DEFUN([AM_ICONV], +[ + AM_ICONV_LINK + if test "$am_cv_func_iconv" = yes; then + AC_MSG_CHECKING([for iconv declaration]) + AC_CACHE_VAL([am_cv_proto_iconv], [ + AC_TRY_COMPILE([ +#include <stdlib.h> +#include <iconv.h> +extern +#ifdef __cplusplus +"C" +#endif +#if defined(__STDC__) || defined(__cplusplus) +size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); +#else +size_t iconv(); +#endif +], [], [am_cv_proto_iconv_arg1=""], [am_cv_proto_iconv_arg1="const"]) + am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) + am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` + AC_MSG_RESULT([ + $am_cv_proto_iconv]) + AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1], + [Define as const if the declaration of iconv() needs const.]) + fi +]) diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4 new file mode 100644 index 0000000..ebb3052 --- /dev/null +++ b/m4/lib-ld.m4 @@ -0,0 +1,110 @@ +# lib-ld.m4 serial 4 (gettext-0.18) +dnl Copyright (C) 1996-2003, 2009-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision +dnl with libtool.m4. + +dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld], +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 </dev/null` in +*GNU* | *'with BFD'*) + acl_cv_prog_gnu_ld=yes ;; +*) + acl_cv_prog_gnu_ld=no ;; +esac]) +with_gnu_ld=$acl_cv_prog_gnu_ld +]) + +dnl From libtool-1.4. Sets the variable LD. +AC_DEFUN([AC_LIB_PROG_LD], +[AC_ARG_WITH([gnu-ld], +[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]], +test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no) +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]* | [A-Za-z]:[\\/]*)] + [re_direlt='/[^/][^/]*/\.\./'] + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL([acl_cv_path_LD], +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$acl_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT([$LD]) +else + AC_MSG_RESULT([no]) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_LIB_PROG_LD_GNU +]) diff --git a/m4/lib-link.m4 b/m4/lib-link.m4 new file mode 100644 index 0000000..c73bd8e --- /dev/null +++ b/m4/lib-link.m4 @@ -0,0 +1,774 @@ +# lib-link.m4 serial 21 (gettext-0.18) +dnl Copyright (C) 2001-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ([2.54]) + +dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and +dnl augments the CPPFLAGS variable. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + pushdef([Name],[translit([$1],[./-], [___])]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + ac_cv_lib[]Name[]_libs="$LIB[]NAME" + ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" + ac_cv_lib[]Name[]_cppflags="$INC[]NAME" + ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX" + ]) + LIB[]NAME="$ac_cv_lib[]Name[]_libs" + LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" + INC[]NAME="$ac_cv_lib[]Name[]_cppflags" + LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the + dnl results of this search when this library appears as a dependency. + HAVE_LIB[]NAME=yes + popdef([NAME]) + popdef([Name]) +]) + +dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, [missing-message]) +dnl searches for libname and the libraries corresponding to explicit and +dnl implicit dependencies, together with the specified include files and +dnl the ability to compile and link the specified testcode. The missing-message +dnl defaults to 'no' and may contain additional hints for the user. +dnl If found, it sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} +dnl and LTLIB${NAME} variables and augments the CPPFLAGS variable, and +dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs +dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. +dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname +dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + pushdef([Name],[translit([$1],[./-], [___])]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + + dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + + dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, + dnl because if the user has installed lib[]Name and not disabled its use + dnl via --without-lib[]Name-prefix, he wants to use it. + ac_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + + AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ + ac_save_LIBS="$LIBS" + dnl If $LIB[]NAME contains some -l options, add it to the end of LIBS, + dnl because these -l options might require -L options that are present in + dnl LIBS. -l options benefit only from the -L options listed before it. + dnl Otherwise, add it to the front of LIBS, because it may be a static + dnl library that depends on another static library that is present in LIBS. + dnl Static libraries benefit only from the static libraries listed after + dnl it. + case " $LIB[]NAME" in + *" -l"*) LIBS="$LIBS $LIB[]NAME" ;; + *) LIBS="$LIB[]NAME $LIBS" ;; + esac + AC_TRY_LINK([$3], [$4], + [ac_cv_lib[]Name=yes], + [ac_cv_lib[]Name='m4_if([$5], [], [no], [[$5]])']) + LIBS="$ac_save_LIBS" + ]) + if test "$ac_cv_lib[]Name" = yes; then + HAVE_LIB[]NAME=yes + AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the lib][$1 library.]) + AC_MSG_CHECKING([how to link with lib[]$1]) + AC_MSG_RESULT([$LIB[]NAME]) + else + HAVE_LIB[]NAME=no + dnl If $LIB[]NAME didn't lead to a usable library, we don't need + dnl $INC[]NAME either. + CPPFLAGS="$ac_save_CPPFLAGS" + LIB[]NAME= + LTLIB[]NAME= + LIB[]NAME[]_PREFIX= + fi + AC_SUBST([HAVE_LIB]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + AC_SUBST([LIB]NAME[_PREFIX]) + popdef([NAME]) + popdef([Name]) +]) + +dnl Determine the platform dependent parameters needed to use rpath: +dnl acl_libext, +dnl acl_shlibext, +dnl acl_hardcode_libdir_flag_spec, +dnl acl_hardcode_libdir_separator, +dnl acl_hardcode_direct, +dnl acl_hardcode_minus_L. +AC_DEFUN([AC_LIB_RPATH], +[ + dnl Tell automake >= 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + acl_libext="$acl_cv_libext" + acl_shlibext="$acl_cv_shlibext" + acl_libname_spec="$acl_cv_libname_spec" + acl_library_names_spec="$acl_cv_library_names_spec" + acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + acl_hardcode_direct="$acl_cv_hardcode_direct" + acl_hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE([rpath], + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_FROMPACKAGE(name, package) +dnl declares that libname comes from the given package. The configure file +dnl will then not have a --with-libname-prefix option but a +dnl --with-package-prefix option. Several libraries can come from the same +dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar +dnl macro call that searches for libname. +AC_DEFUN([AC_LIB_FROMPACKAGE], +[ + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + define([acl_frompackage_]NAME, [$2]) + popdef([NAME]) + pushdef([PACK],[$2]) + pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + define([acl_libsinpackage_]PACKUP, + m4_ifdef([acl_libsinpackage_]PACKUP, [acl_libsinpackage_]PACKUP[[, ]],)[lib$1]) + popdef([PACKUP]) + popdef([PACK]) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found +dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, lib[$1])]) + pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, [acl_libsinpackage_]PACKUP, lib[$1])]) + dnl Autoconf >= 2.61 supports dots in --with options. + pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit(PACK,[.],[_])],PACK)]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_ARG_WITH(P_A_C_K[-prefix], +[[ --with-]]P_A_C_K[[-prefix[=DIR] search for ]PACKLIBS[ in DIR/include and DIR/lib + --without-]]P_A_C_K[[-prefix don't search for ]PACKLIBS[ in includedir and libdir]], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + if test "$acl_libdirstem2" != "$acl_libdirstem" \ + && ! test -d "$withval/$acl_libdirstem"; then + additional_libdir="$withval/$acl_libdirstem2" + fi + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + LIB[]NAME[]_PREFIX= + dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been + dnl computed. So it has to be reset here. + HAVE_LIB[]NAME= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + eval libname=\"$acl_libname_spec\" # typically: libname=lib$name + if test -n "$acl_shlibext"; then + shrext=".$acl_shlibext" # typically: shrext=.so + else + shrext= + fi + if test $use_additional = yes; then + dir="$additional_libdir" + dnl The same code as in the loop below: + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + dnl First look for a shared library. + if test -n "$acl_shlibext"; then + if test -f "$dir/$libname$shrext"; then + found_dir="$dir" + found_so="$dir/$libname$shrext" + else + if test "$acl_library_names_spec" = '$libname$shrext$versuffix'; then + ver=`(cd "$dir" && \ + for f in "$libname$shrext".*; do echo "$f"; done \ + | sed -e "s,^$libname$shrext\\\\.,," \ + | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \ + | sed 1q ) 2>/dev/null` + if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; then + found_dir="$dir" + found_so="$dir/$libname$shrext.$ver" + fi + else + eval library_names=\"$acl_library_names_spec\" + for f in $library_names; do + if test -f "$dir/$f"; then + found_dir="$dir" + found_so="$dir/$f" + break + fi + done + fi + fi + fi + dnl Then look for a static library. + if test "X$found_dir" = "X"; then + if test -f "$dir/$libname.$acl_libext"; then + found_dir="$dir" + found_a="$dir/$libname.$acl_libext" + fi + fi + if test "X$found_dir" != "X"; then + if test -f "$dir/$libname.la"; then + found_la="$dir/$libname.la" + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no \ + || test "X$found_dir" = "X/usr/$acl_libdirstem" \ + || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$acl_hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$acl_hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $acl_hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */$acl_libdirstem | */$acl_libdirstem/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + */$acl_libdirstem2 | */$acl_libdirstem2/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem2/"'*$,,'` + if test "$name" = '$1'; then + LIB[]NAME[]_PREFIX="$basedir" + fi + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \ + && test "X$additional_libdir" != "X/usr/$acl_libdirstem2"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem" \ + || test "X$additional_libdir" = "X/usr/local/$acl_libdirstem2"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir" + done + dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi + popdef([P_A_C_K]) + popdef([PACKLIBS]) + popdef([PACKUP]) + popdef([PACK]) + popdef([NAME]) +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +dnl For those cases where a variable contains several -L and -l options +dnl referring to unknown libraries and directories, this macro determines the +dnl necessary additional linker options for the runtime path. +dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL]) +dnl sets LDADDVAR to linker options needed together with LIBSVALUE. +dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed, +dnl otherwise linking without libtool is assumed. +AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], +[ + AC_REQUIRE([AC_LIB_RPATH]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + $1= + if test "$enable_rpath" != no; then + if test -n "$acl_hardcode_libdir_flag_spec" && test "$acl_hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode directories into the resulting + dnl binary. + rpathdirs= + next= + for opt in $2; do + if test -n "$next"; then + dir="$next" + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= + else + case $opt in + -L) next=yes ;; + -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'` + dnl No need to hardcode the standard /usr/lib. + if test "X$dir" != "X/usr/$acl_libdirstem" \ + && test "X$dir" != "X/usr/$acl_libdirstem2"; then + rpathdirs="$rpathdirs $dir" + fi + next= ;; + *) next= ;; + esac + fi + done + if test "X$rpathdirs" != "X"; then + if test -n ""$3""; then + dnl libtool is used for linking. Use -R options. + for dir in $rpathdirs; do + $1="${$1}${$1:+ }-R$dir" + done + else + dnl The linker is used for linking directly. + if test -n "$acl_hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user + dnl must pass all path elements in one option. + alldirs= + for dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="$flag" + else + dnl The -rpath options are cumulative. + for dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$dir" + eval flag=\"$acl_hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + $1="${$1}${$1:+ }$flag" + done + fi + fi + fi + fi + fi + AC_SUBST([$1]) +]) diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4 new file mode 100644 index 0000000..1601cea --- /dev/null +++ b/m4/lib-prefix.m4 @@ -0,0 +1,224 @@ +# lib-prefix.m4 serial 7 (gettext-0.18) +dnl Copyright (C) 2001-2005, 2008-2010 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_MULTILIB]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/$acl_libdirstem" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_PREPARE_MULTILIB creates +dnl - a variable acl_libdirstem, containing the basename of the libdir, either +dnl "lib" or "lib64" or "lib/64", +dnl - a variable acl_libdirstem2, as a secondary possible value for +dnl acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or +dnl "lib/amd64". +AC_DEFUN([AC_LIB_PREPARE_MULTILIB], +[ + dnl There is no formal standard regarding lib and lib64. + dnl On glibc systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine + dnl the compiler's default mode by looking at the compiler's library search + dnl path. If at least one of its elements ends in /lib64 or points to a + dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI. + dnl Otherwise we use the default, namely "lib". + dnl On Solaris systems, the current practice is that on a system supporting + dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under + dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or + dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib. + AC_REQUIRE([AC_CANONICAL_HOST]) + acl_libdirstem=lib + acl_libdirstem2= + case "$host_os" in + solaris*) + dnl See Solaris 10 Software Developer Collection > Solaris 64-bit Developer's Guide > The Development Environment + dnl <http://docs.sun.com/app/docs/doc/816-5138/dev-env?l=en&a=view>. + dnl "Portable Makefiles should refer to any library directories using the 64 symbolic link." + dnl But we want to recognize the sparcv9 or amd64 subdirectory also if the + dnl symlink is missing, so we set acl_libdirstem2 too. + AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit], + [AC_EGREP_CPP([sixtyfour bits], [ +#ifdef _LP64 +sixtyfour bits +#endif + ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no]) + ]) + if test $gl_cv_solaris_64bit = yes; then + acl_libdirstem=lib/64 + case "$host_cpu" in + sparc*) acl_libdirstem2=lib/sparcv9 ;; + i*86 | x86_64) acl_libdirstem2=lib/amd64 ;; + esac + fi + ;; + *) + searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'` + if test -n "$searchpath"; then + acl_save_IFS="${IFS= }"; IFS=":" + for searchdir in $searchpath; do + if test -d "$searchdir"; then + case "$searchdir" in + */lib64/ | */lib64 ) acl_libdirstem=lib64 ;; + */../ | */.. ) + # Better ignore directories of this form. They are misleading. + ;; + *) searchdir=`cd "$searchdir" && pwd` + case "$searchdir" in + */lib64 ) acl_libdirstem=lib64 ;; + esac ;; + esac + fi + done + IFS="$acl_save_IFS" + fi + ;; + esac + test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem" +]) diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..e2ecf77 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,36 @@ +LDADD = $(top_builddir)/htp/libhtp.la -lz @LIBICONV@ + +AM_CFLAGS = -D_GNU_SOURCE -g -Wall -Wextra -std=gnu99 -pedantic \ + -Wextra -Wno-missing-field-initializers -Wshadow -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wno-unused-parameter + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_builddir)/htp -Wno-write-strings -DGTEST_USE_OWN_TR1_TUPLE=1 \ + -D_GNU_SOURCE -g -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare + +AUTOMAKE_OPTIONS = subdir-objects +EXTRA_DIST = files +check_PROGRAMS = test_all test_fuzz +check_LIBRARIES = libgtest.a + +test_all_SOURCES = test_bstr.cpp test_gunzip.cpp test_hybrid.cpp test_main.cpp test_multipart.cpp test.c test.h test_utils.cpp test_bench.cpp +test_all_LDADD = libgtest.a -lpthread $(LDADD) + +test_fuzz_SOURCES = fuzz/onefile.c fuzz/fuzz_htp.c fuzz/fuzz_htp.h test.c +test_fuzz_LDADD = $(LDADD) + +libgtest_a_SOURCES = gtest/gtest-all.cc gtest/gtest_main.cc gtest/gtest.h + +TESTS_ENVIRONMENT = srcdir=$(srcdir)/files +TESTS = test_all + +test: check + @echo + +test-compile-only: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + +check-compile-only: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + diff --git a/test/files/00-adhoc.t b/test/files/00-adhoc.t new file mode 100644 index 0000000..36805de --- /dev/null +++ b/test/files/00-adhoc.t @@ -0,0 +1,14 @@ +>>> +GET /?p=%20 HTTP/1.0 +User-Agent: Mozilla + + +<<< +HTTP/1.0 200 OK +Date: Mon, 31 Aug 2009 20:25:50 GMT +Server: Apache +Connection: close +Content-Type: text/html +Content-Length: 12 + +Hello World!
\ No newline at end of file diff --git a/test/files/01-get.t b/test/files/01-get.t new file mode 100644 index 0000000..e9edceb --- /dev/null +++ b/test/files/01-get.t @@ -0,0 +1,14 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/02-header-test-apache2.t b/test/files/02-header-test-apache2.t Binary files differnew file mode 100644 index 0000000..a7cb787 --- /dev/null +++ b/test/files/02-header-test-apache2.t diff --git a/test/files/03-post-urlencoded.t b/test/files/03-post-urlencoded.t new file mode 100644 index 0000000..052377e --- /dev/null +++ b/test/files/03-post-urlencoded.t @@ -0,0 +1,34 @@ +>>>
+POST /?qsp1=1&%20p%20q=2&u=Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_ HTTP/1.0
+Content-Length: 12
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+p=0123456789
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
+>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+9
+012345678
+1
+9
+0
+
diff --git a/test/files/04-post-urlencoded-chunked.t b/test/files/04-post-urlencoded-chunked.t new file mode 100644 index 0000000..1d72e71 --- /dev/null +++ b/test/files/04-post-urlencoded-chunked.t @@ -0,0 +1,26 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+b
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/05-expect.t b/test/files/05-expect.t new file mode 100644 index 0000000..0ad3090 --- /dev/null +++ b/test/files/05-expect.t @@ -0,0 +1,39 @@ +>>> +POST / HTTP/1.1 +User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.8 libssh2/0.18 +Accept: */* +Content-Length: 216 +Expect: 100-continue +Content-Type: multipart/form-data; boundary=----------------------------07869933ca1b + + +<<< +HTTP/1.1 100 Continue +Header1: This +Header2: That + + +>>> +------------------------------07869933ca1b +Content-Disposition: form-data; name="file"; filename="404.php" +Content-Type: application/octet-stream + + +>>> +<? echo "404"; ?> +>>> + +------------------------------07869933ca1b-- + +<<< +HTTP/1.1 200 OK +Date: Tue, 03 Nov 2009 09:27:47 GMT +Server: Apache +Last-Modified: Thu, 30 Apr 2009 12:20:49 GMT +ETag: "2dcada-2d-468c4b9ec6a40" +Accept-Ranges: bytes +Content-Length: 45 +Vary: Accept-Encoding +Content-Type: text/html + +<html><body><h1>It works!</h1></body></html> diff --git a/test/files/06-uri-normal.t b/test/files/06-uri-normal.t new file mode 100644 index 0000000..78a138c --- /dev/null +++ b/test/files/06-uri-normal.t @@ -0,0 +1,9 @@ +>>>
+GET http://username:password@www.example.com:8080/sub/folder/file.jsp?p=q#f HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/07-pipelined-connection.t b/test/files/07-pipelined-connection.t new file mode 100644 index 0000000..07ef603 --- /dev/null +++ b/test/files/07-pipelined-connection.t @@ -0,0 +1,15 @@ +>>>
+GET /first HTTP/1.1
+
+GET /second HTTP/1.1
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/08-not-pipelined-connection.t b/test/files/08-not-pipelined-connection.t new file mode 100644 index 0000000..2a1bac3 --- /dev/null +++ b/test/files/08-not-pipelined-connection.t @@ -0,0 +1,18 @@ +>>>
+GET /first HTTP/1.1
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
+>>>
+GET /second HTTP/1.1
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/09-multi-packet-request-head.t b/test/files/09-multi-packet-request-head.t new file mode 100644 index 0000000..08a16f2 --- /dev/null +++ b/test/files/09-multi-packet-request-head.t @@ -0,0 +1,14 @@ +>>>
+GET / HTTP/1.0
+
+>>>
+Host: www.example.com
+
+>>>
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/10-host-in-headers.t b/test/files/10-host-in-headers.t new file mode 100644 index 0000000..b892b0a --- /dev/null +++ b/test/files/10-host-in-headers.t @@ -0,0 +1,34 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
+>>>
+GET / HTTP/1.1
+Host: www.example.com.
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+>>>
+GET / HTTP/1.1
+Host: WwW.ExamPle.cOm
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+>>>
+GET / HTTP/1.1
+Host: www.example.com:80
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
\ No newline at end of file diff --git a/test/files/100-auth-bearer.t b/test/files/100-auth-bearer.t new file mode 100644 index 0000000..4c1456a --- /dev/null +++ b/test/files/100-auth-bearer.t @@ -0,0 +1,5 @@ +>>> +GET / HTTP/1.1 +Host: www.example.com +Authorization: Bearer mF_9.B5f-4.1JqM + diff --git a/test/files/100-response-body-data.t b/test/files/100-response-body-data.t new file mode 100644 index 0000000..020bebd --- /dev/null +++ b/test/files/100-response-body-data.t @@ -0,0 +1,6 @@ +<<< +1 +2 +<<< +3 +4
\ No newline at end of file diff --git a/test/files/11-response-stream-closure.t b/test/files/11-response-stream-closure.t new file mode 100644 index 0000000..8bc167c --- /dev/null +++ b/test/files/11-response-stream-closure.t @@ -0,0 +1,13 @@ +>>>
+GET / HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+
+Hello World!
\ No newline at end of file diff --git a/test/files/12-connect-request.t b/test/files/12-connect-request.t new file mode 100644 index 0000000..89faf8e --- /dev/null +++ b/test/files/12-connect-request.t @@ -0,0 +1,21 @@ +>>>
+CONNECT www.ssllabs.com:443 HTTP/1.0
+
+
+<<<
+HTTP/1.1 405 Method Not Allowed
+Date: Sat, 12 Dec 2009 05:08:45 GMT
+Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8g PHP/5.3.0
+Allow: GET,HEAD,POST,OPTIONS,TRACE
+Vary: Accept-Encoding
+Content-Length: 230
+Connection: close
+Content-Type: text/html; charset=iso-8859-1
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>405 Method Not Allowed</title>
+</head><body>
+<h1>Method Not Allowed</h1>
+<p>The requested method CONNECT is not allowed for the URL /.</p>
+</body></html>
\ No newline at end of file diff --git a/test/files/13-compressed-response-gzip-ct.t b/test/files/13-compressed-response-gzip-ct.t Binary files differnew file mode 100644 index 0000000..d5a2e31 --- /dev/null +++ b/test/files/13-compressed-response-gzip-ct.t diff --git a/test/files/14-compressed-response-gzip-chunked.t b/test/files/14-compressed-response-gzip-chunked.t Binary files differnew file mode 100644 index 0000000..bae8a2d --- /dev/null +++ b/test/files/14-compressed-response-gzip-chunked.t diff --git a/test/files/15-connect-complete.t b/test/files/15-connect-complete.t Binary files differnew file mode 100644 index 0000000..071d064 --- /dev/null +++ b/test/files/15-connect-complete.t diff --git a/test/files/16-connect-extra.t b/test/files/16-connect-extra.t new file mode 100644 index 0000000..9c08f17 --- /dev/null +++ b/test/files/16-connect-extra.t @@ -0,0 +1,32 @@ +>>>
+CONNECT www.feistyduck.com:80 HTTP/1.1
+Host: www.feistyduck.com
+
+HEAD / HTTP/1.0
+
+
+<<<
+HTTP/1.1 301 Moved Permanently
+Date: Wed, 06 Jan 2010 17:41:34 GMT
+Server: Apache
+Location: https://www.feistyduck.com/
+Vary: Accept-Encoding
+Content-Length: 235
+Content-Type: text/html; charset=iso-8859-1
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> +<html><head> +<title>301 Moved Permanently</title> +</head><body> +<h1>Moved Permanently</h1> +<p>The document has moved <a href="https://www.feistyduck.com/">here</a>.</p> +</body></html> +
+HTTP/1.1 301 Moved Permanently
+Date: Wed, 06 Jan 2010 17:41:46 GMT
+Server: Apache
+Location: https://www.feistyduck.com/
+Vary: Accept-Encoding
+Connection: close
+Content-Type: text/html; charset=iso-8859-1
+
diff --git a/test/files/17-multipart-1.t b/test/files/17-multipart-1.t new file mode 100644 index 0000000..7c083c6 --- /dev/null +++ b/test/files/17-multipart-1.t @@ -0,0 +1,41 @@ +>>>
+POST /upload.php?qsp1=1&%20p%20q=2 HTTP/1.1
+Host: 192.168.3.100:8080
+User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)
+Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+Accept-Language: en-us,en;q=0.5
+Accept-Encoding: gzip,deflate
+Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+Keep-Alive: 300
+Connection: keep-alive
+Content-Type: multipart/form-data; boundary=---------------------------41184676334
+Content-Length: 610
+
+-----------------------------41184676334
+Content-Disposition: form-data; name="field1"
+
+0123456789
+-----------------------------41184676334
+Content-Disposition: form-data; name="field2"
+
+9876543210
+-----------------------------41184676334
+Content-Disposition: form-data; name="file1"; filename="New Text Document.txt"
+Content-Type: text/plain
+
+FFFFFFFFFFFFFFFFFFFFFFFFFFFF
+-----------------------------41184676334
+Content-Disposition: form-data; name="file2"; filename="New Text Document.txt"
+Content-Type: text/plain
+
+FFFFFFFFFFFFFFFFFFFFFFFFFFFF
+-----------------------------41184676334--
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+
+Hello World!
\ No newline at end of file diff --git a/test/files/18-compressed-response-deflate.t b/test/files/18-compressed-response-deflate.t Binary files differnew file mode 100644 index 0000000..b70940e --- /dev/null +++ b/test/files/18-compressed-response-deflate.t diff --git a/test/files/19-urlencoded-test.t b/test/files/19-urlencoded-test.t new file mode 100644 index 0000000..21d7f27 --- /dev/null +++ b/test/files/19-urlencoded-test.t @@ -0,0 +1,15 @@ +>>>
+POST /?p=1&q=2 HTTP/1.0
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 11
+
+p=3&q=4&z=5
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/20-ambiguous-host.t b/test/files/20-ambiguous-host.t new file mode 100644 index 0000000..bb3e551 --- /dev/null +++ b/test/files/20-ambiguous-host.t @@ -0,0 +1,58 @@ +>>>
+GET http://example.com/1 HTTP/1.1
+Host: example.com
+
+
+<<<
+HTTP/1.1 200 OK
+Date: Mon, 26 Apr 2010 13:56:31 GMT
+Content-Length: 8
+
+12345678
+>>>
+GET http://example.com/2 HTTP/1.1
+Host: foo.com
+
+
+<<<
+HTTP/1.1 200 OK
+Date: Mon, 26 Apr 2010 13:56:31 GMT
+Content-Length: 8
+
+12345678
+>>>
+POST http://www.example.com:8001/3 HTTP/1.1
+Host: www.example.com:8001
+Content-Length: 8
+
+12345678
+<<<
+HTTP/1.1 200 OK
+Date: Mon, 26 Apr 2010 13:56:31 GMT
+Content-Length: 8
+
+12345678
+>>>
+POST http://www.example.com:8002/4 HTTP/1.1
+Host: www.example.com:8003
+Content-Length: 8
+
+12345678
+<<<
+HTTP/1.1 200 OK
+Date: Mon, 26 Apr 2010 13:56:31 GMT
+Content-Length: 8
+
+12345678
+>>>
+POST http://www.example.com:80/5 HTTP/1.1
+Host: www.example.com
+Content-Length: 8
+
+12345678
+<<<
+HTTP/1.1 200 OK
+Date: Mon, 26 Apr 2010 13:56:31 GMT
+Content-Length: 8
+
+12345678
\ No newline at end of file diff --git a/test/files/21-http09.t b/test/files/21-http09.t new file mode 100644 index 0000000..5359a7f --- /dev/null +++ b/test/files/21-http09.t @@ -0,0 +1,11 @@ +>>>
+GET /?foo=bar
+
+<<<
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>301 Moved Permanently</title>
+</head><body>
+<h1>Moved Permanently</h1>
+<p>The document has moved.</p>
+</body></html>
diff --git a/test/files/22-http_1_1-host_missing b/test/files/22-http_1_1-host_missing new file mode 100644 index 0000000..53ca3e8 --- /dev/null +++ b/test/files/22-http_1_1-host_missing @@ -0,0 +1,14 @@ +>>>
+GET /?p=%20 HTTP/1.1
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/22-php-param-processing.t b/test/files/22-php-param-processing.t new file mode 100644 index 0000000..772a6d1 --- /dev/null +++ b/test/files/22-php-param-processing.t @@ -0,0 +1,14 @@ +>>>
+GET /?%20p%20q%20=1&q=2&z%20w=3 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/23-http09-multiple.t b/test/files/23-http09-multiple.t new file mode 100644 index 0000000..5fcb04b --- /dev/null +++ b/test/files/23-http09-multiple.t @@ -0,0 +1,12 @@ +>>>
+GET /?foo=bar
+GET /?foo=bar
+
+<<<
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>301 Moved Permanently</title>
+</head><body>
+<h1>Moved Permanently</h1>
+<p>The document has moved.</p>
+</body></html>
diff --git a/test/files/24-http09-explicit.t b/test/files/24-http09-explicit.t new file mode 100644 index 0000000..0198991 --- /dev/null +++ b/test/files/24-http09-explicit.t @@ -0,0 +1,13 @@ +>>>
+GET /?foo=bar HTTP/0.9
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/25-small-chunks.t b/test/files/25-small-chunks.t new file mode 100644 index 0000000..fdbfc9e --- /dev/null +++ b/test/files/25-small-chunks.t @@ -0,0 +1,41 @@ +>>> +GET +>>> + /?x=y +>>> + HTTP/1.0 +User-Agent: +>>> + Test +>>> + User +>>> + Agent +Host: www.example.com + + +<<< +HTTP/1.0 +<<< + 200 +<<< + OK +Date: +<<< + Mon, +<<< + 31 +<<< + Aug +<<< + 2009 +<<< + 20:25:50 +<<< + GMT +Server: Apache +Connection: close +Content-Type: text/html +Content-Length: 12 + +Hello World!
\ No newline at end of file diff --git a/test/files/26-request-headers-raw.t b/test/files/26-request-headers-raw.t new file mode 100644 index 0000000..fdbfc9e --- /dev/null +++ b/test/files/26-request-headers-raw.t @@ -0,0 +1,41 @@ +>>> +GET +>>> + /?x=y +>>> + HTTP/1.0 +User-Agent: +>>> + Test +>>> + User +>>> + Agent +Host: www.example.com + + +<<< +HTTP/1.0 +<<< + 200 +<<< + OK +Date: +<<< + Mon, +<<< + 31 +<<< + Aug +<<< + 2009 +<<< + 20:25:50 +<<< + GMT +Server: Apache +Connection: close +Content-Type: text/html +Content-Length: 12 + +Hello World!
\ No newline at end of file diff --git a/test/files/27-request-trailer-raw.t b/test/files/27-request-trailer-raw.t new file mode 100644 index 0000000..1d72e71 --- /dev/null +++ b/test/files/27-request-trailer-raw.t @@ -0,0 +1,26 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+b
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/28-response-headers-raw.t b/test/files/28-response-headers-raw.t new file mode 100644 index 0000000..db1e07f --- /dev/null +++ b/test/files/28-response-headers-raw.t @@ -0,0 +1,33 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date:
+<<<
+ Mon,
+<<<
+ 31 Aug 2009 20:25:50 GMT
+Server:
+<<<
+ Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+b
+Hello World
+1
+!
+0
+Set-Cookie:
+<<<
+ name=
+<<<
+value
+Another-Header:
+<<<
+ Header-Value
+
diff --git a/test/files/29-response-trailer-raw.t b/test/files/29-response-trailer-raw.t new file mode 100644 index 0000000..db1e07f --- /dev/null +++ b/test/files/29-response-trailer-raw.t @@ -0,0 +1,33 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date:
+<<<
+ Mon,
+<<<
+ 31 Aug 2009 20:25:50 GMT
+Server:
+<<<
+ Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+b
+Hello World
+1
+!
+0
+Set-Cookie:
+<<<
+ name=
+<<<
+value
+Another-Header:
+<<<
+ Header-Value
+
diff --git a/test/files/30-get-ipv6.t b/test/files/30-get-ipv6.t new file mode 100644 index 0000000..baf3920 --- /dev/null +++ b/test/files/30-get-ipv6.t @@ -0,0 +1,15 @@ +>>>
+GET http://[::1]:8080/?p=%20 HTTP/1.0
+Host: [::1]:8080
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/31-get-request-line-nul.t b/test/files/31-get-request-line-nul.t Binary files differnew file mode 100644 index 0000000..3de2eb4 --- /dev/null +++ b/test/files/31-get-request-line-nul.t diff --git a/test/files/32-invalid-hostname.t b/test/files/32-invalid-hostname.t new file mode 100644 index 0000000..a034785 --- /dev/null +++ b/test/files/32-invalid-hostname.t @@ -0,0 +1,15 @@ +>>>
+GET http://www..example.com/?p=%20 HTTP/1.0
+Host: www example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/33-invalid-hostname.t b/test/files/33-invalid-hostname.t new file mode 100644 index 0000000..ad18d33 --- /dev/null +++ b/test/files/33-invalid-hostname.t @@ -0,0 +1,15 @@ +>>>
+GET http://www.example.com:XXX/?p=%20 HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/34-invalid-hostname.t b/test/files/34-invalid-hostname.t new file mode 100644 index 0000000..e886ebf --- /dev/null +++ b/test/files/34-invalid-hostname.t @@ -0,0 +1,15 @@ +>>>
+GET /?p=%20 HTTP/1.0
+Host: www.example.com:
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/35-early-response.t b/test/files/35-early-response.t new file mode 100644 index 0000000..4b205db --- /dev/null +++ b/test/files/35-early-response.t @@ -0,0 +1,18 @@ +>>>
+POST / HTTP/1.0
+Content-Length: 12
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 400 Bad Request
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 11
+
+Bad Request
+>>>
+p=0123456789
\ No newline at end of file diff --git a/test/files/36-invalid-request-1-invalid-c-l.t b/test/files/36-invalid-request-1-invalid-c-l.t new file mode 100644 index 0000000..42980f4 --- /dev/null +++ b/test/files/36-invalid-request-1-invalid-c-l.t @@ -0,0 +1,17 @@ +>>>
+POST / HTTP/1.0
+Host: www.example.com
+Content-Length: ABC
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+p=0123456789
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/37-invalid-request-2-t-e-and-c-l.t b/test/files/37-invalid-request-2-t-e-and-c-l.t new file mode 100644 index 0000000..8edab9c --- /dev/null +++ b/test/files/37-invalid-request-2-t-e-and-c-l.t @@ -0,0 +1,28 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Length: 12
+Host: www.example.com
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+b
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/38-invalid-request-3-invalid-t-e.t b/test/files/38-invalid-request-3-invalid-t-e.t new file mode 100644 index 0000000..ada8dd5 --- /dev/null +++ b/test/files/38-invalid-request-3-invalid-t-e.t @@ -0,0 +1,27 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: ABC
+Host: www.example.com
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+b
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/39-auto-destroy-crash.t b/test/files/39-auto-destroy-crash.t new file mode 100644 index 0000000..b892b0a --- /dev/null +++ b/test/files/39-auto-destroy-crash.t @@ -0,0 +1,34 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
+>>>
+GET / HTTP/1.1
+Host: www.example.com.
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+>>>
+GET / HTTP/1.1
+Host: WwW.ExamPle.cOm
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+>>>
+GET / HTTP/1.1
+Host: www.example.com:80
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
\ No newline at end of file diff --git a/test/files/40-auth-basic.t b/test/files/40-auth-basic.t new file mode 100644 index 0000000..8606001 --- /dev/null +++ b/test/files/40-auth-basic.t @@ -0,0 +1,5 @@ +>>>
+GET / HTTP/1.0
+Host: www.example.com
+Authorization: Basic aXZhbnI6c2VjcmV0
+
diff --git a/test/files/41-auth-digest.t b/test/files/41-auth-digest.t new file mode 100644 index 0000000..53065b0 --- /dev/null +++ b/test/files/41-auth-digest.t @@ -0,0 +1,8 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+Authorization: Digest username="ivanr", realm="Book Review",
+ nonce="OgmPjb/jAwA=7c5a49c2ed9416dba1b04b5307d6d935f74a859d",
+ uri="/review/", algorithm=MD5, response="3c430d26043cc306e0282635929d57cb",
+ qop=auth, nc=00000004, cnonce="c3bcee9534c051a0"
+
diff --git a/test/files/42-unknown-method_only.t b/test/files/42-unknown-method_only.t new file mode 100644 index 0000000..8c9a603 --- /dev/null +++ b/test/files/42-unknown-method_only.t @@ -0,0 +1,3 @@ +>>>
+HELLO
+
diff --git a/test/files/43-invalid-protocol.t b/test/files/43-invalid-protocol.t new file mode 100644 index 0000000..0e4c0ad --- /dev/null +++ b/test/files/43-invalid-protocol.t @@ -0,0 +1,3 @@ +>>>
+GET / JUNK/1.0
+
diff --git a/test/files/44-auth-basic-invalid.t b/test/files/44-auth-basic-invalid.t new file mode 100644 index 0000000..ed44445 --- /dev/null +++ b/test/files/44-auth-basic-invalid.t @@ -0,0 +1,5 @@ +>>>
+GET / HTTP/1.0
+Host: www.example.com
+Authorization: Basic notBase64:EncodedStuff
+
diff --git a/test/files/45-auth-digest-unquoted-username.t b/test/files/45-auth-digest-unquoted-username.t new file mode 100644 index 0000000..855e00e --- /dev/null +++ b/test/files/45-auth-digest-unquoted-username.t @@ -0,0 +1,8 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+Authorization: Digest username=ivanr, realm="Book Review",
+ nonce="OgmPjb/jAwA=7c5a49c2ed9416dba1b04b5307d6d935f74a859d",
+ uri="/review/", algorithm=MD5, response="3c430d26043cc306e0282635929d57cb",
+ qop=auth, nc=00000004, cnonce="c3bcee9534c051a0"
+
diff --git a/test/files/46-auth-digest-invalid-username.t b/test/files/46-auth-digest-invalid-username.t new file mode 100644 index 0000000..dbd1c43 --- /dev/null +++ b/test/files/46-auth-digest-invalid-username.t @@ -0,0 +1,8 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+Authorization: Digest username = ivanr, realm="Book Review",
+ nonce="OgmPjb/jAwA=7c5a49c2ed9416dba1b04b5307d6d935f74a859d",
+ uri="/review/", algorithm=MD5, response="3c430d26043cc306e0282635929d57cb",
+ qop=auth, nc=00000004, cnonce="c3bcee9534c051a0"
+
diff --git a/test/files/47-auth-unrecognized.t b/test/files/47-auth-unrecognized.t new file mode 100644 index 0000000..5d51455 --- /dev/null +++ b/test/files/47-auth-unrecognized.t @@ -0,0 +1,5 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+Authorization: Turbo customAuthDataHere
+
diff --git a/test/files/48-invalid-response-headers-1.t b/test/files/48-invalid-response-headers-1.t new file mode 100644 index 0000000..d10582a --- /dev/null +++ b/test/files/48-invalid-response-headers-1.t @@ -0,0 +1,17 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+No Colon
+Lws : After Header Name
+Header@Name: Not Token
+
+Hello World!
\ No newline at end of file diff --git a/test/files/49-invalid-response-headers-2.t b/test/files/49-invalid-response-headers-2.t new file mode 100644 index 0000000..16970f9 --- /dev/null +++ b/test/files/49-invalid-response-headers-2.t @@ -0,0 +1,15 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+: Empty Name
+
+Hello World!
\ No newline at end of file diff --git a/test/files/50-util.t b/test/files/50-util.t new file mode 100644 index 0000000..e9edceb --- /dev/null +++ b/test/files/50-util.t @@ -0,0 +1,14 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/51-get-ipv6-invalid.t b/test/files/51-get-ipv6-invalid.t new file mode 100644 index 0000000..bde929c --- /dev/null +++ b/test/files/51-get-ipv6-invalid.t @@ -0,0 +1,15 @@ +>>>
+GET http://[::1:8080/?p=%20 HTTP/1.0
+Host: [::1]:8080
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/52-invalid-path.t b/test/files/52-invalid-path.t new file mode 100644 index 0000000..97528e7 --- /dev/null +++ b/test/files/52-invalid-path.t @@ -0,0 +1,15 @@ +>>>
+GET invalid/path?p=%20 HTTP/1.0
+Host: [::1]:8080
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/53-path-utf8-none.t b/test/files/53-path-utf8-none.t new file mode 100644 index 0000000..9234cd9 --- /dev/null +++ b/test/files/53-path-utf8-none.t @@ -0,0 +1,15 @@ +>>>
+GET /Ristic.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/54-path-utf8-valid.t b/test/files/54-path-utf8-valid.t new file mode 100644 index 0000000..518918e --- /dev/null +++ b/test/files/54-path-utf8-valid.t @@ -0,0 +1,15 @@ +>>>
+GET /Risti%C4%87.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/55-path-utf8-overlong-2.t b/test/files/55-path-utf8-overlong-2.t new file mode 100644 index 0000000..f78a088 --- /dev/null +++ b/test/files/55-path-utf8-overlong-2.t @@ -0,0 +1,15 @@ +>>>
+GET /%c0%a6.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/56-path-utf8-overlong-3.t b/test/files/56-path-utf8-overlong-3.t new file mode 100644 index 0000000..3184dc8 --- /dev/null +++ b/test/files/56-path-utf8-overlong-3.t @@ -0,0 +1,15 @@ +>>>
+GET /%e0%80%a6.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/57-path-utf8-overlong-4.t b/test/files/57-path-utf8-overlong-4.t new file mode 100644 index 0000000..cfccdbe --- /dev/null +++ b/test/files/57-path-utf8-overlong-4.t @@ -0,0 +1,15 @@ +>>>
+GET /%f0%80%80%a6.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/58-path-utf8-invalid.t b/test/files/58-path-utf8-invalid.t new file mode 100644 index 0000000..f3d5803 --- /dev/null +++ b/test/files/58-path-utf8-invalid.t @@ -0,0 +1,15 @@ +>>>
+GET /Risti%C4%87%80.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/59-path-utf8-fullwidth.t b/test/files/59-path-utf8-fullwidth.t new file mode 100644 index 0000000..4321652 --- /dev/null +++ b/test/files/59-path-utf8-fullwidth.t @@ -0,0 +1,15 @@ +>>>
+GET /%EF%BC%86.txt HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/60-request-cookies.t b/test/files/60-request-cookies.t new file mode 100644 index 0000000..51aca6f --- /dev/null +++ b/test/files/60-request-cookies.t @@ -0,0 +1,16 @@ +>>>
+GET / HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+Cookie: =0; p=1; q=2; =; z=
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/61-empty-line-between-requests.t b/test/files/61-empty-line-between-requests.t new file mode 100644 index 0000000..47a8c21 --- /dev/null +++ b/test/files/61-empty-line-between-requests.t @@ -0,0 +1,19 @@ +>>>
+GET /first HTTP/1.1
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
+>>>
+
+GET /second HTTP/1.1
+
+
+<<<
+HTTP/1.0 200 OK
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/62-post-no-body.t b/test/files/62-post-no-body.t new file mode 100644 index 0000000..10a8d4b --- /dev/null +++ b/test/files/62-post-no-body.t @@ -0,0 +1,34 @@ +>>>
+POST / HTTP/1.0
+Content-Length: 0
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
+>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+9
+012345678
+1
+9
+0
+
diff --git a/test/files/63-post-chunked-invalid-1.t b/test/files/63-post-chunked-invalid-1.t new file mode 100644 index 0000000..eb5ef0c --- /dev/null +++ b/test/files/63-post-chunked-invalid-1.t @@ -0,0 +1,26 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+80000000
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/64-post-chunked-invalid-2.t b/test/files/64-post-chunked-invalid-2.t new file mode 100644 index 0000000..f5fc91d --- /dev/null +++ b/test/files/64-post-chunked-invalid-2.t @@ -0,0 +1,26 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+-1
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/65-post-chunked-invalid-3.t b/test/files/65-post-chunked-invalid-3.t new file mode 100644 index 0000000..4076e0b --- /dev/null +++ b/test/files/65-post-chunked-invalid-3.t @@ -0,0 +1,26 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+
+p=012345678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/66-post-chunked-split-chunk.t b/test/files/66-post-chunked-split-chunk.t new file mode 100644 index 0000000..6f5dd48 --- /dev/null +++ b/test/files/66-post-chunked-split-chunk.t @@ -0,0 +1,28 @@ +>>>
+POST / HTTP/1.1
+Transfer-Encoding: chunked
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+Cookie: 1
+
+b
+p=01234
+>>>
+5678
+1
+9
+0
+Cookie:
+>>>
+ 2
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/67-long-request-line.t b/test/files/67-long-request-line.t new file mode 100644 index 0000000..fa3f984 --- /dev/null +++ b/test/files/67-long-request-line.t @@ -0,0 +1,16 @@ +>>>
+GET /0123456789/
+>>>
+0123456789/ HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/68-invalid-request-header.t b/test/files/68-invalid-request-header.t Binary files differnew file mode 100644 index 0000000..4e6d688 --- /dev/null +++ b/test/files/68-invalid-request-header.t diff --git a/test/files/69-long-response-header.t b/test/files/69-long-response-header.t new file mode 100644 index 0000000..822d3ca --- /dev/null +++ b/test/files/69-long-response-header.t @@ -0,0 +1,16 @@ +>>>
+GET / HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache Apache Apache Apache
+<<<
+Apache Apache Apache Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/70-response-invalid-chunk-length.t b/test/files/70-response-invalid-chunk-length.t new file mode 100644 index 0000000..68b43e3 --- /dev/null +++ b/test/files/70-response-invalid-chunk-length.t @@ -0,0 +1,18 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+g
+012345678
+1
+9
+0
+
diff --git a/test/files/71-response-split-chunk.t b/test/files/71-response-split-chunk.t new file mode 100644 index 0000000..1d3f091 --- /dev/null +++ b/test/files/71-response-split-chunk.t @@ -0,0 +1,20 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+9
+01234
+<<<
+5678
+1
+9
+0
+
diff --git a/test/files/72-response-split-body.t b/test/files/72-response-split-body.t new file mode 100644 index 0000000..db5ab9f --- /dev/null +++ b/test/files/72-response-split-body.t @@ -0,0 +1,16 @@ +>>>
+GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello
+<<<
+ World!
\ No newline at end of file diff --git a/test/files/73-response-te-and-cl.t b/test/files/73-response-te-and-cl.t new file mode 100644 index 0000000..46c646d --- /dev/null +++ b/test/files/73-response-te-and-cl.t @@ -0,0 +1,19 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 10
+Transfer-Encoding: chunked
+
+9
+012345678
+1
+9
+0
+
diff --git a/test/files/74-response-multiple-cl.t b/test/files/74-response-multiple-cl.t new file mode 100644 index 0000000..556fb8f --- /dev/null +++ b/test/files/74-response-multiple-cl.t @@ -0,0 +1,14 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/75-response-invalid-cl.t b/test/files/75-response-invalid-cl.t new file mode 100644 index 0000000..8743d88 --- /dev/null +++ b/test/files/75-response-invalid-cl.t @@ -0,0 +1,13 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: -1
+
+Hello World!
\ No newline at end of file diff --git a/test/files/76-response-no-body.t b/test/files/76-response-no-body.t new file mode 100644 index 0000000..831571c --- /dev/null +++ b/test/files/76-response-no-body.t @@ -0,0 +1,34 @@ +>>>
+POST /?qsp1=1&%20p%20q=2&u=Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_Ivan+Risti%C4%87_ HTTP/1.0
+Content-Length: 12
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+p=0123456789
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 0
+
+
+>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+9
+012345678
+1
+9
+0
+
diff --git a/test/files/77-response-folded-headers.t b/test/files/77-response-folded-headers.t new file mode 100644 index 0000000..dd33c07 --- /dev/null +++ b/test/files/77-response-folded-headers.t @@ -0,0 +1,35 @@ +>>>
+POST / HTTP/1.0
+Content-Length: 12
+Content-Type: application/x-www-form-urlencoded
+User-Agent: Mozilla
+
+p=0123456789
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+ Server
+Connection: close
+Content-Type: text/html
+Content-Length: 0
+
+
+>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apach2
+Connection: close
+Content-Type: text/html
+Transfer-Encoding: chunked
+
+9
+012345678
+1
+9
+0
+
diff --git a/test/files/78-response-no-status-headers.t b/test/files/78-response-no-status-headers.t new file mode 100644 index 0000000..82e8d2a --- /dev/null +++ b/test/files/78-response-no-status-headers.t @@ -0,0 +1,8 @@ +>>>
+GET / HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+Hello
+World!
\ No newline at end of file diff --git a/test/files/79-connect-invalid-hostport.t b/test/files/79-connect-invalid-hostport.t new file mode 100644 index 0000000..9258b7b --- /dev/null +++ b/test/files/79-connect-invalid-hostport.t @@ -0,0 +1,32 @@ +>>>
+CONNECT [:80 HTTP/1.1
+Host: www.feistyduck.com
+
+HEAD / HTTP/1.0
+
+
+<<<
+HTTP/1.1 301 Moved Permanently
+Date: Wed, 06 Jan 2010 17:41:34 GMT
+Server: Apache
+Location: https://www.feistyduck.com/
+Vary: Accept-Encoding
+Content-Length: 235
+Content-Type: text/html; charset=iso-8859-1
+
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>301 Moved Permanently</title>
+</head><body>
+<h1>Moved Permanently</h1>
+<p>The document has moved <a href="https://www.feistyduck.com/">here</a>.</p>
+</body></html>
+
+HTTP/1.1 301 Moved Permanently
+Date: Wed, 06 Jan 2010 17:41:46 GMT
+Server: Apache
+Location: https://www.feistyduck.com/
+Vary: Accept-Encoding
+Connection: close
+Content-Type: text/html; charset=iso-8859-1
+
diff --git a/test/files/80-hostname-invalid-1.t b/test/files/80-hostname-invalid-1.t new file mode 100644 index 0000000..f5e28c1 --- /dev/null +++ b/test/files/80-hostname-invalid-1.t @@ -0,0 +1,15 @@ +>>>
+GET http://www.example.com/?p=%20 HTTP/1.0
+Host: [:80
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/81-hostname-invalid-2.t b/test/files/81-hostname-invalid-2.t new file mode 100644 index 0000000..d3065c9 --- /dev/null +++ b/test/files/81-hostname-invalid-2.t @@ -0,0 +1,15 @@ +>>>
+GET http://[:80/?p=%20 HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/82-put.t b/test/files/82-put.t new file mode 100644 index 0000000..9931462 --- /dev/null +++ b/test/files/82-put.t @@ -0,0 +1,16 @@ +>>>
+PUT / HTTP/1.0
+Host: www.example.com
+User-Agent: Mozilla
+Content-Length: 12
+
+Hello World!
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/83-auth-digest-invalid-username-2.t b/test/files/83-auth-digest-invalid-username-2.t new file mode 100644 index 0000000..2344a40 --- /dev/null +++ b/test/files/83-auth-digest-invalid-username-2.t @@ -0,0 +1,5 @@ +>>>
+GET / HTTP/1.1
+Host: www.example.com
+Authorization: Digest username="ivanr
+
diff --git a/test/files/84-response-no-status-headers-2.t b/test/files/84-response-no-status-headers-2.t new file mode 100644 index 0000000..239e08a --- /dev/null +++ b/test/files/84-response-no-status-headers-2.t @@ -0,0 +1,7 @@ +>>>
+GET / HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+Hello World!
\ No newline at end of file diff --git a/test/files/85-zero-byte-request-timeout.t b/test/files/85-zero-byte-request-timeout.t new file mode 100644 index 0000000..0cc0e09 --- /dev/null +++ b/test/files/85-zero-byte-request-timeout.t @@ -0,0 +1,16 @@ +<<< +HTTP/1.0 408 Request Time-out +Server: AkamaiGHost +Mime-Version: 1.0 +Date: Fri, 27 Sep 2013 16:37:37 GMT +Content-Type: text/html +Content-Length: 218 +Expires: Fri, 27 Sep 2013 16:37:37 GMT + +<HTML><HEAD> +<TITLE>Request Timeout</TITLE> +</HEAD><BODY> +<H1>Request Timeout</H1> +The server timed out while waiting for the browser's request.<P> +Reference #2.9efcd4d9.1380708056.0 +</BODY></HTML> diff --git a/test/files/86-partial-request-timeout.t b/test/files/86-partial-request-timeout.t new file mode 100644 index 0000000..97dc4bb --- /dev/null +++ b/test/files/86-partial-request-timeout.t @@ -0,0 +1,18 @@ +>>> +GET +<<< +HTTP/1.0 408 Request Time-out +Server: AkamaiGHost +Mime-Version: 1.0 +Date: Fri, 27 Sep 2013 16:37:37 GMT +Content-Type: text/html +Content-Length: 218 +Expires: Fri, 27 Sep 2013 16:37:37 GMT + +<HTML><HEAD> +<TITLE>Request Timeout</TITLE> +</HEAD><BODY> +<H1>Request Timeout</H1> +The server timed out while waiting for the browser's request.<P> +Reference #2.9efcd4d9.1380708056.0 +</BODY></HTML> diff --git a/test/files/87-issue-55-incorrect-host-ambiguous-warning.t b/test/files/87-issue-55-incorrect-host-ambiguous-warning.t new file mode 100644 index 0000000..463ce5d --- /dev/null +++ b/test/files/87-issue-55-incorrect-host-ambiguous-warning.t @@ -0,0 +1,8 @@ +>>>
+CONNECT www.example.com:443 HTTP/1.1
+Host: www.example.com:443
+Accept: */*
+Content-Type: text/html
+Proxy-Connection: Keep-Alive
+Content-length: 0
+
diff --git a/test/files/88-response-multiple-cl-mismatch.t b/test/files/88-response-multiple-cl-mismatch.t new file mode 100644 index 0000000..a1c17c8 --- /dev/null +++ b/test/files/88-response-multiple-cl-mismatch.t @@ -0,0 +1,14 @@ +>>>
+GET / HTTP/1.0
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+Content-Length: 11
+
+Hello World!
\ No newline at end of file diff --git a/test/files/89-get-whitespace.t b/test/files/89-get-whitespace.t new file mode 100644 index 0000000..0bb5b2d --- /dev/null +++ b/test/files/89-get-whitespace.t @@ -0,0 +1,14 @@ +>>>
+ GET /?p=%20 HTTP/1.0
+User-Agent: Mozilla
+
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 12
+
+Hello World!
\ No newline at end of file diff --git a/test/files/90-request-uri-too-large.t b/test/files/90-request-uri-too-large.t new file mode 100644 index 0000000..eef176a --- /dev/null +++ b/test/files/90-request-uri-too-large.t @@ -0,0 +1,17 @@ +>>> +GET /blaaaaaaaaaaaaaaaaaaaaaaaaa +<<< +HTTP/1.0 414 Request-URI Too Large +Server: MyBigFatServer +Mime-Version: 1.0 +Date: Fri, 27 Sep 2013 16:37:37 GMT +Content-Type: text/html +Content-Length: 139 +Expires: Fri, 27 Sep 2013 16:37:37 GMT + +<HTML><HEAD> +<TITLE>Request-URI Too Large</TITLE> +</HEAD><BODY> +<H1>Request-URI Too Large</H1> +The Request-URI is Too Large +</BODY></HTML> diff --git a/test/files/91-request-unexpected-body.t b/test/files/91-request-unexpected-body.t new file mode 100644 index 0000000..358da12 --- /dev/null +++ b/test/files/91-request-unexpected-body.t @@ -0,0 +1,16 @@ +>>>
+POST / HTTP/1.1
+Host: localhost
+Content-Type: application/x-www-form-urlencoded
+
+login=foo&password=bar
+
+<<<
+HTTP/1.1 200 OK
+Content-Length: 0
+
+
+>>>
+GET / HTTP/1.1
+Host: localhost
+
diff --git a/test/files/92-http_0_9-method_only.t b/test/files/92-http_0_9-method_only.t new file mode 100644 index 0000000..5c7c9b2 --- /dev/null +++ b/test/files/92-http_0_9-method_only.t @@ -0,0 +1,3 @@ +>>>
+GET /
+
diff --git a/test/files/93-compressed-response-deflateasgzip.t b/test/files/93-compressed-response-deflateasgzip.t Binary files differnew file mode 100644 index 0000000..e6c2eb5 --- /dev/null +++ b/test/files/93-compressed-response-deflateasgzip.t diff --git a/test/files/94-compressed-response-multiple.t b/test/files/94-compressed-response-multiple.t Binary files differnew file mode 100644 index 0000000..4d0fdf7 --- /dev/null +++ b/test/files/94-compressed-response-multiple.t diff --git a/test/files/95-compressed-response-gzipasdeflate.t b/test/files/95-compressed-response-gzipasdeflate.t Binary files differnew file mode 100644 index 0000000..8076f83 --- /dev/null +++ b/test/files/95-compressed-response-gzipasdeflate.t diff --git a/test/files/96-compressed-response-lzma.t b/test/files/96-compressed-response-lzma.t Binary files differnew file mode 100644 index 0000000..a5ea306 --- /dev/null +++ b/test/files/96-compressed-response-lzma.t diff --git a/test/files/97-requests-cut.t b/test/files/97-requests-cut.t new file mode 100644 index 0000000..2d2da6c --- /dev/null +++ b/test/files/97-requests-cut.t @@ -0,0 +1,9 @@ +>>>
+GET /?p=%20 HTTP/1.1
+User-Agent: Mozilla
+
+G
+>>>
+ET /?p=%21 HTTP/1.1
+User-Agent: Mozilla + diff --git a/test/files/98-responses-cut.t b/test/files/98-responses-cut.t new file mode 100644 index 0000000..5bd8164 --- /dev/null +++ b/test/files/98-responses-cut.t @@ -0,0 +1,26 @@ +>>>
+GET /?p=%20 HTTP/1.1
+User-Agent: Mozilla
+
+GET /?p=%21 HTTP/1.1
+User-Agent: Mozilla
+
+<<<
+HTTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 14
+
+Hello World!
+H
+<<<
+TTP/1.0 200 OK
+Date: Mon, 31 Aug 2009 20:25:50 GMT
+Server: Apache
+Connection: close
+Content-Type: text/html
+Content-Length: 13
+
+Hello People!
\ No newline at end of file diff --git a/test/files/99-expect-100.t b/test/files/99-expect-100.t new file mode 100644 index 0000000..422f97f --- /dev/null +++ b/test/files/99-expect-100.t @@ -0,0 +1,27 @@ +>>> +PUT /forbidden HTTP/1.1 +Content-Length: 14 +Expect: 100-continue + + +<<< +HTTP/1.0 401 Forbidden +Content-Length: 0 + + +>>> +POST /ok HTTP/1.1 +Content-Length: 14 +Expect: 100-continue + + +<<< +HTTP/1.0 100 continue +Content-Length: 0 + + +>>> +Hello People! + +<<< +HTTP/1.0 200 OK diff --git a/test/files/anchor.empty b/test/files/anchor.empty new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/files/anchor.empty diff --git a/test/files/generate-gzip-tests.php b/test/files/generate-gzip-tests.php new file mode 100755 index 0000000..021c436 --- /dev/null +++ b/test/files/generate-gzip-tests.php @@ -0,0 +1,322 @@ +#!/usr/bin/env php +<? + +/* +Copyright (c) 2009-2010 Open Information Security Foundation +Copyright (c) 2010-2013 Qualys, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +- Neither the name of the Qualys, Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* + +ZLIB Compressed Data Format Specification version 3.3 +http://www.ietf.org/rfc/rfc1950.txt + +DEFLATE Compressed Data Format Specification version 1.3 +http://www.ietf.org/rfc/rfc1951.txt + +GZIP file format specification version 4.3 +http://www.ietf.org/rfc/rfc1952.txt + +*/ + +class GzipTest { + + private $compressionMethod = 0x08; + + private $forcedFlags = false; + + private $filename = false; + + private $comment = false; + + private $extra = false; + + private $textFlag = false; + + private $useHeaderCrc = false; + + private $headerCrc = false; + + private $crc32 = false; + + private $isize = false; + + private $data = "The five boxing wizards jump quickly."; + + private $xfl = 0; + + public function setCompressionMethod($m) { + $this->compressionMethod = $m; + } + + public function setCrc32($crc) { + $this->crc32 = $crc; + } + + public function setInputSize($len) { + $this->isize = $len; + } + + public function setXfl($xfl) { + $this->xfl = $xfl; + } + + public function setFilename($filename) { + $this->filename = $filename; + } + + public function setComment($comment) { + $this->comment = $comment; + } + + public function setExtra($extra) { + $this->extra = $extra; + } + + public function setTextFlag($b) { + $this->textFlag = $b; + } + + public function useHeaderCrc($b) { + $this->useHeaderCrc = $b; + } + + public function setHeaderCrc($crc) { + $this->headerCrc = $crc; + } + + public function setFlags($f) { + $this->forcedFlags = $f; + } + + public function getFlags() { + if ($this->forcedFlags !== false) { + return $this->forcedFlags; + } + + $flags = 0; + + // FTEXT + if ($this->textFlag) { + $flags = $flags | 0x01; + } + + // FHCRC + if ($this->useHeaderCrc) { + $flags = $flags | 0x02; + } + + // FEXTRA + if ($this->extra !== false) { + $flags = $flags | 0x04; + } + + // FNAME + if ($this->filename !== false) { + $flags = $flags | 0x08; + } + + // FCOMMENT + if ($this->comment !== false) { + $flags = $flags | 0x16; + } + + return $flags; + } + + public function setData($data) { + $this->data = $data; + } + + public function writeTo($filename) { + $fp = fopen($filename, "w+"); + $this->write($fp); + fclose($fp); + } + + public function write($fp) { + $header = ""; + + // header (ID1 + ID2) + $header .= "\x1f\x8b"; + + // compression method (CM) + $header .= pack("C", $this->compressionMethod); + + // flags (FLG) + $header .= pack("C", $this->getFlags()); + + // mtime (MTIME) + $header .= "\x9c\x54\xf4\x50"; + + // extra flags (XFL) + $header .= pack("C", $this->xfl); + + // operating system (OS) + $header .= "\xff"; + + // FEXTRA + if ($this->extra !== false) { + $header .= pack("v", strlen($this->extra)); + $header .= $this->extra; + } + + // FNAME + if ($this->filename !== false) { + $header .= $this->filename; + $header .= "\x00"; + } + + // FCOMMENT + if ($this->comment !== false) { + $header .= $this->comment; + $header .= "\x00"; + } + + fwrite($fp, $header); + + // FHCRC + if ($this->useHeaderCrc) { + if ($this->headerCrc !== false) { + // "The CRC16 consists of the two least significant bytes of the CRC32 [...]" + fwrite($fp, pack("v", crc32($header))); + } else { + fwrite($fp, pack("v", $this->headerCrc)); + } + } + + // compressed blocks + $compressedData = gzcompress($this->data); + // The gzcompress() function does not produce output that's fully compatible with gzip, + // so we need to strip out the extra data: remove 2 bytes from the beginning + // (CMF and FLG) and 4 bytes from the end (Adler CRC). + $compressedData = substr($compressedData, 2, strlen($compressedData) - 6); + fwrite($fp, $compressedData); + + // CRC32 + if ($this->crc32 === false) { + fwrite($fp, pack("V", crc32($this->data))); + } else { + fwrite($fp, pack("V", $this->crc32)); + } + + // uncompressed size (ISIZE) + if ($this->isize === false) { + fwrite($fp, pack("V", strlen($this->data))); + } else { + fwrite($fp, pack("V", $this->isize)); + } + } +} + +// 01: minimal file +$gz = new GzipTest(); +$gz->writeTo("gztest-01-minimal.gz"); + +// 02: with FNAME +$gz = new GzipTest(); +$gz->setFilename("file.txt"); +$gz->writeTo("gztest-02-fname.gz"); + +// 03: with FCOMMENT +$gz = new GzipTest(); +$gz->setComment("COMMENT"); +$gz->writeTo("gztest-03-fcomment.gz"); + +// 04: with FHCRC +$gz = new GzipTest(); +$gz->useHeaderCrc(true); +$gz->writeTo("gztest-04-fhcrc.gz"); + +// 05: with FEXTRA +$gz = new GzipTest(); +$gz->setExtra("EXTRA"); +$gz->writeTo("gztest-05-fextra.gz"); + +// 06: with FTEXT +$gz = new GzipTest(); +$gz->setTextFlag(true); +$gz->writeTo("gztest-06-ftext.gz"); + +// 07: with FRESERVED1 +$gz = new GzipTest(); +$gz->setFlags($gz->getFlags() | 0x20); +$gz->writeTo("gztest-07-freserved1.gz"); + +// 08: with FRESERVED2 +$gz = new GzipTest(); +$gz->setFlags($gz->getFlags() | 0x40); +$gz->writeTo("gztest-08-freserved2.gz"); + +// 09: with FRESERVED3 +$gz = new GzipTest(); +$gz->setFlags($gz->getFlags() | 0x80); +$gz->writeTo("gztest-09-freserved3.gz"); + +// 10: Two parts (compressed streams) +$gz = new GzipTest(); +$fp = fopen("gztest-10-multipart.gz", "w+"); +$gz->setFilename("file1.txt"); +$gz->write($fp); +$gz->setData("The quick brown fox jumps over the lazy dog."); +$gz->setFilename("file2.txt"); +$gz->write($fp); +fclose($fp); + +// 11: Invalid compression method +$gz = new GzipTest(); +$gz->setCompressionMethod(0x07); +$gz->writeTo("gztest-11-invalid-method.gz"); + +// 12: Invalid CRC32 +$gz = new GzipTest(); +$gz->setCrc32(0xffffffff); +$gz->writeTo("gztest-12-invalid-crc32.gz"); + +// 13: Invalid ISIZE +$gz = new GzipTest(); +$gz->setData("Grumpy Wizards make toxic brew for the Evil Queen and Jack."); +$gz->setInputSize(0x10); +$gz->writeTo("gztest-13-invalid-isize.gz"); + +// 14: Invalid extra flags (XFL) +$gz = new GzipTest(); +$gz->setXfl(0xff); +$gz->writeTo("gztest-14-invalid-xfl.gz"); + +// 15: Invalid header CRC (FHCRC) +$gz = new GzipTest(); +$gz->useHeaderCrc(true); +$gz->setHeaderCrc(0xffff); +$gz->writeTo("gztest-15-invalid-fhcrc.gz"); + +?> diff --git a/test/files/gztest-01-minimal.gz b/test/files/gztest-01-minimal.gz Binary files differnew file mode 100644 index 0000000..e82fcde --- /dev/null +++ b/test/files/gztest-01-minimal.gz diff --git a/test/files/gztest-02-fname.gz b/test/files/gztest-02-fname.gz Binary files differnew file mode 100644 index 0000000..bb38b70 --- /dev/null +++ b/test/files/gztest-02-fname.gz diff --git a/test/files/gztest-03-fcomment.gz b/test/files/gztest-03-fcomment.gz Binary files differnew file mode 100644 index 0000000..fe55135 --- /dev/null +++ b/test/files/gztest-03-fcomment.gz diff --git a/test/files/gztest-04-fhcrc.gz b/test/files/gztest-04-fhcrc.gz Binary files differnew file mode 100644 index 0000000..cd0ce6b --- /dev/null +++ b/test/files/gztest-04-fhcrc.gz diff --git a/test/files/gztest-05-fextra.gz b/test/files/gztest-05-fextra.gz Binary files differnew file mode 100644 index 0000000..72290b0 --- /dev/null +++ b/test/files/gztest-05-fextra.gz diff --git a/test/files/gztest-06-ftext.gz b/test/files/gztest-06-ftext.gz Binary files differnew file mode 100644 index 0000000..9d9aecc --- /dev/null +++ b/test/files/gztest-06-ftext.gz diff --git a/test/files/gztest-07-freserved1.gz b/test/files/gztest-07-freserved1.gz Binary files differnew file mode 100644 index 0000000..bd365b5 --- /dev/null +++ b/test/files/gztest-07-freserved1.gz diff --git a/test/files/gztest-08-freserved2.gz b/test/files/gztest-08-freserved2.gz Binary files differnew file mode 100644 index 0000000..e240ec1 --- /dev/null +++ b/test/files/gztest-08-freserved2.gz diff --git a/test/files/gztest-09-freserved3.gz b/test/files/gztest-09-freserved3.gz Binary files differnew file mode 100644 index 0000000..4071cdc --- /dev/null +++ b/test/files/gztest-09-freserved3.gz diff --git a/test/files/gztest-10-multipart.gz b/test/files/gztest-10-multipart.gz Binary files differnew file mode 100644 index 0000000..a2c0cd5 --- /dev/null +++ b/test/files/gztest-10-multipart.gz diff --git a/test/files/gztest-11-invalid-method.gz b/test/files/gztest-11-invalid-method.gz Binary files differnew file mode 100644 index 0000000..9c13768 --- /dev/null +++ b/test/files/gztest-11-invalid-method.gz diff --git a/test/files/gztest-12-invalid-crc32.gz b/test/files/gztest-12-invalid-crc32.gz Binary files differnew file mode 100644 index 0000000..1832ef8 --- /dev/null +++ b/test/files/gztest-12-invalid-crc32.gz diff --git a/test/files/gztest-13-invalid-isize.gz b/test/files/gztest-13-invalid-isize.gz Binary files differnew file mode 100644 index 0000000..55263bc --- /dev/null +++ b/test/files/gztest-13-invalid-isize.gz diff --git a/test/files/gztest-14-invalid-xfl.gz b/test/files/gztest-14-invalid-xfl.gz Binary files differnew file mode 100644 index 0000000..a844957 --- /dev/null +++ b/test/files/gztest-14-invalid-xfl.gz diff --git a/test/files/gztest-15-invalid-fhcrc.gz b/test/files/gztest-15-invalid-fhcrc.gz Binary files differnew file mode 100644 index 0000000..b6fa5dd --- /dev/null +++ b/test/files/gztest-15-invalid-fhcrc.gz diff --git a/test/fuzz/fuzz_diff.c b/test/fuzz/fuzz_diff.c new file mode 100644 index 0000000..01ba4bc --- /dev/null +++ b/test/fuzz/fuzz_diff.c @@ -0,0 +1,440 @@ +/** + * @file + * @author Philippe Antoine <contact@catenacyber.fr> + * fuzz harness for libhtp + */ + + +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "htp/htp.h" +#include "test/test.h" +#include "fuzz_htp.h" +#include "htp/htp_private.h" + +FILE * logfile = NULL; + + +/** + * Invoked at the end of every transaction. + * + * @param[in] connp + */ +static int HTPCallbackResponse(htp_tx_t *out_tx) { + if (out_tx != NULL) { + char *x = bstr_util_strdup_to_c(out_tx->request_line); + fprintf(logfile, "HTPCallbackResponse %s\n", x); + free(x); + } + return 0; +} + +static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackRequestHeaderData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0) { + fprintf(logfile, "HTPCallbackRequestHeaderData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackResponseHeaderData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0) { + fprintf(logfile, "HTPCallbackResponseHeaderData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackRequestHasTrailer(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestHasTrailer\n"); + return 0; +} + +static int HTPCallbackResponseHasTrailer(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackResponseHasTrailer\n"); + return 0; +} + +static int HTPCallbackRequestBodyData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackRequestBodyData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0 && tx_data->data != NULL) { + fprintf(logfile, "HTPCallbackRequestBodyData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackResponseBodyData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackResponseBodyData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0 && tx_data->data != NULL) { + fprintf(logfile, "HTPCallbackResponseBodyData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackRequestStart(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestStart\n"); + return 0; +} + +static int HTPCallbackRequest(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequest\n"); + return 0; +} + +static int HTPCallbackResponseStart(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackResponseStart\n"); + return 0; +} + +static int HTPCallbackRequestLine(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestLine\n"); + return 0; +} + +/** + * Invoked every time LibHTP wants to log. + * + * @param[in] log + */ +static int HTPCallbackLog(htp_log_t *log) { + fprintf(logfile, "HTPCallbackLog [%d][code %d][file %s][line %d] %s\n", + log->level, log->code, log->file, log->line, log->msg); + return 0; +} + +void fuzz_openFile(const char * name) { + if (logfile != NULL) { + fclose(logfile); + } + logfile = fopen(name, "w"); +} + +htp_cfg_t *cfg; + +static void libhtpFuzzInit() { + logfile = fopen("/dev/null", "w"); + if (logfile == NULL) { + abort(); + } + // Create LibHTP configuration + cfg = htp_config_create(); + if (htp_config_set_server_personality(cfg, HTP_SERVER_IDS) != HTP_OK) { + htp_config_destroy(cfg); + return; + } + htp_config_register_log(cfg, HTPCallbackLog); + htp_config_register_request_header_data(cfg, HTPCallbackRequestHeaderData); + htp_config_register_request_trailer_data(cfg, HTPCallbackRequestHeaderData); + htp_config_register_response_header_data(cfg, HTPCallbackResponseHeaderData); + htp_config_register_response_trailer_data(cfg, HTPCallbackResponseHeaderData); + htp_config_register_request_trailer(cfg, HTPCallbackRequestHasTrailer); + htp_config_register_response_trailer(cfg, HTPCallbackResponseHasTrailer); + htp_config_register_request_body_data(cfg, HTPCallbackRequestBodyData); + htp_config_register_response_body_data(cfg, HTPCallbackResponseBodyData); + htp_config_register_request_start(cfg, HTPCallbackRequestStart); + htp_config_register_request_complete(cfg, HTPCallbackRequest); + htp_config_register_response_start(cfg, HTPCallbackResponseStart); + htp_config_register_response_complete(cfg, HTPCallbackResponse); + htp_config_register_request_line(cfg, HTPCallbackRequestLine); + setenv("srcdir", ".", 1); +} + +static htp_connp_t * libhtpFuzzRun(const uint8_t *Data, size_t Size) { + htp_connp_t * connp; + int rc; + test_t test; + + connp = htp_connp_create(cfg); + htp_connp_set_user_data(connp, (void *) 0x02); + htp_connp_open(connp, (const char *) "192.168.2.3", 12345, (const char *) "192.168.2.2", 80, NULL); + + test.buf = (char *)Data; + test.len = Size; + test.pos = 0; + test.chunk = NULL; + + // Find all chunks and feed them to the parser + int in_data_other = 0; + char *in_data = NULL; + size_t in_data_len = 0; + size_t in_data_offset = 0; + int out_data_other = 0; + char *out_data = NULL; + size_t out_data_len = 0; + size_t out_data_offset = 0; + + for (;;) { + if (test_next_chunk(&test) <= 0) { + break; + } + if (test.chunk_len == 0) { + continue; + } + if (test.chunk_direction == CLIENT) { + if (in_data_other) { + break; + } + rc = htp_connp_req_data(connp, NULL, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + break; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + in_data_other = 1; + in_data = test.chunk; + in_data_len = test.chunk_len; + in_data_offset = htp_connp_req_data_consumed(connp); + } + } else { + if (out_data_other) { + if (out_data == NULL) { + rc = htp_connp_res_data(connp, NULL, NULL, out_data_len - out_data_offset); + } else { + rc = htp_connp_res_data(connp, NULL, out_data + out_data_offset, out_data_len - out_data_offset); + } + if (rc == HTP_STREAM_ERROR) { + break; + } + out_data_other = 0; + } + rc = htp_connp_res_data(connp, NULL, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + break; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + out_data_other = 1; + out_data = test.chunk; + out_data_len = test.chunk_len; + out_data_offset = htp_connp_res_data_consumed(connp); + } + if (in_data_other) { + if (in_data == NULL) { + rc = htp_connp_req_data(connp, NULL, NULL, in_data_len - in_data_offset); + } else { + rc = htp_connp_req_data(connp, NULL, in_data + in_data_offset, in_data_len - in_data_offset); + } + if (rc == HTP_STREAM_ERROR) { + break; + } + in_data_other = 0; + } + } + } + if (out_data_other) { + if (out_data == NULL) { + (void) htp_connp_res_data(connp, NULL, NULL, out_data_len - out_data_offset); + } else { + (void) htp_connp_res_data(connp, NULL, out_data + out_data_offset, out_data_len - out_data_offset); + } + } + + htp_connp_close(connp, NULL); + return connp; +} + +void* libhtprsFuzzRun(const uint8_t *Data, uint32_t Size); +void* libhtprsFuzzConnp(void *); +size_t htp_connp__rstx_size(void *); +void * htp_connp__rstx(void *, size_t); +void * htp_tx_request_method(void *); +void * htp_tx_request_uri(void *); +void * htp_tx_request_protocol(void *); +void * htp_tx_response_protocol(void *); +void * htp_tx_response_status(void *); +size_t htp_tx_request_headers_size(void *); +void *htp_tx_request_header_index(void *, size_t); +size_t htp_tx_response_headers_size(void *); +void *htp_tx_response_header_index(void *, size_t); +void * htp_header_name(void *); +void * htp_header_value(void *); +size_t bstr_len_rs(void *); +uint8_t * bstr_ptr_rs(void *); +void libhtprsFreeFuzzRun(void *t); + +static int bstrDiff(void* rsbstr, bstr * cbstr, const char *field) { + if (rsbstr == NULL && cbstr == NULL) { + return 0; + } + if (rsbstr == NULL) { + printf("Assertion failure: Bstr %s rust is zero\n", field); + return 1; + } + if (cbstr == NULL) { + printf("Assertion failure: Bstr %s C is zero\n", field); + return 1; + } + size_t len = bstr_len(cbstr); + uint8_t * rsptr = bstr_ptr_rs(rsbstr); + uint8_t * cptr = bstr_ptr(cbstr); + if (bstr_len_rs(rsbstr) != len) { + fprint_raw_data(stdout, "c=", cptr, len); + fprint_raw_data(stdout, "rust=", rsptr, bstr_len_rs(rsbstr)); + printf("Assertion failure: Bstr %s lengths are different %zu vs %zu\n", field, bstr_len_rs(rsbstr), len); + return 1; + } + for (size_t i=0; i<len; i++) { + if (rsptr[i] != cptr[i]) { + fprint_raw_data(stdout, "c=", cptr, len); + fprint_raw_data(stdout, "rust=", rsptr, bstr_len_rs(rsbstr)); + printf("Assertion failure: Bstr %s index %zu are different %02x vs %02x\n", field, i, rsptr[i], cptr[i]); + return 1; + } + } + return 0; +} + +static int txDiff(void* rstx, htp_tx_t * ctx) { + if (bstrDiff(htp_tx_request_method(rstx), ctx->request_method, "methods")) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_tx_request_uri(rstx), ctx->request_uri, "uri")) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_tx_request_protocol(rstx), ctx->request_protocol, "protocol_request")) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_tx_response_protocol(rstx), ctx->response_protocol, "protocol_response")) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_tx_response_status(rstx), ctx->response_status, "status")) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + + uint32_t nbhc = htp_table_size(ctx->request_headers); + uint32_t rsnbh = htp_tx_request_headers_size(rstx); + if (rsnbh != nbhc) { + printf("Assertion failure: got nbheaders c=%d versus rust=%d\n", nbhc, rsnbh); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + + for (uint32_t i = 0; i < nbhc; i++) { + htp_header_t *h = (htp_header_t *) htp_table_get_index(ctx->request_headers, i, NULL); + void *rsh = htp_tx_request_header_index(rstx, (size_t) i); + if (bstrDiff(htp_header_name(rsh), h->name, "header-name")) { + printf("request header %d is different\n", i); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_header_value(rsh), h->value, "header-value")) { + printf("request header %d is different\n", i); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + } + + nbhc = htp_table_size(ctx->response_headers); + rsnbh = htp_tx_response_headers_size(rstx); + if (rsnbh != nbhc) { + printf("Assertion failure: got nbheaders c=%d versus rust=%d\n", nbhc, rsnbh); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + + for (uint32_t i = 0; i < nbhc; i++) { + htp_header_t *h = (htp_header_t *) htp_table_get_index(ctx->response_headers, i, NULL); + void *rsh = htp_tx_response_header_index(rstx, (size_t) i); + if (bstrDiff(htp_header_name(rsh), h->name, "header-name")) { + printf("response header %d is different\n", i); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + if (bstrDiff(htp_header_value(rsh), h->value, "header-value")) { + printf("response header %d is different\n", i); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + } + + return 0; +} + +static int connDiff(void* rsconnp, htp_conn_t * conn) { + uint32_t rs = htp_connp__rstx_size(rsconnp); + uint32_t c = htp_list_size(conn->transactions); + if (rs != c) { + printf("Assertion failure: got nbtx c=%d versus rust=%d\n", c, rs); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#endif + return 1; + } + for (uint32_t i = 0; i < c; i++) { + htp_tx_t *ctx = (htp_tx_t *) htp_list_get(conn->transactions, i); + void *rstx = htp_connp__rstx(rsconnp, (size_t) i); + if (txDiff(rstx, ctx)) { + printf("tx %d is different\n", i); + return 1; + } + } + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + //initialize output file + if (logfile == NULL) { + libhtpFuzzInit(); + } + + htp_connp_t * connp = libhtpFuzzRun(Data, Size); + htp_conn_t * conn = htp_connp_get_connection(connp); + + void* rstest = libhtprsFuzzRun(Data, Size); + void * rsconnp = libhtprsFuzzConnp(rstest); + if (connDiff(rsconnp, conn)) { + printf("results are different\n"); + } + libhtprsFreeFuzzRun(rstest); + + htp_connp_destroy_all(connp); + + return 0; +} + diff --git a/test/fuzz/fuzz_htp.c b/test/fuzz/fuzz_htp.c new file mode 100644 index 0000000..37bba57 --- /dev/null +++ b/test/fuzz/fuzz_htp.c @@ -0,0 +1,257 @@ +/** + * @file + * @author Philippe Antoine <contact@catenacyber.fr> + * fuzz harness for libhtp + */ + + +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <inttypes.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "htp/htp.h" +#include "test/test.h" +#include "fuzz_htp.h" + +FILE * logfile = NULL; + + +/** + * Invoked at the end of every transaction. + * + * @param[in] connp + */ +static int HTPCallbackResponse(htp_tx_t *out_tx) { + if (out_tx != NULL) { + char *x = bstr_util_strdup_to_c(out_tx->request_line); + fprintf(logfile, "HTPCallbackResponse %s\n", x); + free(x); + } + return 0; +} + +static int HTPCallbackRequestHeaderData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackRequestHeaderData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0) { + fprintf(logfile, "HTPCallbackRequestHeaderData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackResponseHeaderData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackResponseHeaderData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0) { + fprintf(logfile, "HTPCallbackResponseHeaderData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackRequestHasTrailer(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestHasTrailer\n"); + return 0; +} + +static int HTPCallbackResponseHasTrailer(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackResponseHasTrailer\n"); + return 0; +} + +static int HTPCallbackRequestBodyData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackRequestBodyData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0 && tx_data->data != NULL) { + fprintf(logfile, "HTPCallbackRequestBodyData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackResponseBodyData(htp_tx_data_t *tx_data) +{ + fprintf(logfile, "HTPCallbackResponseBodyData %"PRIuMAX"\n", (uintmax_t)tx_data->len); + if (tx_data->len > 0 && tx_data->data != NULL) { + fprintf(logfile, "HTPCallbackResponseBodyData %x %x\n", tx_data->data[0], tx_data->data[(uintmax_t)tx_data->len-1]); + } + return 0; +} + +static int HTPCallbackRequestStart(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestStart\n"); + return 0; +} + +static int HTPCallbackRequest(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequest\n"); + return 0; +} + +static int HTPCallbackResponseStart(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackResponseStart\n"); + return 0; +} + +static int HTPCallbackRequestLine(htp_tx_t *tx) +{ + fprintf(logfile, "HTPCallbackRequestLine\n"); + return 0; +} + +/** + * Invoked every time LibHTP wants to log. + * + * @param[in] log + */ +static int HTPCallbackLog(htp_log_t *log) { + fprintf(logfile, "HTPCallbackLog [%d][code %d][file %s][line %d] %s\n", + log->level, log->code, log->file, log->line, log->msg); + return 0; +} + +void fuzz_openFile(const char * name) { + if (logfile != NULL) { + fclose(logfile); + } + logfile = fopen(name, "w"); +} + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + htp_cfg_t *cfg; + htp_connp_t * connp; + int rc; + test_t test; + + //initialize output file + if (logfile == NULL) { + logfile = fopen("/dev/null", "w"); + if (logfile == NULL) { + abort(); + } + } + + // Create LibHTP configuration + cfg = htp_config_create(); + if (htp_config_set_server_personality(cfg, HTP_SERVER_IDS) != HTP_OK) { + htp_config_destroy(cfg); + return 0; + } + htp_config_register_log(cfg, HTPCallbackLog); + htp_config_register_request_header_data(cfg, HTPCallbackRequestHeaderData); + htp_config_register_request_trailer_data(cfg, HTPCallbackRequestHeaderData); + htp_config_register_response_header_data(cfg, HTPCallbackResponseHeaderData); + htp_config_register_response_trailer_data(cfg, HTPCallbackResponseHeaderData); + htp_config_register_request_trailer(cfg, HTPCallbackRequestHasTrailer); + htp_config_register_response_trailer(cfg, HTPCallbackResponseHasTrailer); + htp_config_register_request_body_data(cfg, HTPCallbackRequestBodyData); + htp_config_register_response_body_data(cfg, HTPCallbackResponseBodyData); + htp_config_register_request_start(cfg, HTPCallbackRequestStart); + htp_config_register_request_complete(cfg, HTPCallbackRequest); + htp_config_register_response_start(cfg, HTPCallbackResponseStart); + htp_config_register_response_complete(cfg, HTPCallbackResponse); + htp_config_register_request_line(cfg, HTPCallbackRequestLine); + + connp = htp_connp_create(cfg); + htp_connp_set_user_data(connp, (void *) 0x02); + htp_connp_open(connp, (const char *) "192.168.2.3", 12345, (const char *) "192.168.2.2", 80, NULL); + + test.buf = (char *)Data; + test.len = Size; + test.pos = 0; + test.chunk = NULL; + + // Find all chunks and feed them to the parser + int in_data_other = 0; + char *in_data = NULL; + size_t in_data_len = 0; + size_t in_data_offset = 0; + int out_data_other = 0; + char *out_data = NULL; + size_t out_data_len = 0; + size_t out_data_offset = 0; + + for (;;) { + if (test_next_chunk(&test) <= 0) { + break; + } + if (test.chunk_len == 0) { + continue; + } + if (test.chunk_direction == CLIENT) { + if (in_data_other) { + break; + } + rc = htp_connp_req_data(connp, NULL, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + break; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + in_data_other = 1; + in_data = test.chunk; + in_data_len = test.chunk_len; + in_data_offset = htp_connp_req_data_consumed(connp); + } + } else { + if (out_data_other) { + if (out_data == NULL) { + rc = htp_connp_res_data(connp, NULL, NULL, out_data_len - out_data_offset); + } else { + rc = htp_connp_res_data(connp, NULL, out_data + out_data_offset, out_data_len - out_data_offset); + } + if (rc == HTP_STREAM_ERROR) { + break; + } + out_data_other = 0; + } + rc = htp_connp_res_data(connp, NULL, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + break; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + out_data_other = 1; + out_data = test.chunk; + out_data_len = test.chunk_len; + out_data_offset = htp_connp_res_data_consumed(connp); + } + if (in_data_other) { + if (in_data == NULL) { + rc = htp_connp_req_data(connp, NULL, NULL, in_data_len - in_data_offset); + } else { + rc = htp_connp_req_data(connp, NULL, in_data + in_data_offset, in_data_len - in_data_offset); + } + if (rc == HTP_STREAM_ERROR) { + break; + } + in_data_other = 0; + } + } + } + if (out_data_other) { + if (out_data == NULL) { + (void) htp_connp_res_data(connp, NULL, NULL, out_data_len - out_data_offset); + } else { + (void) htp_connp_res_data(connp, NULL, out_data + out_data_offset, out_data_len - out_data_offset); + } + } + + htp_connp_close(connp, NULL); + htp_connp_destroy_all(connp); + // Destroy LibHTP configuration + htp_config_destroy(cfg); + + return 0; +} + diff --git a/test/fuzz/fuzz_htp.h b/test/fuzz/fuzz_htp.h new file mode 100644 index 0000000..57ce613 --- /dev/null +++ b/test/fuzz/fuzz_htp.h @@ -0,0 +1,26 @@ +/** + * @file + * @author Philippe Antoine <contact@catenacyber.fr> + * fuzz harness for libhtp + */ + +#ifndef __FUZZ_HTP_H__ +#define __FUZZ_HTP_H__ + +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <stdio.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#include "htp/htp.h" +#include "test/test.h" + +void fuzz_openFile(const char * name); +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); + +#endif /* __FUZZ_HTP_H__ */ + diff --git a/test/fuzz/onefile.c b/test/fuzz/onefile.c new file mode 100644 index 0000000..6e188d9 --- /dev/null +++ b/test/fuzz/onefile.c @@ -0,0 +1,52 @@ +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include "fuzz_htp.h" + +int main(int argc, char** argv) +{ + FILE * fp; + uint8_t *Data; + size_t Size; + + if (argc == 3) { + fuzz_openFile(argv[2]); + } else if (argc != 2) { + return 1; + } + //opens the file, get its size, and reads it into a buffer + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + return 2; + } + if (fseek(fp, 0L, SEEK_END) != 0) { + fclose(fp); + return 2; + } + Size = ftell(fp); + if (Size == (size_t) -1) { + fclose(fp); + return 2; + } + if (fseek(fp, 0L, SEEK_SET) != 0) { + fclose(fp); + return 2; + } + Data = malloc(Size); + if (Data == NULL) { + fclose(fp); + return 2; + } + if (fread(Data, Size, 1, fp) != 1) { + fclose(fp); + free(Data); + return 2; + } + + //lauch fuzzer + LLVMFuzzerTestOneInput(Data, Size); + free(Data); + fclose(fp); + return 0; +} + diff --git a/test/gtest/gtest-all.cc b/test/gtest/gtest-all.cc new file mode 100644 index 0000000..5ced66a --- /dev/null +++ b/test/gtest/gtest-all.cc @@ -0,0 +1,9118 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include <ctype.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> +#include <wctype.h> + +#include <algorithm> +#include <ostream> // NOLINT +#include <sstream> +#include <vector> + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include <fcntl.h> // NOLINT +# include <limits.h> // NOLINT +# include <sched.h> // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include <strings.h> // NOLINT +# include <sys/mman.h> // NOLINT +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT +# include <string> + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include <strings.h> // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include <windows.h> // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include <io.h> // NOLINT +# include <sys/timeb.h> // NOLINT +# include <sys/types.h> // NOLINT +# include <sys/stat.h> // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include <sys/time.h> // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include <windows.h> // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include <sys/time.h> // NOLINT +# include <unistd.h> // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include <stdexcept> +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include <arpa/inet.h> // NOLINT +# include <netdb.h> // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include <errno.h> +#endif // !_WIN32_WCE +#include <stddef.h> +#include <stdlib.h> // For strtoll/_strtoul64/malloc/free. +#include <string.h> // For memmove. + +#include <algorithm> +#include <string> +#include <vector> + + +#if GTEST_OS_WINDOWS +# include <windows.h> // NOLINT +#endif // GTEST_OS_WINDOWS + + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast<unsigned int>(GetTimeInMillis()) : + static_cast<unsigned int>(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast<int>((raw_seed - 1U) % + static_cast<unsigned int>(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + String color_; + String death_test_style_; + bool death_test_use_fork_; + String filter_; + String internal_run_death_test_; + bool list_tests_; + String output_; + bool print_time_; + bool pretty_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + String stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// The output buffer str must containt at least 32 characters. +// The function returns the address of the output buffer. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. +GTEST_API_ char* CodePointToUtf8(UInt32 code_point, char* str); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ String WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template <class Container, typename Predicate> +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template <class Container, typename Functor> +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template <typename E> +inline E GetElementOr(const std::vector<E>& v, int i, E default_value) { + return (i < 0 || i >= static_cast<int>(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template <typename E> +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector<E>* v) { + const int size = static_cast<int>(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template <typename E> +inline void Shuffle(internal::Random* random, std::vector<E>* v) { + ShuffleRange(random, 0, static_cast<int>(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template <typename T> +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const char* key) + : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return String(test_property.key()).Compare(key_) == 0; + } + + private: + String key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static String GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static String GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const String &test_case_name, + const String &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const String& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as a String. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + virtual String CurrentStackTrace(int max_depth, int skip_count); + virtual void UponLeavingGTest(); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + String message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as a String. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + String CurrentOsStackTraceExceptTop(int skip_count); + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector<Environment*>& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector<TraceInfo>& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector<TraceInfo>& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal<TestPartResultReporterInterface*> + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector<Environment*> environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector<TestCase*> test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector<int> test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_; + internal::scoped_ptr<internal::DeathTestFactory> death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ String GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template <typename Integer> +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast<Integer>(parsed); + if (parse_success && static_cast<BiggestConvertible>(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const TestProperty& property) { + test_result->RecordProperty(property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector<testing::TestPartResult>& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to xterm, xterm-color, xterm-256color, linux or cygwin."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", kUniversalFilter), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector<TestCase*>& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +String g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +String UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + String(gtest_output_flag) : + String(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +String UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return String(internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).ToString() ); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.ToString(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.ToString(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// TODO(keithray): move String function implementations to gtest-string.cc. + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, + const String &test_name) { + const String& full_name = String::Format("%s.%s", + test_case_name.c_str(), + test_name.c_str()); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + String positive; + String negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = String(""); + } else { + positive = String(p, dash - p); // Everything up to the dash + negative = String(dash+1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId<Test>(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const String expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast<int>(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as a String. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return String(""); +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast<TimeInMillis>(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast<TimeInMillis>(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast<TimeInMillis>(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String + +// Returns the input enclosed in double quotes if it's not NULL; +// otherwise returns "(null)". For example, "\"Hello\"" is returned +// for input "Hello". +// +// This is useful for printing a C string in the syntax of a literal. +// +// Known issue: escape sequences are not handled yet. +String String::ShowCStringQuoted(const char* c_str) { + return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); +} + +// Copies at most length characters from str into a newly-allocated +// piece of memory of size length+1. The memory is allocated with new[]. +// A terminating null byte is written to the memory, and a pointer to it +// is returned. If str is NULL, NULL is returned. +static char* CloneString(const char* str, size_t length) { + if (str == NULL) { + return NULL; + } else { + char* const clone = new char[length + 1]; + posix::StrNCpy(clone, str, length); + clone[length] = '\0'; + return clone; + } +} + +// Clones a 0-terminated C string, allocating memory using new. The +// caller is responsible for deleting[] the return value. Returns the +// cloned string, or NULL if the input is NULL. +const char * String::CloneCString(const char* c_str) { + return (c_str == NULL) ? + NULL : CloneString(c_str, strlen(c_str)); +} + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + // TODO(wan): consider allowing a testing::String object to + // contain '\0'. This will make it behave more like std::string, + // and will allow ToUtf8String() to return the correct encoding + // for '\0' s.t. we can get rid of the conditional here (and in + // several other places). + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast<int>(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template <typename RawType> +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint<RawType> lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE<float>(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE<double>(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template <typename StringType> +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template <typename StringType> +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; // String::Format can't exceed this length. + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const String error_hex(String::Format("0x%08X ", hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast<UInt32>(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast<UInt32>(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast<UInt32>(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast<UInt32>(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast<UInt32>(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// The output buffer str must containt at least 32 characters. +// The function returns the address of the output buffer. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. +char* CodePointToUtf8(UInt32 code_point, char* str) { + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast<char>(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xE0 | code_point); // 1110xxxx + } else if (code_point <= kMaxCodePoint4) { + str[4] = '\0'; + str[3] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast<char>(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast<char>(0xF0 | code_point); // 11110xxx + } else { + // The longest string String::Format can produce when invoked + // with these parameters is 28 character long (not including + // the terminating nul character). We are asking for 32 character + // buffer just in case. This is also enough for strncpy to + // null-terminate the destination string. + posix::StrNCpy( + str, String::Format("(Invalid Unicode 0x%X)", code_point).c_str(), 32); + str[31] = '\0'; // Makes sure no change in the format to strncpy leaves + // the result unterminated. + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast<UInt32>(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +String WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast<int>(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast<UInt32>(str[i]); + } + + char buffer[32]; // CodePointToUtf8 requires a buffer this big. + stream << CodePointToUtf8(unicode_code_point, buffer); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to a String using the UTF-8 encoding. +// NULL will be converted to "(null)". +String String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + return String(internal::WideStringToUtf8(wide_c_str, -1).c_str()); +} + +// Similar to ShowWideCString(), except that this function encloses +// the converted string in double quotes. +String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + return String::Format("L\"%s\"", + String::ShowWideCString(wide_c_str).c_str()); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowWideCStringQuoted(expected), + String::ShowWideCStringQuoted(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << String::ShowWideCStringQuoted(s1) + << " vs " << String::ShowWideCStringQuoted(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Compares this with another String. +// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 +// if this is greater than rhs. +int String::Compare(const String & rhs) const { + const char* const lhs_c_str = c_str(); + const char* const rhs_c_str = rhs.c_str(); + + if (lhs_c_str == NULL) { + return rhs_c_str == NULL ? 0 : -1; // NULL < anything except NULL + } else if (rhs_c_str == NULL) { + return 1; + } + + const size_t shorter_str_len = + length() <= rhs.length() ? length() : rhs.length(); + for (size_t i = 0; i != shorter_str_len; i++) { + if (lhs_c_str[i] < rhs_c_str[i]) { + return -1; + } else if (lhs_c_str[i] > rhs_c_str[i]) { + return 1; + } + } + return (length() < rhs.length()) ? -1 : + (length() > rhs.length()) ? 1 : 0; +} + +// Returns true iff this String ends with the given suffix. *Any* +// String is considered to end with a NULL or empty suffix. +bool String::EndsWith(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str() == NULL) return false; + + const size_t this_len = strlen(c_str()); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CStringEquals(c_str() + this_len - suffix_len, suffix); +} + +// Returns true iff this String ends with the given suffix, ignoring case. +// Any String is considered to end with a NULL or empty suffix. +bool String::EndsWithCaseInsensitive(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str() == NULL) return false; + + const size_t this_len = strlen(c_str()); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CaseInsensitiveCStringEquals(c_str() + this_len - suffix_len, suffix); +} + +// Formats a list of arguments to a String, using the same format +// spec string as for printf. +// +// We do not use the StringPrintf class as it is not universally +// available. +// +// The result is limited to 4096 characters (including the tailing 0). +// If 4096 characters are not enough to format the input, or if +// there's an error, "<formatting error or buffer exceeded>" is +// returned. +String String::Format(const char * format, ...) { + va_list args; + va_start(args, format); + + char buffer[4096]; + const int kBufferSize = sizeof(buffer)/sizeof(buffer[0]); + + // MSVC 8 deprecates vsnprintf(), so we want to suppress warning + // 4996 (deprecated function) there. +#ifdef _MSC_VER // We are using MSVC. +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + + const int size = vsnprintf(buffer, kBufferSize, format, args); + +# pragma warning(pop) // Restores the warning state. +#else // We are not using MSVC. + const int size = vsnprintf(buffer, kBufferSize, format, args); +#endif // _MSC_VER + va_end(args); + + // vsnprintf()'s behavior is not portable. When the buffer is not + // big enough, it returns a negative value in MSVC, and returns the + // needed buffer size on Linux. When there is an output error, it + // always returns a negative value. For simplicity, we lump the two + // error cases together. + if (size < 0 || size >= kBufferSize) { + return String("<formatting error or buffer exceeded>"); + } else { + return String(buffer, size); + } +} + +// Converts the buffer in a stringstream to a String, converting NUL +// bytes to "\\0" along the way. +String StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + // We need to use a helper stringstream to do this transformation + // because String doesn't support push_back(). + ::std::stringstream helper; + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + helper << "\\0"; // Replaces NUL with "\\0"; + } else { + helper.put(*ch); + } + } + + return String(helper.str().c_str()); +} + +// Appends the user-supplied message to the Google-Test-generated message. +String AppendUserMessage(const String& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const String user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + Message msg; + msg << gtest_msg << "\n" << user_msg_string; + + return msg.GetString(); +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const TestProperty& test_property) { + if (!ValidateTestProperty(test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector<TestProperty>::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// Adds a failure if the key is a reserved attribute of Google Test +// testcase tags. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const TestProperty& test_property) { + internal::String key(test_property.key()); + if (key == "name" || key == "status" || key == "time" || key == "classname") { + ADD_FAILURE() + << "Reserved key used in RecordProperty(): " + << key + << " ('name', 'status', 'time', and 'classname' are reserved by " + << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast<int>(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast<int>(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, const char* value) { + UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const String& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + String()); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static internal::String* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new internal::String(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static internal::String FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static internal::String PrintTestPartResultToString( + const TestPartResult& test_part_result); + +// A failed Google Test assertion will throw an exception of this type when +// GTEST_FLAG(throw_on_failure) is true (if exceptions are enabled). We +// derive it from std::runtime_error, which is for errors presumably +// detectable only at run time. Since std::runtime_error inherits from +// std::exception, many testing frameworks know how to extract and print the +// message inside it. +class GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} +}; +#endif // GTEST_HAS_EXCEPTIONS + +namespace internal { +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template <class T, typename Result> +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + internal::String* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast<Result>(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template <class T, typename Result> +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const GoogleTestFailureException&) { // NOLINT + // This exception doesn't originate in code under test. It makes no + // sense to report it as a test failure. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast<Result>(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +// TODO(vladl@google.com): Make a_test_case_name and a_name const string&'s +// to signify they cannot be NULLs. +TestInfo::TestInfo(const char* a_test_case_name, + const char* a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && internal::String(test_info->name()).Compare(name_) == 0; + } + + private: + internal::String name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast<int>(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete<TestInfo>); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast<int>(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast<int>(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static internal::String FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::String::Format("%d %s", count, + count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static internal::String FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static internal::String FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +// Prints a TestPartResult to a String. +static internal::String PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const internal::String& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +namespace internal { + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("TypeParam = %s", type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("GetParam() = %s", value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); + + internal::String test_case_name_; +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast<int>(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + test_case_name_ = test_case.name(); + const internal::String counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case_name_.c_str()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where TypeParam = %s\n", test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_case_name_.c_str(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_case_name_.c_str(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + test_case_name_ = test_case.name(); + const internal::String counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case_name_.c_str(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector<TestEventListener*> listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete<TestEventListener>); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static String EscapeXml(const char* str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static string RemoveInvalidXmlCharacters(const string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static String EscapeXmlAttribute(const char* str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static String EscapeXmlText(const char* str) { return EscapeXml(str, false); } + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(FILE* out, const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(FILE* out, const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the String is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static String TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const String output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + PrintXmlUnitTest(xmlout, unit_test); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +String XmlUnitTestResultPrinter::EscapeXml(const char* str, bool is_attribute) { + Message m; + + if (str != NULL) { + for (const char* src = str; *src; ++src) { + switch (*src) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(*src)) { + if (is_attribute && IsNormalizableWhitespace(*src)) + m << String::Format("&#x%02X;", unsigned(*src)); + else + m << *src; + } + break; + } + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters(const string& str) { + string output; + output.reserve(str.size()); + for (string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <testsuites name="AllTests"> <-- corresponds to a UnitTest object +// <testsuite name="testcase-name"> <-- corresponds to a TestCase object +// <testcase name="test-name"> <-- corresponds to a TestInfo object +// <failure message="...">...</failure> +// <failure message="...">...</failure> +// <failure message="...">...</failure> +// <-- individual assertion failures +// </testcase> +// </testsuite> +// </testsuites> + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << "<![CDATA["; + for (;;) { + const char* const next_segment = strstr(segment, "]]>"); + if (next_segment != NULL) { + stream->write( + segment, static_cast<std::streamsize>(next_segment - segment)); + *stream << "]]>]]><![CDATA["; + segment = next_segment + strlen("]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + *stream << " <testcase name=\"" + << EscapeXmlAttribute(test_info.name()).c_str() << "\""; + + if (test_info.value_param() != NULL) { + *stream << " value_param=\"" << EscapeXmlAttribute(test_info.value_param()) + << "\""; + } + if (test_info.type_param() != NULL) { + *stream << " type_param=\"" << EscapeXmlAttribute(test_info.type_param()) + << "\""; + } + + *stream << " status=\"" + << (test_info.should_run() ? "run" : "notrun") + << "\" time=\"" + << FormatTimeInMillisAsSeconds(result.elapsed_time()) + << "\" classname=\"" << EscapeXmlAttribute(test_case_name).c_str() + << "\"" << TestPropertiesAsXmlAttributes(result).c_str(); + + int failures = 0; + for (int i = 0; i < result.total_part_count(); ++i) { + const TestPartResult& part = result.GetTestPartResult(i); + if (part.failed()) { + if (++failures == 1) + *stream << ">\n"; + *stream << " <failure message=\"" + << EscapeXmlAttribute(part.summary()).c_str() + << "\" type=\"\">"; + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string message = location + "\n" + part.message(); + OutputXmlCDataSection(stream, + RemoveInvalidXmlCharacters(message).c_str()); + *stream << "</failure>\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " </testcase>\n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, + const TestCase& test_case) { + fprintf(out, + " <testsuite name=\"%s\" tests=\"%d\" failures=\"%d\" " + "disabled=\"%d\" ", + EscapeXmlAttribute(test_case.name()).c_str(), + test_case.total_test_count(), + test_case.failed_test_count(), + test_case.disabled_test_count()); + fprintf(out, + "errors=\"0\" time=\"%s\">\n", + FormatTimeInMillisAsSeconds(test_case.elapsed_time()).c_str()); + for (int i = 0; i < test_case.total_test_count(); ++i) { + ::std::stringstream stream; + OutputXmlTestInfo(&stream, test_case.name(), *test_case.GetTestInfo(i)); + fprintf(out, "%s", StringStreamToString(&stream).c_str()); + } + fprintf(out, " </testsuite>\n"); +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, + const UnitTest& unit_test) { + fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(out, + "<testsuites tests=\"%d\" failures=\"%d\" disabled=\"%d\" " + "errors=\"0\" time=\"%s\" ", + unit_test.total_test_count(), + unit_test.failed_test_count(), + unit_test.disabled_test_count(), + FormatTimeInMillisAsSeconds(unit_test.elapsed_time()).c_str()); + if (GTEST_FLAG(shuffle)) { + fprintf(out, "random_seed=\"%d\" ", unit_test.random_seed()); + } + fprintf(out, "name=\"AllTests\">\n"); + for (int i = 0; i < unit_test.total_test_case_count(); ++i) + PrintXmlTestCase(out, *unit_test.GetTestCase(i)); + fprintf(out, "</testsuites>\n"); +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + Send("gtest_streaming_protocol_version=1.0\n"); + } + + virtual ~StreamingListener() { + if (sockfd_ != -1) + CloseConnection(); + } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + Send("event=TestProgramStart\n"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + Send(String::Format("event=TestProgramEnd&passed=%d\n", + unit_test.Passed())); + + // Notify the streaming server to stop. + CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + Send(String::Format("event=TestIterationStart&iteration=%d\n", + iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + Send(String::Format("event=TestIterationEnd&passed=%d&elapsed_time=%sms\n", + unit_test.Passed(), + StreamableToString(unit_test.elapsed_time()).c_str())); + } + + void OnTestCaseStart(const TestCase& test_case) { + Send(String::Format("event=TestCaseStart&name=%s\n", test_case.name())); + } + + void OnTestCaseEnd(const TestCase& test_case) { + Send(String::Format("event=TestCaseEnd&passed=%d&elapsed_time=%sms\n", + test_case.Passed(), + StreamableToString(test_case.elapsed_time()).c_str())); + } + + void OnTestStart(const TestInfo& test_info) { + Send(String::Format("event=TestStart&name=%s\n", test_info.name())); + } + + void OnTestEnd(const TestInfo& test_info) { + Send(String::Format( + "event=TestEnd&passed=%d&elapsed_time=%sms\n", + (test_info.result())->Passed(), + StreamableToString((test_info.result())->elapsed_time()).c_str())); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + Send(String::Format("event=TestPartResult&file=%s&line=%d&message=", + UrlEncode(file_name).c_str(), + test_part_result.line_number())); + Send(UrlEncode(test_part_result.message()) + "\n"); + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + // Sends a string to the socket. + void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast<int>(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append(String::Format("%%%02x", static_cast<unsigned char>(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +// L < UnitTest::mutex_ +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +// L < UnitTest::mutex_ +ScopedTrace::~ScopedTrace() { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as a String. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +// L < mutex_ +// We use "L < mutex_" to denote that the function may acquire mutex_. +String OsStackTraceGetter::CurrentStackTrace(int, int) { + return String(""); +} + +// L < mutex_ +void OsStackTraceGetter::UponLeavingGTest() { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest * UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +// L < mutex_ +void UnitTest::AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast<int>(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast<volatile int*>(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Creates and adds a property to the current TestResult. If a property matching +// the supplied value already exists, updates its value instead. +void UnitTest::RecordPropertyForCurrentTest(const char* key, + const char* value) { + const TestProperty test_property(key, value); + impl_->current_test_result()->RecordProperty(test_property); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { + +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestCase* UnitTest::current_test_case() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestInfo* UnitTest::current_test_info() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +// L < mutex_ +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +// L < mutex_ +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +// L < mutex_ +void UnitTest::PopGTestTrace() { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + internal_run_death_test_flag_(NULL), + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete<TestCase>); + + // Deletes every Environment. + ForEach(environments_, internal::Delete<Environment>); + + delete os_stack_trace_getter_; +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const String& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in String form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const String& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + String name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector<TestCase*>::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(String(test_case_name), + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast<int>(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const String &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const String test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.\n", test_case->name()); + } + printf(" %s\n", test_info->name()); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast<int>(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast<int>(i); + } +} + +// Returns the current OS stack trace as a String. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +String GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX_, flag); + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", String(str, p - str).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template <typename CharType> +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const String arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template <typename CharType> +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include <crt_externs.h> +# endif // GTEST_OS_MAC + +# include <errno.h> +# include <fcntl.h> +# include <limits.h> +# include <stdarg.h> + +# if GTEST_OS_WINDOWS +# include <windows.h> +# else +# include <sys/mman.h> +# include <sys/wait.h> +# endif // GTEST_OS_WINDOWS + +#endif // GTEST_HAS_DEATH_TEST + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "colons. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static String ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static String DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const String& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort(::testing::internal::String::Format( \ + "CHECK failed: File %s, line %d: %s", \ + __FILE__, __LINE__, #expression)); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort(::testing::internal::String::Format( \ + "CHECK failed: File %s, line %d: %s != -1", \ + __FILE__, __LINE__, #expression)); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +String GetLastErrnoDescription() { + return String(errno == 0 ? "" : posix::StrError(errno)); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const String& message) { + last_death_test_message_ = message; +} + +String DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast<unsigned int>(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const String error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast<int>(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast<intptr_t>(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const String filter_flag = String::Format("--%s%s=%s.%s", + GTEST_FLAG_PREFIX_, kFilterFlag, + info->test_case_name(), + info->name()); + const String internal_flag = String::Format( + "--%s%s=%s|%d|%d|%u|%Iu|%Iu", + GTEST_FLAG_PREFIX_, + kInternalRunDeathTestFlag, + file_, line_, + death_test_index, + static_cast<unsigned int>(::GetCurrentProcessId()), + // size_t has the same with as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + reinterpret_cast<size_t>(write_handle), + reinterpret_cast<size_t>(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + String command_line = String::Format("%s %s \"%s\"", + ::GetCommandLineA(), + filter_flag.c_str(), + internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast<char*>(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector<char*>::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template <typename Str> + void AddArguments(const ::std::vector<Str>& arguments) { + for (typename ::std::vector<Str>::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + private: + std::vector<char*> args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast<ExecDeathTestArgs*>(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(String::Format("chdir(\"%s\") failed: %s", + original_dir, + GetLastErrnoDescription().c_str())); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(String::Format("execve(%s, ...) in %s failed: %s", + args->argv[0], + original_dir, + GetLastErrnoDescription().c_str())); + return EXIT_FAILURE; +} + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +bool StackLowerThanAddress(const void* ptr) GTEST_NO_INLINE_; +bool StackLowerThanAddress(const void* ptr) { + int dummy; + return &dummy < ptr; +} + +bool StackGrowsDown() { + int dummy; + return StackLowerThanAddress(&dummy); +} + +// A threadsafe implementation of fork(2) for threadsafe-style death tests +// that uses clone(2). It dies with an error message if anything goes +// wrong. +static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + void* const stack_top = + static_cast<char*>(stack) + (stack_grows_down ? stack_size : 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const String filter_flag = + String::Format("--%s%s=%s.%s", + GTEST_FLAG_PREFIX_, kFilterFlag, + info->test_case_name(), info->name()); + const String internal_flag = + String::Format("--%s%s=%s|%d|%d|%d", + GTEST_FLAG_PREFIX_, kInternalRunDeathTestFlag, + file_, line_, death_test_index, pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvs()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message(String::Format( + "Death test count (%d) somehow exceeded expected maximum (%d)", + death_test_index, flag->index())); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message(String::Format( + "Unknown death test style \"%s\" encountered", + GTEST_FLAG(death_test_style).c_str())); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort(String::Format("Unable to open parent process %u", + parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast<HANDLE>(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort(String::Format( + "Unable to duplicate the pipe handle %Iu from the parent process %u", + write_handle_as_size_t, parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast<HANDLE>(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort(String::Format( + "Unable to duplicate the event handle %Iu from the parent process %u", + event_handle_as_size_t, parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast<intptr_t>(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort(String::Format( + "Unable to convert pipe handle %Iu to a file descriptor", + write_handle_as_size_t)); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort(String::Format( + "Bad --gtest_internal_run_death_test flag: %s", + GTEST_FLAG(internal_run_death_test).c_str())); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort(String::Format( + "Bad --gtest_internal_run_death_test flag: %s", + GTEST_FLAG(internal_run_death_test).c_str())); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) + + +#include <stdlib.h> + +#if GTEST_OS_WINDOWS_MOBILE +# include <windows.h> +#elif GTEST_OS_WINDOWS +# include <direct.h> +# include <io.h> +#elif GTEST_OS_SYMBIAN || GTEST_OS_NACL +// Symbian OpenC and NaCl have PATH_MAX in sys/syslimits.h +# include <sys/syslimits.h> +#else +# include <limits.h> +# include <climits> // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + String dot_extension(String::Format(".%s", extension)); + if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { + return FilePath(String(pathname_.c_str(), pathname_.length() - 4)); + } + return *this; +} + +// Returns a pointer to the last occurence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(String(last_sep + 1)) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + String dir; + if (last_sep) { + dir = String(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + String file; + if (number == 0) { + file = String::Format("%s.%s", base_name.c_str(), extension); + } else { + file = String::Format("%s_%d.%s", base_name.c_str(), number, extension); + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(String::Format("%s%c%s", dir.c_str(), kPathSeparator, + relative_path.c_str())); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_<number>.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(String(pathname_.c_str(), pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#if GTEST_OS_WINDOWS_MOBILE +# include <windows.h> // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include <io.h> +# include <sys/stat.h> +#else +# include <unistd.h> +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include <mach/mach_init.h> +# include <mach/task.h> +# include <mach/vm_map.h> +#endif // GTEST_OS_MAC + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast<vm_address_t>(thread_list), + sizeof(thread_t) * thread_count); + return static_cast<size_t>(thread_count); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast<char*>(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in <ctype.h>, these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +String FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast<size_t>(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast<char*>(pattern_)); + free(const_cast<char*>(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast<char*>(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const char* const file_name = file == NULL ? kUnknownFile : file; + + if (line < 0) { + return String::Format("%s:", file_name).c_str(); + } +#ifdef _MSC_VER + return String::Format("%s(%d):", file_name, line).c_str(); +#else + return String::Format("%s:%d:", file_name, line).c_str(); +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const char* const file_name = file == NULL ? kUnknownFile : file; + + if (line < 0) + return file_name; + else + return String::Format("%s:%d", file_name, line).c_str(); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { + +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the + // current directory, so we create the temporary file in the /tmp + // directory instead. + char name_template[] = "/tmp/captured_stream.XXXXXX"; + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + String GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const String content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as a String. + static String ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast<size_t>(ftell(file)); +} + +// Reads the entire content of a file as a string. +String CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const String content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +String GetCapturedStream(CapturedStream** captured_stream) { + const String content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +String GetCapturedStdout() { return GetCapturedStream(&g_captured_stdout); } + +// Stops capturing stderr and returns the captured string. +String GetCapturedStderr() { return GetCapturedStream(&g_captured_stderr); } + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector<String> g_argvs; + +// Returns the command line as a vector of strings. +const ::std::vector<String>& GetArgvs() { return g_argvs; } + +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static String FlagToEnvVar(const char* flag) { + const String full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast<Int32>(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include <ctype.h> +#include <stdio.h> +#include <ostream> // NOLINT +#include <string> + +namespace testing { + +namespace { + +using ::std::ostream; + +#if GTEST_OS_WINDOWS_MOBILE // Windows CE does not define _snprintf_s. +# define snprintf _snprintf +#elif _MSC_VER >= 1400 // VC 8.0 and later deprecate snprintf and _snprintf. +# define snprintf _snprintf_s +#elif _MSC_VER +# define snprintf _snprintf +#endif // GTEST_OS_WINDOWS_MOBILE + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + snprintf(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template <typename UnsignedChar, typename Char> +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast<wchar_t>(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast<char>(c); + return kAsIs; + } else { + *os << String::Format("\\x%X", static_cast<UnsignedChar>(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsWideStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo<wchar_t>(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsNarrowStringLiteralTo(char c, ostream* os) { + return PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template <typename UnsignedChar, typename Char> +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << String::Format("%d", c).c_str(); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << String::Format(", 0x%X", + static_cast<UnsignedChar>(c)).c_str(); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo<unsigned char>(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo<unsigned char>(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo<wchar_t>(wc, os); +} + +// Prints the given array of characters to the ostream. +// The array starts at *begin, the length is len, it may include '\0' characters +// and may not be null-terminated. +static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) { + *os << "\""; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const char cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" \""; + } + is_previous_hex = PrintAsNarrowStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + PrintCharsAsStringTo(begin, len, os); +} + +// Prints the given array of wide characters to the ostream. +// The array starts at *begin, the length is len, it may include L'\0' +// characters and may not be null-terminated. +static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len, + ostream* os) { + *os << "L\""; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const wchar_t cur = begin[index]; + if (is_previous_hex && isascii(cur) && IsXDigit(static_cast<char>(cur))) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" L\""; + } + is_previous_hex = PrintAsWideStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_<const void*>(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_<const void*>(s) << " pointing to "; + PrintWideCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintWideCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintWideCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +internal::String TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? internal::String(message) : + internal::String(message, stack_trace - message); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast<int>(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set<const char*>::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set<String> tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const String name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const String& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/test/gtest/gtest.h b/test/gtest/gtest.h new file mode 100644 index 0000000..3143bd6 --- /dev/null +++ b/test/gtest/gtest.h @@ -0,0 +1,19537 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include <limits> +#include <vector> + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// The user can define the following macros in the build script to +// control Google Test's behavior. If the user doesn't define a macro +// in this list, Google Test will define it. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::string, which is different to std::string). +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::wstring, which is different to std::wstring). +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that <pthread.h> +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. + +// This header defines the following utilities: +// +// Macros indicating the current platform (defined to 1 if compiled on +// the given platform; otherwise undefined): +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// Note that it is possible that none of the GTEST_OS_* macros are defined. +// +// Macros indicating available Google Test features (defined to 1 if +// the corresponding feature is supported; otherwise undefined): +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_PARAM_TEST - value-parameterized tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above two are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above +// synchronization primitives have real implementations +// and Google Test is thread-safe; or 0 otherwise. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like +// platforms, or a reduced regular exception syntax on +// other platforms, including Windows. +// +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include <ctype.h> // for isspace, etc +#include <stddef.h> // for ptrdiff_t +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifndef _WIN32_WCE +# include <sys/types.h> +# include <sys/stat.h> +#endif // !_WIN32_WCE + +#include <iostream> // NOLINT +#include <sstream> // NOLINT +#include <string> // NOLINT + +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# ifdef ANDROID +# define GTEST_OS_LINUX_ANDROID 1 +# endif // ANDROID +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#endif // __CYGWIN__ + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if !GTEST_OS_WINDOWS +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include <unistd.h> +# if !GTEST_OS_NACL +// TODO(vladl@google.com): Remove this condition when Native Client SDK adds +// strings.h (tracked in +// http://code.google.com/p/nativeclient/issues/detail?id=1175). +# include <strings.h> // Native Client doesn't provide strings.h. +# endif +#elif !GTEST_OS_WINDOWS_MOBILE +# include <direct.h> +# include <io.h> +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +#endif + +#if GTEST_HAS_POSIX_RE + +// On some platforms, <regex.h> needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included <stdlib.h>, which is guaranteed to define size_t through +// <stddef.h>. +# include <regex.h> // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// <regex.h> is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// <regex.h> may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_HAS_POSIX_RE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "Google Test cannot be used where ::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# ifdef __GXX_RTTI +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include <typeinfo> when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include <typeinfo> +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we assume pthreads support is +// available on Linux and Mac. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is +// true. +# include <pthread.h> // NOLINT + +// For timespec and nanosleep, used below. +# include <time.h> // NOLINT +#endif + +// Determines whether Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, GCC 4.0.0+ and MSVC +// 2010 are the only mainstream compilers that come with a TR1 tuple +// implementation. NVIDIA's CUDA NVCC compiler pretends to be GCC by +// defining __GNUC__ and friends, but cannot compile GCC's tuple +// implementation. MSVC 2008 (9.0) provides TR1 tuple in a 323 MB +// Feature Pack download, which we cannot assume the user has. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000)) \ + || _MSC_VER >= 1600 +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif + +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation everywhere, we make it +// gtest-port.h's responsibility to #include the header implementing +// tr1/tuple. +#if GTEST_HAS_TR1_TUPLE + +# if GTEST_USE_OWN_TR1_TUPLE +// This file was GENERATED by a script. DO NOT EDIT BY HAND!!! + +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include <utility> // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template <GTEST_10_TYPENAMES_(U)> friend class tuple; \ + private: +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple<T##0, void, void, void, void, void, void, \ + void, void, void> +#define GTEST_2_TUPLE_(T) tuple<T##0, T##1, void, void, void, void, void, \ + void, void, void> +#define GTEST_3_TUPLE_(T) tuple<T##0, T##1, T##2, void, void, void, void, \ + void, void, void> +#define GTEST_4_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, void, void, void, \ + void, void, void> +#define GTEST_5_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, void, void, \ + void, void, void> +#define GTEST_6_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, void, \ + void, void, void> +#define GTEST_7_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \ + void, void, void> +#define GTEST_8_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \ + T##7, void, void> +#define GTEST_9_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \ + T##7, T##8, void> +#define GTEST_10_TUPLE_(T) tuple<T##0, T##1, T##2, T##3, T##4, T##5, T##6, \ + T##7, T##8, T##9> + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template <typename T0 = void, typename T1 = void, typename T2 = void, + typename T3 = void, typename T4 = void, typename T5 = void, + typename T6 = void, typename T7 = void, typename T8 = void, + typename T9 = void> +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef<T>::type is T if T is a reference; otherwise it's const T&. +template <typename T> +struct ByRef { typedef const T& type; }; // NOLINT +template <typename T> +struct ByRef<T&> { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef<T>::type + +// AddRef<T>::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference<T>::type. +template <typename T> +struct AddRef { typedef T& type; }; // NOLINT +template <typename T> +struct AddRef<T&> { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef<T>::type + +// A helper for implementing get<k>(). +template <int k> class Get; + +// A helper for implementing tuple_element<k, T>. kIndexValid is true +// iff k < the number of fields in tuple type T. +template <bool kIndexValid, int kIndex, class Tuple> +struct TupleElement; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 0, GTEST_10_TUPLE_(T)> { typedef T0 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 1, GTEST_10_TUPLE_(T)> { typedef T1 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 2, GTEST_10_TUPLE_(T)> { typedef T2 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 3, GTEST_10_TUPLE_(T)> { typedef T3 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 4, GTEST_10_TUPLE_(T)> { typedef T4 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 5, GTEST_10_TUPLE_(T)> { typedef T5 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 6, GTEST_10_TUPLE_(T)> { typedef T6 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 7, GTEST_10_TUPLE_(T)> { typedef T7 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 8, GTEST_10_TUPLE_(T)> { typedef T8 type; }; + +template <GTEST_10_TYPENAMES_(T)> +struct TupleElement<true, 9, GTEST_10_TUPLE_(T)> { typedef T9 type; }; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template <GTEST_1_TYPENAMES_(T)> +class GTEST_1_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template <GTEST_1_TYPENAMES_(U)> + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_1_TYPENAMES_(U)> + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_1_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template <GTEST_2_TYPENAMES_(T)> +class GTEST_2_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template <GTEST_2_TYPENAMES_(U)> + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template <typename U0, typename U1> + tuple(const ::std::pair<U0, U1>& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_2_TYPENAMES_(U)> + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template <typename U0, typename U1> + tuple& operator=(const ::std::pair<U0, U1>& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_2_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template <GTEST_3_TYPENAMES_(T)> +class GTEST_3_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template <GTEST_3_TYPENAMES_(U)> + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_3_TYPENAMES_(U)> + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_3_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template <GTEST_4_TYPENAMES_(T)> +class GTEST_4_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template <GTEST_4_TYPENAMES_(U)> + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_4_TYPENAMES_(U)> + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_4_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template <GTEST_5_TYPENAMES_(T)> +class GTEST_5_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template <GTEST_5_TYPENAMES_(U)> + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_5_TYPENAMES_(U)> + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_5_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template <GTEST_6_TYPENAMES_(T)> +class GTEST_6_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template <GTEST_6_TYPENAMES_(U)> + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_6_TYPENAMES_(U)> + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_6_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template <GTEST_7_TYPENAMES_(T)> +class GTEST_7_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template <GTEST_7_TYPENAMES_(U)> + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_7_TYPENAMES_(U)> + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_7_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template <GTEST_8_TYPENAMES_(T)> +class GTEST_8_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template <GTEST_8_TYPENAMES_(U)> + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_8_TYPENAMES_(U)> + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_8_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template <GTEST_9_TYPENAMES_(T)> +class GTEST_9_TUPLE_(T) { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template <GTEST_9_TYPENAMES_(U)> + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_9_TYPENAMES_(U)> + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_9_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template <GTEST_10_TYPENAMES_(T)> +class tuple { + public: + template <int k> friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template <GTEST_10_TYPENAMES_(U)> + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template <GTEST_10_TYPENAMES_(U)> + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template <GTEST_10_TYPENAMES_(U)> + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper<T> to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template <GTEST_1_TYPENAMES_(T)> +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template <GTEST_2_TYPENAMES_(T)> +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template <GTEST_3_TYPENAMES_(T)> +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template <GTEST_4_TYPENAMES_(T)> +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template <GTEST_5_TYPENAMES_(T)> +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template <GTEST_6_TYPENAMES_(T)> +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template <GTEST_7_TYPENAMES_(T)> +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template <GTEST_8_TYPENAMES_(T)> +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template <GTEST_9_TYPENAMES_(T)> +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template <GTEST_10_TYPENAMES_(T)> +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template <typename Tuple> struct tuple_size; + +template <GTEST_0_TYPENAMES_(T)> +struct tuple_size<GTEST_0_TUPLE_(T)> { static const int value = 0; }; + +template <GTEST_1_TYPENAMES_(T)> +struct tuple_size<GTEST_1_TUPLE_(T)> { static const int value = 1; }; + +template <GTEST_2_TYPENAMES_(T)> +struct tuple_size<GTEST_2_TUPLE_(T)> { static const int value = 2; }; + +template <GTEST_3_TYPENAMES_(T)> +struct tuple_size<GTEST_3_TUPLE_(T)> { static const int value = 3; }; + +template <GTEST_4_TYPENAMES_(T)> +struct tuple_size<GTEST_4_TUPLE_(T)> { static const int value = 4; }; + +template <GTEST_5_TYPENAMES_(T)> +struct tuple_size<GTEST_5_TUPLE_(T)> { static const int value = 5; }; + +template <GTEST_6_TYPENAMES_(T)> +struct tuple_size<GTEST_6_TUPLE_(T)> { static const int value = 6; }; + +template <GTEST_7_TYPENAMES_(T)> +struct tuple_size<GTEST_7_TUPLE_(T)> { static const int value = 7; }; + +template <GTEST_8_TYPENAMES_(T)> +struct tuple_size<GTEST_8_TUPLE_(T)> { static const int value = 8; }; + +template <GTEST_9_TYPENAMES_(T)> +struct tuple_size<GTEST_9_TUPLE_(T)> { static const int value = 9; }; + +template <GTEST_10_TYPENAMES_(T)> +struct tuple_size<GTEST_10_TUPLE_(T)> { static const int value = 10; }; + +template <int k, class Tuple> +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size<Tuple>::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element<k, Tuple >::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template <class Tuple> + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template <class Tuple> + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template <int k, GTEST_10_TYPENAMES_(T)> +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get<k>::Field(t); +} + +template <int k, GTEST_10_TYPENAMES_(T)> +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get<k>::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator<k, k>::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template <int kSize1, int kSize2> +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template <class Tuple1, class Tuple2> + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template <int k> +struct SameSizeTuplePrefixComparator<k, k> { + template <class Tuple1, class Tuple2> + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator<k - 1, k - 1>::Eq(t1, t2) && + ::std::tr1::get<k - 1>(t1) == ::std::tr1::get<k - 1>(t2); + } +}; + +} // namespace gtest_internal + +template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)> +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size<GTEST_10_TUPLE_(T)>::value, + tuple_size<GTEST_10_TUPLE_(U)>::value>::Eq(t, u); +} + +template <GTEST_10_TYPENAMES_(T), GTEST_10_TYPENAMES_(U)> +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents <boost/tr1/detail/config.hpp>, which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's <tuple>. +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include <tuple> + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the <tr1/tuple> header. This does +// not conform to the TR1 spec, which requires the header to be <tuple>. + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes <tr1/functional>, +// which is #included by <tr1/tuple>, to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// <tr1/functional>. Hence the following #define is a hack to prevent +// <tr1/functional> from being included. +# define _TR1_FUNCTIONAL 1 +# include <tr1/tuple> +# undef _TR1_FUNCTIONAL // Allows the user to #include + // <tr1/functional> if he chooses to. +# else +# include <tr1/tuple> // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +# else +// If the compiler is not GCC 4.0+, we assume the user is using a +// spec-conforming TR1 implementation. +# include <tuple> // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX) +# define GTEST_HAS_DEATH_TEST 1 +# include <vector> // NOLINT +#endif + +// We don't support MSVC 7.1 with exceptions disabled now. Therefore +// all the compilers we care about are adequate for supporting +// value-parameterized tests. +#define GTEST_HAS_PARAM_TEST 1 + +// Determines whether to support type-driven tests. + +// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#else +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type)\ + void operator=(type const &) + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ + type(type const &);\ + GTEST_DISALLOW_ASSIGN_(type) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifdef _MSC_VER + +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif + +#endif // _MSC_VER + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +namespace testing { + +class Message; + +namespace internal { + +class String; + +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template <bool> +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(bool(expr))> \ + msg[bool(expr) ? 1 : -1] + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template <typename T1, typename T2> +struct StaticAssertTypeEqHelper; + +template <typename T> +struct StaticAssertTypeEqHelper<T, T> {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template <typename T> +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines RE. + +// A simple C++ wrapper for <regex.h>. It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + +#if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of a string, as Google Test may be used + // where string is not available. We also do not use Google Test's own + // String type here, in order to simplify dependencies between the + // files. + const char* pattern_; + bool is_valid_; + +#if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +#endif + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_<ToType>(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To> +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template<typename To, typename From> // use like this: DownCast_<T*>(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_<From*>(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast<To>(f) != NULL); +#endif + return static_cast<To>(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template <class Derived, class Base> +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); + return dynamic_cast<Derived*>(base); // NOLINT +#else + return static_cast<Derived*>(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ String GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ String GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION + + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector<String> g_argvs; + +// GTEST_HAS_DEATH_TEST implies we have ::std::string. +const ::std::vector<String>& GetArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +#if GTEST_HAS_PTHREAD + +// Sleeps for (roughly) n milli-seconds. This function is only for +// testing Google Test's own constructs. Don't use it in user tests, +// either directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, NULL); +} + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) {} + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { notified_ = true; } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + while(!notified_) { + SleepMilliseconds(10); + } + } + + private: + volatile bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast<ThreadWithParamBase*>(thread)->Run(); + return NULL; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template <typename T> +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void (*UserThreadFunc)(T); + + ThreadWithParam( + UserThreadFunc func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + const UserThreadFunc func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// MutexBase and Mutex implement mutex on pthreads-based platforms. They +// are used in conjunction with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end +// // of the current scope. +// +// MutexBase implements behavior for both statically and dynamically +// allocated mutexes. Do not use MutexBase directly. Instead, write +// the following to define a static mutex: +// +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// +// You can forward declare a static mutex like this: +// +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// To create a dynamic mutex, just define an object of type Mutex. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + } + + // Releases this mutex. + void Unlock() { + // We don't protect writing to owner_ here, as it's the caller's + // responsibility to ensure that the current thread holds the + // mutex when this is called. + owner_ = 0; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(owner_ == pthread_self()) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + pthread_t owner_; // The thread holding the mutex; 0 means no one holds it. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 } + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + owner_ = 0; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal<T>. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast<ThreadLocalValueHolderBase*>(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +// +// // Thread 1 +// ThreadLocal<int> tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// An object managed for a thread by a ThreadLocal instance is deleted +// when the thread exits. Or, if the ThreadLocal instance dies in +// that thread, when the ThreadLocal dies. It's the user's +// responsibility to ensure that all other threads using a ThreadLocal +// have exited when it dies, or the per-thread objects for those +// threads will not be deleted. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template <typename T> +class ThreadLocal { + public: + ThreadLocal() : key_(CreateKey()), + default_() {} + explicit ThreadLocal(const T& value) : key_(CreateKey()), + default_(value) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_)); + if (holder != NULL) { + return CheckedDowncastToActualType<ValueHolder>(holder)->pointer(); + } + + ValueHolder* const new_holder = new ValueHolder(default_); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# define GTEST_IS_THREADSAFE 1 + +#else // GTEST_HAS_PTHREAD + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template <typename T> +class ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// The above synchronization primitives have dummy implementations. +// Therefore Google Test is not thread-safe. +# define GTEST_IS_THREADSAFE 0 + +#endif // GTEST_HAS_PTHREAD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template <bool bool_value> +struct bool_constant { + typedef bool_constant<bool_value> type; + static const bool value = bool_value; +}; +template <bool bool_value> const bool bool_constant<bool_value>::value; + +typedef bool_constant<false> false_type; +typedef bool_constant<true> true_type; + +template <typename T> +struct is_pointer : public false_type {}; + +template <typename T> +struct is_pointer<T*> : public true_type {}; + +template <typename Iterator> +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template <typename T> +struct IteratorTraits<T*> { + typedef T value_type; +}; + +template <typename T> +struct IteratorTraits<const T*> { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast<unsigned char>(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast<unsigned char>(ch)) != 0; +} + +inline char ToLower(char ch) { + return static_cast<char>(tolower(static_cast<unsigned char>(ch))); +} +inline char ToUpper(char ch) { + return static_cast<char>(toupper(static_cast<unsigned char>(ch))); +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int IsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +// Functions deprecated by MSVC 8.0. + +#ifdef _MSC_VER +// Temporarily disable warning 4996 (deprecated function). +# pragma warning(push) +# pragma warning(disable:4996) +#endif + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast<int>(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast<int>(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE + // We are on Windows CE, which has no environment variables. + return NULL; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +void Abort(); +#else +inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast<BiggestInt>(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template <size_t size> +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize<N> with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: + +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::testing::internal::String GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::String GTEST_FLAG(name) = (default_val) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#if GTEST_OS_LINUX +# include <stdlib.h> +# include <sys/types.h> +# include <sys/wait.h> +# include <unistd.h> +#endif // GTEST_OS_LINUX + +#include <ctype.h> +#include <string.h> +#include <iomanip> +#include <limits> +#include <set> + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by <gtest/internal/gtest-internal.h>. +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include <mem.h> +#endif + +#include <string.h> + +#include <string> + +namespace testing { +namespace internal { + +// String - a UTF-8 string class. +// +// For historic reasons, we don't use std::string. +// +// TODO(wan@google.com): replace this class with std::string or +// implement it in terms of the latter. +// +// Note that String can represent both NULL and the empty string, +// while std::string cannot represent NULL. +// +// NULL and the empty string are considered different. NULL is less +// than anything (including the empty string) except itself. +// +// This class only provides minimum functionality necessary for +// implementing Google Test. We do not intend to implement a full-fledged +// string class here. +// +// Since the purpose of this class is to provide a substitute for +// std::string on platforms where it cannot be used, we define a copy +// constructor and assignment operators such that we don't need +// conditional compilation in a lot of places. +// +// In order to make the representation efficient, the d'tor of String +// is not virtual. Therefore DO NOT INHERIT FROM String. +class GTEST_API_ String { + public: + // Static utility methods + + // Returns the input enclosed in double quotes if it's not NULL; + // otherwise returns "(null)". For example, "\"Hello\"" is returned + // for input "Hello". + // + // This is useful for printing a C string in the syntax of a literal. + // + // Known issue: escape sequences are not handled yet. + static String ShowCStringQuoted(const char* c_str); + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static String ShowWideCString(const wchar_t* wide_c_str); + + // Similar to ShowWideCString(), except that this function encloses + // the converted string in double quotes. + static String ShowWideCStringQuoted(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Formats a list of arguments to a String, using the same format + // spec string as for printf. + // + // We do not use the StringPrintf class as it is not universally + // available. + // + // The result is limited to 4096 characters (including the tailing + // 0). If 4096 characters are not enough to format the input, + // "<buffer exceeded>" is returned. + static String Format(const char* format, ...); + + // C'tors + + // The default c'tor constructs a NULL string. + String() : c_str_(NULL), length_(0) {} + + // Constructs a String by cloning a 0-terminated C string. + String(const char* a_c_str) { // NOLINT + if (a_c_str == NULL) { + c_str_ = NULL; + length_ = 0; + } else { + ConstructNonNull(a_c_str, strlen(a_c_str)); + } + } + + // Constructs a String by copying a given number of chars from a + // buffer. E.g. String("hello", 3) creates the string "hel", + // String("a\0bcd", 4) creates "a\0bc", String(NULL, 0) creates "", + // and String(NULL, 1) results in access violation. + String(const char* buffer, size_t a_length) { + ConstructNonNull(buffer, a_length); + } + + // The copy c'tor creates a new copy of the string. The two + // String objects do not share content. + String(const String& str) : c_str_(NULL), length_(0) { *this = str; } + + // D'tor. String is intended to be a final class, so the d'tor + // doesn't need to be virtual. + ~String() { delete[] c_str_; } + + // Allows a String to be implicitly converted to an ::std::string or + // ::string, and vice versa. Converting a String containing a NULL + // pointer to ::std::string or ::string is undefined behavior. + // Converting a ::std::string or ::string containing an embedded NUL + // character to a String will result in the prefix up to the first + // NUL character. + String(const ::std::string& str) { + ConstructNonNull(str.c_str(), str.length()); + } + + operator ::std::string() const { return ::std::string(c_str(), length()); } + +#if GTEST_HAS_GLOBAL_STRING + String(const ::string& str) { + ConstructNonNull(str.c_str(), str.length()); + } + + operator ::string() const { return ::string(c_str(), length()); } +#endif // GTEST_HAS_GLOBAL_STRING + + // Returns true iff this is an empty string (i.e. ""). + bool empty() const { return (c_str() != NULL) && (length() == 0); } + + // Compares this with another String. + // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 + // if this is greater than rhs. + int Compare(const String& rhs) const; + + // Returns true iff this String equals the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator==(const char* a_c_str) const { return Compare(a_c_str) == 0; } + + // Returns true iff this String is less than the given String. A + // NULL string is considered less than "". + bool operator<(const String& rhs) const { return Compare(rhs) < 0; } + + // Returns true iff this String doesn't equal the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator!=(const char* a_c_str) const { return !(*this == a_c_str); } + + // Returns true iff this String ends with the given suffix. *Any* + // String is considered to end with a NULL or empty suffix. + bool EndsWith(const char* suffix) const; + + // Returns true iff this String ends with the given suffix, not considering + // case. Any String is considered to end with a NULL or empty suffix. + bool EndsWithCaseInsensitive(const char* suffix) const; + + // Returns the length of the encapsulated string, or 0 if the + // string is NULL. + size_t length() const { return length_; } + + // Gets the 0-terminated C string this String object represents. + // The String object still owns the string. Therefore the caller + // should NOT delete the return value. + const char* c_str() const { return c_str_; } + + // Assigns a C string to this object. Self-assignment works. + const String& operator=(const char* a_c_str) { + return *this = String(a_c_str); + } + + // Assigns a String object to this object. Self-assignment works. + const String& operator=(const String& rhs) { + if (this != &rhs) { + delete[] c_str_; + if (rhs.c_str() == NULL) { + c_str_ = NULL; + length_ = 0; + } else { + ConstructNonNull(rhs.c_str(), rhs.length()); + } + } + + return *this; + } + + private: + // Constructs a non-NULL String from the given content. This + // function can only be called when c_str_ has not been allocated. + // ConstructNonNull(NULL, 0) results in an empty string (""). + // ConstructNonNull(NULL, non_zero) is undefined behavior. + void ConstructNonNull(const char* buffer, size_t a_length) { + char* const str = new char[a_length + 1]; + memcpy(str, buffer, a_length); + str[a_length] = '\0'; + c_str_ = str; + length_ = a_length; + } + + const char* c_str_; + size_t length_; +}; // class String + +// Streams a String to an ostream. Each '\0' character in the String +// is replaced with "\\0". +inline ::std::ostream& operator<<(::std::ostream& os, const String& str) { + if (str.c_str() == NULL) { + os << "(null)"; + } else { + const char* const c_str = str.c_str(); + for (size_t i = 0; i != str.length(); i++) { + if (c_str[i] == '\0') { + os << "\\0"; + } else { + os << c_str[i]; + } + } + } + return os; +} + +// Gets the content of the stringstream's buffer as a String. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ String StringStreamToString(::std::stringstream* stream); + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". + +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template <typename T> +String StreamableToString(const T& streamable); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in <gtest/internal/gtest-internal.h>. +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const char* pathname) : pathname_(pathname) { + Normalize(); + } + + explicit FilePath(const String& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + String ToString() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_<number>.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true iff the path is NULL or "". + bool IsEmpty() const { return c_str() == NULL || *c_str() == '\0'; } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + String pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# ifdef __GLIBCXX__ +# include <cxxabi.h> +# elif defined(__HP_aCC) +# include <acxx_demangle.h> +# endif // __GLIBCXX__ + +namespace testing { +namespace internal { + +// GetTypeName<T>() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template <typename T> +String GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if defined(__GLIBCXX__) || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# ifdef __GLIBCXX__ + using abi::__cxa_demangle; +# endif // __GLIBCXX__ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const String name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // __GLIBCXX__ || __HP_aCC + +# else + + return "<type>"; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq<T1, T2>::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template <typename T1, typename T2> +struct AssertTypeEq; + +template <typename T> +struct AssertTypeEq<T, T> { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types<int>, Type<int, double>, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN<T1, T2, ..., TN> +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template <typename T1> +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template <typename T1, typename T2> +struct Types2 { + typedef T1 Head; + typedef Types1<T2> Tail; +}; + +template <typename T1, typename T2, typename T3> +struct Types3 { + typedef T1 Head; + typedef Types2<T2, T3> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4> +struct Types4 { + typedef T1 Head; + typedef Types3<T2, T3, T4> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +struct Types5 { + typedef T1 Head; + typedef Types4<T2, T3, T4, T5> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +struct Types6 { + typedef T1 Head; + typedef Types5<T2, T3, T4, T5, T6> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +struct Types7 { + typedef T1 Head; + typedef Types6<T2, T3, T4, T5, T6, T7> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +struct Types8 { + typedef T1 Head; + typedef Types7<T2, T3, T4, T5, T6, T7, T8> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +struct Types9 { + typedef T1 Head; + typedef Types8<T2, T3, T4, T5, T6, T7, T8, T9> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +struct Types10 { + typedef T1 Head; + typedef Types9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11> +struct Types11 { + typedef T1 Head; + typedef Types10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12> +struct Types12 { + typedef T1 Head; + typedef Types11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13> +struct Types13 { + typedef T1 Head; + typedef Types12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14> +struct Types14 { + typedef T1 Head; + typedef Types13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15> +struct Types15 { + typedef T1 Head; + typedef Types14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16> +struct Types16 { + typedef T1 Head; + typedef Types15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17> +struct Types17 { + typedef T1 Head; + typedef Types16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18> +struct Types18 { + typedef T1 Head; + typedef Types17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19> +struct Types19 { + typedef T1 Head; + typedef Types18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20> +struct Types20 { + typedef T1 Head; + typedef Types19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21> +struct Types21 { + typedef T1 Head; + typedef Types20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22> +struct Types22 { + typedef T1 Head; + typedef Types21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23> +struct Types23 { + typedef T1 Head; + typedef Types22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24> +struct Types24 { + typedef T1 Head; + typedef Types23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25> +struct Types25 { + typedef T1 Head; + typedef Types24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26> +struct Types26 { + typedef T1 Head; + typedef Types25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27> +struct Types27 { + typedef T1 Head; + typedef Types26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28> +struct Types28 { + typedef T1 Head; + typedef Types27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29> +struct Types29 { + typedef T1 Head; + typedef Types28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30> +struct Types30 { + typedef T1 Head; + typedef Types29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31> +struct Types31 { + typedef T1 Head; + typedef Types30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32> +struct Types32 { + typedef T1 Head; + typedef Types31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33> +struct Types33 { + typedef T1 Head; + typedef Types32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34> +struct Types34 { + typedef T1 Head; + typedef Types33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35> +struct Types35 { + typedef T1 Head; + typedef Types34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36> +struct Types36 { + typedef T1 Head; + typedef Types35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37> +struct Types37 { + typedef T1 Head; + typedef Types36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38> +struct Types38 { + typedef T1 Head; + typedef Types37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39> +struct Types39 { + typedef T1 Head; + typedef Types38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40> +struct Types40 { + typedef T1 Head; + typedef Types39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41> +struct Types41 { + typedef T1 Head; + typedef Types40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42> +struct Types42 { + typedef T1 Head; + typedef Types41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43> +struct Types43 { + typedef T1 Head; + typedef Types42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44> +struct Types44 { + typedef T1 Head; + typedef Types43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45> +struct Types45 { + typedef T1 Head; + typedef Types44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46> +struct Types46 { + typedef T1 Head; + typedef Types45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47> +struct Types47 { + typedef T1 Head; + typedef Types46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48> +struct Types48 { + typedef T1 Head; + typedef Types47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49> +struct Types49 { + typedef T1 Head; + typedef Types48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48, T49> Tail; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49, typename T50> +struct Types50 { + typedef T1 Head; + typedef Types49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48, T49, T50> Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types<int> +// will appear as Types<int, None, None, ..., None> in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types<T1, ..., TN>, and Google Test will translate +// that to TypesN<T1, ..., TN> internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template <typename T1 = internal::None, typename T2 = internal::None, + typename T3 = internal::None, typename T4 = internal::None, + typename T5 = internal::None, typename T6 = internal::None, + typename T7 = internal::None, typename T8 = internal::None, + typename T9 = internal::None, typename T10 = internal::None, + typename T11 = internal::None, typename T12 = internal::None, + typename T13 = internal::None, typename T14 = internal::None, + typename T15 = internal::None, typename T16 = internal::None, + typename T17 = internal::None, typename T18 = internal::None, + typename T19 = internal::None, typename T20 = internal::None, + typename T21 = internal::None, typename T22 = internal::None, + typename T23 = internal::None, typename T24 = internal::None, + typename T25 = internal::None, typename T26 = internal::None, + typename T27 = internal::None, typename T28 = internal::None, + typename T29 = internal::None, typename T30 = internal::None, + typename T31 = internal::None, typename T32 = internal::None, + typename T33 = internal::None, typename T34 = internal::None, + typename T35 = internal::None, typename T36 = internal::None, + typename T37 = internal::None, typename T38 = internal::None, + typename T39 = internal::None, typename T40 = internal::None, + typename T41 = internal::None, typename T42 = internal::None, + typename T43 = internal::None, typename T44 = internal::None, + typename T45 = internal::None, typename T46 = internal::None, + typename T47 = internal::None, typename T48 = internal::None, + typename T49 = internal::None, typename T50 = internal::None> +struct Types { + typedef internal::Types50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46, T47, T48, T49, T50> type; +}; + +template <> +struct Types<internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types0 type; +}; +template <typename T1> +struct Types<T1, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types1<T1> type; +}; +template <typename T1, typename T2> +struct Types<T1, T2, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types2<T1, T2> type; +}; +template <typename T1, typename T2, typename T3> +struct Types<T1, T2, T3, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types3<T1, T2, T3> type; +}; +template <typename T1, typename T2, typename T3, typename T4> +struct Types<T1, T2, T3, T4, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types4<T1, T2, T3, T4> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5> +struct Types<T1, T2, T3, T4, T5, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types5<T1, T2, T3, T4, T5> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +struct Types<T1, T2, T3, T4, T5, T6, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types6<T1, T2, T3, T4, T5, T6> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +struct Types<T1, T2, T3, T4, T5, T6, T7, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types7<T1, T2, T3, T4, T5, T6, T7> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types8<T1, T2, T3, T4, T5, T6, T7, T8> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, internal::None, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None, internal::None> { + typedef internal::Types43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + internal::None, internal::None, internal::None, internal::None, + internal::None, internal::None> { + typedef internal::Types44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, + internal::None, internal::None, internal::None, internal::None, + internal::None> { + typedef internal::Types45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, + T46, internal::None, internal::None, internal::None, internal::None> { + typedef internal::Types46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, + T46, T47, internal::None, internal::None, internal::None> { + typedef internal::Types47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46, T47> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, + T46, T47, T48, internal::None, internal::None> { + typedef internal::Types48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46, T47, T48> type; +}; +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49> +struct Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, + T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, + T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, + T46, T47, T48, T49, internal::None> { + typedef internal::Types49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46, T47, T48, T49> type; +}; + +namespace internal { + +# define GTEST_TEMPLATE_ template <typename T> class + +// The template "selector" struct TemplateSel<Tmpl> is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined +// as the type Tmpl<T>. This allows us to actually instantiate the +// template "selected" by TemplateSel<Tmpl>. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template <GTEST_TEMPLATE_ Tmpl> +struct TemplateSel { + template <typename T> + struct Bind { + typedef Tmpl<T> type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind<T>::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates<int>, Templates<int, double>, +// and etc), which C++ doesn't support directly. +template <typename T> +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN<T1, T2, ..., +// TN> represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template <GTEST_TEMPLATE_ T1> +struct Templates1 { + typedef TemplateSel<T1> Head; + typedef Templates0 Tail; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2> +struct Templates2 { + typedef TemplateSel<T1> Head; + typedef Templates1<T2> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3> +struct Templates3 { + typedef TemplateSel<T1> Head; + typedef Templates2<T2, T3> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4> +struct Templates4 { + typedef TemplateSel<T1> Head; + typedef Templates3<T2, T3, T4> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5> +struct Templates5 { + typedef TemplateSel<T1> Head; + typedef Templates4<T2, T3, T4, T5> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6> +struct Templates6 { + typedef TemplateSel<T1> Head; + typedef Templates5<T2, T3, T4, T5, T6> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7> +struct Templates7 { + typedef TemplateSel<T1> Head; + typedef Templates6<T2, T3, T4, T5, T6, T7> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8> +struct Templates8 { + typedef TemplateSel<T1> Head; + typedef Templates7<T2, T3, T4, T5, T6, T7, T8> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9> +struct Templates9 { + typedef TemplateSel<T1> Head; + typedef Templates8<T2, T3, T4, T5, T6, T7, T8, T9> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10> +struct Templates10 { + typedef TemplateSel<T1> Head; + typedef Templates9<T2, T3, T4, T5, T6, T7, T8, T9, T10> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11> +struct Templates11 { + typedef TemplateSel<T1> Head; + typedef Templates10<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12> +struct Templates12 { + typedef TemplateSel<T1> Head; + typedef Templates11<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13> +struct Templates13 { + typedef TemplateSel<T1> Head; + typedef Templates12<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14> +struct Templates14 { + typedef TemplateSel<T1> Head; + typedef Templates13<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15> +struct Templates15 { + typedef TemplateSel<T1> Head; + typedef Templates14<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16> +struct Templates16 { + typedef TemplateSel<T1> Head; + typedef Templates15<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17> +struct Templates17 { + typedef TemplateSel<T1> Head; + typedef Templates16<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18> +struct Templates18 { + typedef TemplateSel<T1> Head; + typedef Templates17<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19> +struct Templates19 { + typedef TemplateSel<T1> Head; + typedef Templates18<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20> +struct Templates20 { + typedef TemplateSel<T1> Head; + typedef Templates19<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21> +struct Templates21 { + typedef TemplateSel<T1> Head; + typedef Templates20<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22> +struct Templates22 { + typedef TemplateSel<T1> Head; + typedef Templates21<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23> +struct Templates23 { + typedef TemplateSel<T1> Head; + typedef Templates22<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24> +struct Templates24 { + typedef TemplateSel<T1> Head; + typedef Templates23<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25> +struct Templates25 { + typedef TemplateSel<T1> Head; + typedef Templates24<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26> +struct Templates26 { + typedef TemplateSel<T1> Head; + typedef Templates25<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27> +struct Templates27 { + typedef TemplateSel<T1> Head; + typedef Templates26<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28> +struct Templates28 { + typedef TemplateSel<T1> Head; + typedef Templates27<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29> +struct Templates29 { + typedef TemplateSel<T1> Head; + typedef Templates28<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30> +struct Templates30 { + typedef TemplateSel<T1> Head; + typedef Templates29<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31> +struct Templates31 { + typedef TemplateSel<T1> Head; + typedef Templates30<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32> +struct Templates32 { + typedef TemplateSel<T1> Head; + typedef Templates31<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33> +struct Templates33 { + typedef TemplateSel<T1> Head; + typedef Templates32<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34> +struct Templates34 { + typedef TemplateSel<T1> Head; + typedef Templates33<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35> +struct Templates35 { + typedef TemplateSel<T1> Head; + typedef Templates34<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36> +struct Templates36 { + typedef TemplateSel<T1> Head; + typedef Templates35<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37> +struct Templates37 { + typedef TemplateSel<T1> Head; + typedef Templates36<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38> +struct Templates38 { + typedef TemplateSel<T1> Head; + typedef Templates37<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39> +struct Templates39 { + typedef TemplateSel<T1> Head; + typedef Templates38<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40> +struct Templates40 { + typedef TemplateSel<T1> Head; + typedef Templates39<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41> +struct Templates41 { + typedef TemplateSel<T1> Head; + typedef Templates40<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42> +struct Templates42 { + typedef TemplateSel<T1> Head; + typedef Templates41<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43> +struct Templates43 { + typedef TemplateSel<T1> Head; + typedef Templates42<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44> +struct Templates44 { + typedef TemplateSel<T1> Head; + typedef Templates43<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45> +struct Templates45 { + typedef TemplateSel<T1> Head; + typedef Templates44<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46> +struct Templates46 { + typedef TemplateSel<T1> Head; + typedef Templates45<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45, T46> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47> +struct Templates47 { + typedef TemplateSel<T1> Head; + typedef Templates46<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45, T46, T47> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48> +struct Templates48 { + typedef TemplateSel<T1> Head; + typedef Templates47<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45, T46, T47, T48> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48, + GTEST_TEMPLATE_ T49> +struct Templates49 { + typedef TemplateSel<T1> Head; + typedef Templates48<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45, T46, T47, T48, T49> Tail; +}; + +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48, + GTEST_TEMPLATE_ T49, GTEST_TEMPLATE_ T50> +struct Templates50 { + typedef TemplateSel<T1> Head; + typedef Templates49<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43, T44, T45, T46, T47, T48, T49, T50> Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates<list> +// will appear as Templates<list, NoneT, NoneT, ..., NoneT> in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates<T1, ..., TN>, and Google Test will translate +// that to TemplatesN<T1, ..., TN> internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template <GTEST_TEMPLATE_ T1 = NoneT, GTEST_TEMPLATE_ T2 = NoneT, + GTEST_TEMPLATE_ T3 = NoneT, GTEST_TEMPLATE_ T4 = NoneT, + GTEST_TEMPLATE_ T5 = NoneT, GTEST_TEMPLATE_ T6 = NoneT, + GTEST_TEMPLATE_ T7 = NoneT, GTEST_TEMPLATE_ T8 = NoneT, + GTEST_TEMPLATE_ T9 = NoneT, GTEST_TEMPLATE_ T10 = NoneT, + GTEST_TEMPLATE_ T11 = NoneT, GTEST_TEMPLATE_ T12 = NoneT, + GTEST_TEMPLATE_ T13 = NoneT, GTEST_TEMPLATE_ T14 = NoneT, + GTEST_TEMPLATE_ T15 = NoneT, GTEST_TEMPLATE_ T16 = NoneT, + GTEST_TEMPLATE_ T17 = NoneT, GTEST_TEMPLATE_ T18 = NoneT, + GTEST_TEMPLATE_ T19 = NoneT, GTEST_TEMPLATE_ T20 = NoneT, + GTEST_TEMPLATE_ T21 = NoneT, GTEST_TEMPLATE_ T22 = NoneT, + GTEST_TEMPLATE_ T23 = NoneT, GTEST_TEMPLATE_ T24 = NoneT, + GTEST_TEMPLATE_ T25 = NoneT, GTEST_TEMPLATE_ T26 = NoneT, + GTEST_TEMPLATE_ T27 = NoneT, GTEST_TEMPLATE_ T28 = NoneT, + GTEST_TEMPLATE_ T29 = NoneT, GTEST_TEMPLATE_ T30 = NoneT, + GTEST_TEMPLATE_ T31 = NoneT, GTEST_TEMPLATE_ T32 = NoneT, + GTEST_TEMPLATE_ T33 = NoneT, GTEST_TEMPLATE_ T34 = NoneT, + GTEST_TEMPLATE_ T35 = NoneT, GTEST_TEMPLATE_ T36 = NoneT, + GTEST_TEMPLATE_ T37 = NoneT, GTEST_TEMPLATE_ T38 = NoneT, + GTEST_TEMPLATE_ T39 = NoneT, GTEST_TEMPLATE_ T40 = NoneT, + GTEST_TEMPLATE_ T41 = NoneT, GTEST_TEMPLATE_ T42 = NoneT, + GTEST_TEMPLATE_ T43 = NoneT, GTEST_TEMPLATE_ T44 = NoneT, + GTEST_TEMPLATE_ T45 = NoneT, GTEST_TEMPLATE_ T46 = NoneT, + GTEST_TEMPLATE_ T47 = NoneT, GTEST_TEMPLATE_ T48 = NoneT, + GTEST_TEMPLATE_ T49 = NoneT, GTEST_TEMPLATE_ T50 = NoneT> +struct Templates { + typedef Templates50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45, T46, T47, T48, T49, T50> type; +}; + +template <> +struct Templates<NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT> { + typedef Templates0 type; +}; +template <GTEST_TEMPLATE_ T1> +struct Templates<T1, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT> { + typedef Templates1<T1> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2> +struct Templates<T1, T2, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT> { + typedef Templates2<T1, T2> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3> +struct Templates<T1, T2, T3, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates3<T1, T2, T3> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4> +struct Templates<T1, T2, T3, T4, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates4<T1, T2, T3, T4> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5> +struct Templates<T1, T2, T3, T4, T5, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates5<T1, T2, T3, T4, T5> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6> +struct Templates<T1, T2, T3, T4, T5, T6, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates6<T1, T2, T3, T4, T5, T6> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7> +struct Templates<T1, T2, T3, T4, T5, T6, T7, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates7<T1, T2, T3, T4, T5, T6, T7> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates8<T1, T2, T3, T4, T5, T6, T7, T8> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates9<T1, T2, T3, T4, T5, T6, T7, T8, T9> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT> { + typedef Templates18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT> { + typedef Templates19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT> { + typedef Templates20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT> { + typedef Templates21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT> { + typedef Templates22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT> { + typedef Templates23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT> { + typedef Templates24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT> { + typedef Templates25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT> { + typedef Templates26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT> { + typedef Templates27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT> { + typedef Templates28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT> { + typedef Templates29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, NoneT, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, NoneT, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, NoneT, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, NoneT, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, NoneT, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + NoneT, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + T45, NoneT, NoneT, NoneT, NoneT, NoneT> { + typedef Templates45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + T45, T46, NoneT, NoneT, NoneT, NoneT> { + typedef Templates46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45, T46> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + T45, T46, T47, NoneT, NoneT, NoneT> { + typedef Templates47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45, T46, T47> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + T45, T46, T47, T48, NoneT, NoneT> { + typedef Templates48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45, T46, T47, T48> type; +}; +template <GTEST_TEMPLATE_ T1, GTEST_TEMPLATE_ T2, GTEST_TEMPLATE_ T3, + GTEST_TEMPLATE_ T4, GTEST_TEMPLATE_ T5, GTEST_TEMPLATE_ T6, + GTEST_TEMPLATE_ T7, GTEST_TEMPLATE_ T8, GTEST_TEMPLATE_ T9, + GTEST_TEMPLATE_ T10, GTEST_TEMPLATE_ T11, GTEST_TEMPLATE_ T12, + GTEST_TEMPLATE_ T13, GTEST_TEMPLATE_ T14, GTEST_TEMPLATE_ T15, + GTEST_TEMPLATE_ T16, GTEST_TEMPLATE_ T17, GTEST_TEMPLATE_ T18, + GTEST_TEMPLATE_ T19, GTEST_TEMPLATE_ T20, GTEST_TEMPLATE_ T21, + GTEST_TEMPLATE_ T22, GTEST_TEMPLATE_ T23, GTEST_TEMPLATE_ T24, + GTEST_TEMPLATE_ T25, GTEST_TEMPLATE_ T26, GTEST_TEMPLATE_ T27, + GTEST_TEMPLATE_ T28, GTEST_TEMPLATE_ T29, GTEST_TEMPLATE_ T30, + GTEST_TEMPLATE_ T31, GTEST_TEMPLATE_ T32, GTEST_TEMPLATE_ T33, + GTEST_TEMPLATE_ T34, GTEST_TEMPLATE_ T35, GTEST_TEMPLATE_ T36, + GTEST_TEMPLATE_ T37, GTEST_TEMPLATE_ T38, GTEST_TEMPLATE_ T39, + GTEST_TEMPLATE_ T40, GTEST_TEMPLATE_ T41, GTEST_TEMPLATE_ T42, + GTEST_TEMPLATE_ T43, GTEST_TEMPLATE_ T44, GTEST_TEMPLATE_ T45, + GTEST_TEMPLATE_ T46, GTEST_TEMPLATE_ T47, GTEST_TEMPLATE_ T48, + GTEST_TEMPLATE_ T49> +struct Templates<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, + T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, + T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, + T45, T46, T47, T48, T49, NoneT> { + typedef Templates49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42, T43, T44, T45, T46, T47, T48, T49> type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template <typename T> +struct TypeList { typedef Types1<T> type; }; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49, typename T50> +struct TypeList<Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48, T49, T50> > { + typedef typename Types<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +// Google Test defines the testing::Message class to allow construction of +// test messages via the << operator. The idea is that anything +// streamable to std::ostream can be streamed to a testing::Message. +// This allows a user to use his own types in Google Test assertions by +// overloading the << operator. +// +// util/gtl/stl_logging-inl.h overloads << for STL containers. These +// overloads cannot be defined in the std namespace, as that will be +// undefined behavior. Therefore, they are defined in the global +// namespace instead. +// +// C++'s symbol lookup rule (i.e. Koenig lookup) says that these +// overloads are visible in either the std namespace or the global +// namespace, but not other namespaces, including the testing +// namespace which Google Test's Message class is in. +// +// To allow STL containers (and other types that has a << operator +// defined in the global namespace) to be used in Google Test assertions, +// testing::Message must access the custom << operator from the global +// namespace. Hence this helper function. +// +// Note: Jeffrey Yasskin suggested an alternative fix by "using +// ::operator<<;" in the definition of Message's operator<<. That fix +// doesn't require a helper function, but unfortunately doesn't +// compile with MSVC. +template <typename T> +inline void GTestStreamToHelper(std::ostream* os, const T& val) { + *os << val; +} + +class ProtocolMessage; +namespace proto2 { class Message; } + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test cases. + +template <typename T> +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// How many times InitGoogleTest() has been called. +extern int g_init_gtest_count; + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ String AppendUserMessage(const String& gtest_msg, + const Message& user_msg); + +// A helper class for creating scoped traces in user programs. +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template <typename T> +String StreamableToString(const T& streamable); + +// The Symbian compiler has a bug that prevents it from selecting the +// correct overload of FormatForComparisonFailureMessage (see below) +// unless we pass the first argument by reference. If we do that, +// however, Visual Age C++ 10.1 generates a compiler error. Therefore +// we only apply the work-around for Symbian. +#if defined(__SYMBIAN32__) +# define GTEST_CREF_WORKAROUND_ const& +#else +# define GTEST_CREF_WORKAROUND_ +#endif + +// When this operand is a const char* or char*, if the other operand +// is a ::std::string or ::string, we print this operand as a C string +// rather than a pointer (we do the same for wide strings); otherwise +// we print it as a pointer to be safe. + +// This internal macro is used to avoid duplicated code. +#define GTEST_FORMAT_IMPL_(operand2_type, operand1_printer)\ +inline String FormatForComparisonFailureMessage(\ + operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ + const operand2_type& /*operand2*/) {\ + return operand1_printer(str);\ +}\ +inline String FormatForComparisonFailureMessage(\ + const operand2_type::value_type* GTEST_CREF_WORKAROUND_ str, \ + const operand2_type& /*operand2*/) {\ + return operand1_printer(str);\ +} + +GTEST_FORMAT_IMPL_(::std::string, String::ShowCStringQuoted) +#if GTEST_HAS_STD_WSTRING +GTEST_FORMAT_IMPL_(::std::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_STRING +GTEST_FORMAT_IMPL_(::string, String::ShowCStringQuoted) +#endif // GTEST_HAS_GLOBAL_STRING +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_FORMAT_IMPL_(::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_GLOBAL_WSTRING + +#undef GTEST_FORMAT_IMPL_ + +// The next four overloads handle the case where the operand being +// printed is a char/wchar_t pointer and the other operand is not a +// string/wstring object. In such cases, we just print the operand as +// a pointer to be safe. +#define GTEST_FORMAT_CHAR_PTR_IMPL_(CharType) \ + template <typename T> \ + String FormatForComparisonFailureMessage(CharType* GTEST_CREF_WORKAROUND_ p, \ + const T&) { \ + return PrintToString(static_cast<const void*>(p)); \ + } + +GTEST_FORMAT_CHAR_PTR_IMPL_(char) +GTEST_FORMAT_CHAR_PTR_IMPL_(const char) +GTEST_FORMAT_CHAR_PTR_IMPL_(wchar_t) +GTEST_FORMAT_CHAR_PTR_IMPL_(const wchar_t) + +#undef GTEST_FORMAT_CHAR_PTR_IMPL_ + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ String GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template <typename RawType> +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits<RawType>::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast<Bits>(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint<float> Float; +typedef FloatingPoint<double> Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template <typename T> +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper<T>::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template <typename T> +bool TypeIdHelper<T>::dummy_ = false; + +// GetTypeId<T>() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template <typename T> +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper<T>::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper<T>::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template <class TestClass> +class TestFactoryImpl : public TestFactoryBase { + public: + virtual Test* CreateTest() { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set<const char*> defined_test_names_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline String GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? String(str) : String(str, comma - str); +} + +// TypeParameterizedTest<Fixture, TestSel, Types>::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types> +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + typedef typename Types::Head Type; + typedef Fixture<Type> FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + String::Format("%s%s%s/%d", prefix, prefix[0] == '\0' ? "" : "/", + case_name, index).c_str(), + GetPrefixUntilComma(test_names).c_str(), + GetTypeName<Type>().c_str(), + NULL, // No value parameter. + GetTypeId<FixtureClass>(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl<TestClass>); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest<Fixture, TestSel, typename Types::Tail> + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template <GTEST_TEMPLATE_ Fixture, class TestSel> +class TypeParameterizedTest<Fixture, TestSel, Types0> { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase<Fixture, Tests, Types>::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types> +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest<Fixture, Head, Types>::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase<Fixture, typename Tests::Tail, Types> + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template <GTEST_TEMPLATE_ Fixture, typename Types> +class TypeParameterizedTestCase<Fixture, Templates0, Types> { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// Returns the current OS stack trace as a String. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ String GetCurrentOsStackTraceExceptTop(UnitTest* unit_test, + int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a +// compiler error iff T1 and T2 are different types. +template <typename T1, typename T2> +struct CompileAssertTypesEqual; + +template <typename T> +struct CompileAssertTypesEqual<T, T> { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template <typename T> +struct RemoveReference { typedef T type; }; // NOLINT +template <typename T> +struct RemoveReference<T&> { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference<T>::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template <typename T> +struct RemoveConst { typedef T type; }; // NOLINT +template <typename T> +struct RemoveConst<const T> { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +// However, it causes trouble with GCC and thus needs to be +// conditionally compiled. +#if defined(_MSC_VER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) +template <typename T, size_t N> +struct RemoveConst<const T[N]> { + typedef typename RemoveConst<T>::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst<T>::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template <typename T> +struct AddReference { typedef T& type; }; // NOLINT +template <typename T> +struct AddReference<T&> { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference<T>::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible<From, To>::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template <typename From, typename To> +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template <typename From, typename To> +const bool ImplicitlyConvertible<From, To>::value; + +// IsAProtocolMessage<T>::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template <typename T> +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value || + ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> { +}; + +// When the compiler sees expression IsContainerTest<C>(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest<C>(0). +// The value of the expression is insignificant. +// +// Note that we look for both C::iterator and C::const_iterator. The +// reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template <class C> +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template <class C> +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf<condition>::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf<expression>::type* = 0" as the last parameter. +template<bool> struct EnableIf; +template<> struct EnableIf<true> { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template <typename T, typename U> +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template <typename Iter, typename Element> +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template <typename T, typename U> +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template <typename T, typename U, size_t N> +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template <typename T, typename U> +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template <typename Element> +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast<void>(StaticAssertTypeEqHelper<Element, + GTEST_REMOVE_REFERENCE_AND_CONST_(Element)>()); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + catch (...) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// represenation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + + +#include <stdio.h> + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const String& message); + + private: + // A string containing a description of the outcome of the last death test. + static String last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const String& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + String file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + String file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#else // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i); +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the <regex.h> library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// +// TODO(wan@google.com): make thread-safe death tests search the PATH. + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (::testing::internal::AlwaysFalse()) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (::testing::internal::AlwaysFalse()) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) +#endif + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include <limits> + + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + // We allocate the stringstream separately because otherwise each use of + // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's + // stack frame leading to huge stack frames in some cases; gcc does not reuse + // the stack space. + Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits<double>::digits10 + 2); + } + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template <typename T> + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer<T>::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template <typename T> + inline Message& operator <<(const T& val) { + ::GTestStreamToHelper(ss_.get(), val); + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template <typename T> + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_.get(), pointer); + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + Message& operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as a String. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::String GetString() const { + return internal::StringStreamToString(ss_.get()); + } + + private: + +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template <typename T> + inline void StreamHelper(internal::true_type /*dummy*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_.get(), pointer); + } + } + template <typename T> + inline void StreamHelper(internal::false_type /*dummy*/, const T& value) { + ::GTestStreamToHelper(ss_.get(), value); + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam<T> (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam<T> is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam<const char*> { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam<T> class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface<T>, where T is the type of the parameter +// values. Inheriting from TestWithParam<T> satisfies that requirement because +// TestWithParam<T> inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + + +#if !GTEST_OS_SYMBIAN +# include <utility> +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include <iterator> +#include <utility> +#include <vector> + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2003 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include <stdlib.h> +#include <assert.h> + + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and + // linked_ptr<Derived2>). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + // L < g_linked_ptr_mutex + void join(linked_ptr_internal const* ptr) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + // L < g_linked_ptr_mutex + bool depart() { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template <typename T> +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template <typename U> + bool operator==(linked_ptr<U> const& ptr) const { + return value_ == ptr.get(); + } + template <typename U> + bool operator!=(linked_ptr<U> const& ptr) const { + return value_ != ptr.get(); + } + + private: + template <typename U> + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template <typename U> void copy(linked_ptr<U> const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template<typename T> inline +bool operator==(T* ptr, const linked_ptr<T>& x) { + return ptr == x.get(); +} + +template<typename T> inline +bool operator!=(T* ptr, const linked_ptr<T>& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr<T> +// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation +// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg)) +template <typename T> +linked_ptr<T> make_linked_ptr(T* ptr) { + return linked_ptr<T>(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector<string> UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include <ostream> // NOLINT +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template <typename T, TypeKind kTypeKind> +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template <typename T> +class TypeWithoutFormatter<T, kProtobuf> { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template <typename T> +class TypeWithoutFormatter<T, kConvertibleToInteger> { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter<T>::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream<Char, +// CharTraits> type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream<Char, +// CharTraits>, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more +// specific. +template <typename Char, typename CharTraits, typename T> +::std::basic_ostream<Char, CharTraits>& operator<<( + ::std::basic_ostream<Char, CharTraits>& os, const T& x) { + TypeWithoutFormatter<T, + (internal::IsAProtocolMessage<T>::value ? kProtobuf : + internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template <typename T> +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template <typename T> +class UniversalPrinter; + +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template <typename C> +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template <typename T> +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast<const void*>( + reinterpret_cast<internal::UInt64>(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template <typename T> +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter<T>::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template <typename T> +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter<T>::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast<unsigned char>(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const char*>(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const void*>(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_<const wchar_t*>(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template <typename T> +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template <typename T> +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1> +void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2> +void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +void PrintTo( + const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// Overload for std::pair. +template <typename T1, typename T2> +void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter<T1>::Print(value.first, os); + *os << ", "; + UniversalPrinter<T2>::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template <typename T> +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template <typename T> +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray(const char* begin, + size_t len, + ::std::ostream* os); + +// Implements printing an array type T[N]. +template <typename T, size_t N> +class UniversalPrinter<T[N]> { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template <typename T> +class UniversalPrinter<T&> { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast<const void*>(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. +template <typename T> +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); +} +inline void UniversalTersePrint(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } +} +inline void UniversalTersePrint(char* str, ::std::ostream* os) { + UniversalTersePrint(static_cast<const char*>(str), os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template <typename T> +void UniversalPrint(const T& value, ::std::ostream* os) { + UniversalPrinter<T>::Print(value, os); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector<string> Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter<N - 1>. + +// The inductive case. +template <size_t N> +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template <typename Tuple> + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type> + ::Print(::std::tr1::get<N - 1>(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template <typename Tuple> + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template <typename Tuple> + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template <typename Tuple> + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template <typename T> +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template <typename Tuple> +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template <typename T> +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrint(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line); + +template <typename> class ParamGeneratorInterface; +template <typename> class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface<T>. +template <typename T> +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator<T>. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator<T>::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator<T>::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T> +// and implements the const forward iterator concept. +template <typename T> +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface<T>* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator<T>; + explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {} + scoped_ptr<ParamIteratorInterface<T> > impl_; +}; + +// ParamGeneratorInterface<T> is the binary interface to access generators +// defined in other translation units. +template <typename T> +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface<T>* Begin() const = 0; + virtual ParamIteratorInterface<T>* End() const = 0; +}; + +// Wraps ParamGeneratorInterface<T> and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface<T> instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template<typename T> +class ParamGenerator { + public: + typedef ParamIterator<T> iterator; + + explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + linked_ptr<const ParamGeneratorInterface<T> > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template <typename T, typename IncrementT> +class RangeGenerator : public ParamGeneratorInterface<T> { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface<T>* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface<T>* End() const { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface<T> { + public: + Iterator(const ParamGeneratorInterface<T>* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<T>* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = value_ + step_; + index_++; + } + virtual ParamIteratorInterface<T>* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface<T>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType<const Iterator>(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface<T>(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<T>* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = i + step) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template <typename T> +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> { + public: + template <typename ForwardIterator> + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface<T>* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface<T>* End() const { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector<T> ContainerType; + + class Iterator : public ParamIteratorInterface<T> { + public: + Iterator(const ParamGeneratorInterface<T>* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<T>* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface<T>* Clone() const { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface<T>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType<const Iterator>(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface<T>(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface<T>* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_ptr<const T> value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template <class TestClass> +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + virtual Test* CreateTest() { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template <class ParamType> +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template <class TestCase> +class TestMetaFactory + : public TestMetaFactoryBase<typename TestCase::ParamType> { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory<TestCase>(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template <class TestCase> +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator<ParamType>(GeneratorCreationFunc)(); + + explicit ParameterizedTestCaseInfo(const char* name) + : test_case_name_(name) {} + + // Test case base name for display purposes. + virtual const string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId<TestCase>(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase<ParamType>* meta_factory) { + tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const string& instantiation_name, + GeneratorCreationFunc* func, + const char* /* file */, + int /* line */) { + instantiations_.push_back(::std::make_pair(instantiation_name, func)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr<TestInfo> test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const string& instantiation_name = gen_it->first; + ParamGenerator<ParamType> generator((*gen_it->second)()); + + Message test_case_name_stream; + if ( !instantiation_name.empty() ) + test_case_name_stream << instantiation_name << "/"; + test_case_name_stream << test_info->test_case_base_name; + + int i = 0; + for (typename ParamGenerator<ParamType>::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( + test_case_name_stream.GetString().c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase<ParamType>* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const string test_case_base_name; + const string test_base_name; + const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; + }; + typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer; + // Keeps pairs of <Instantiation name, Sequence generator creation function> + // received from INSTANTIATE_TEST_CASE_P macros. + typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> > + InstantiationContainer; + + const string test_case_name_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template <class TestCase> + ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( + const char* test_case_name, + const char* file, + int line) { + ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, file, line); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestCaseInfo<TestCase> >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template <typename ForwardIterator> +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template <typename T, size_t N> +internal::ParamGenerator<T> ValuesIn(const T (&array)[N]); + +template <class Container> +internal::ParamGenerator<typename Container::value_type> ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template <typename T1> +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template <typename T> + operator ParamGenerator<T>() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template <typename T1, typename T2> +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template <typename T1, typename T2, typename T3> +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template <typename T1, typename T2, typename T3, typename T4> +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11> +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12> +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13> +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14> +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15> +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16> +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17> +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18> +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19> +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20> +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21> +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22> +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23> +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, + v23_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24> +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25> +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26> +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27> +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28> +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29> +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30> +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31> +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32> +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33> +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34> +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35> +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, + v35_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36> +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37> +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38> +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39> +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40> +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41> +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42> +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43> +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44> +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45> +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46> +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47> +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, + v47_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48> +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, + v48_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49> +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, + v48_, v49_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49, typename T50> +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template <typename T> + operator ParamGenerator<T>() const { + const T array[] = {v1_, v2_, v3_, v4_, v5_, v6_, v7_, v8_, v9_, v10_, v11_, + v12_, v13_, v14_, v15_, v16_, v17_, v18_, v19_, v20_, v21_, v22_, v23_, + v24_, v25_, v26_, v27_, v28_, v29_, v30_, v31_, v32_, v33_, v34_, v35_, + v36_, v37_, v38_, v39_, v40_, v41_, v42_, v43_, v44_, v45_, v46_, v47_, + v48_, v49_, v50_}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template <typename T1, typename T2> +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2> > { + public: + typedef ::std::tr1::tuple<T1, T2> ParamType; + + CartesianProductGenerator2(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + ParamType current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; +}; // class CartesianProductGenerator2 + + +template <typename T1, typename T2, typename T3> +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3> ParamType; + + CartesianProductGenerator3(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + ParamType current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; +}; // class CartesianProductGenerator3 + + +template <typename T1, typename T2, typename T3, typename T4> +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4> ParamType; + + CartesianProductGenerator4(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + ParamType current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; +}; // class CartesianProductGenerator4 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5> ParamType; + + CartesianProductGenerator5(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + ParamType current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; +}; // class CartesianProductGenerator5 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, + T6> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> ParamType; + + CartesianProductGenerator6(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5, + const ParamGenerator<T6>& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5, + const ParamGenerator<T6>& g6, + const typename ParamGenerator<T6>::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + const typename ParamGenerator<T6>::iterator begin6_; + const typename ParamGenerator<T6>::iterator end6_; + typename ParamGenerator<T6>::iterator current6_; + ParamType current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; + const ParamGenerator<T6> g6_; +}; // class CartesianProductGenerator6 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, + T7> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> ParamType; + + CartesianProductGenerator7(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5, + const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5, + const ParamGenerator<T6>& g6, + const typename ParamGenerator<T6>::iterator& current6, + const ParamGenerator<T7>& g7, + const typename ParamGenerator<T7>::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + const typename ParamGenerator<T6>::iterator begin6_; + const typename ParamGenerator<T6>::iterator end6_; + typename ParamGenerator<T6>::iterator current6_; + const typename ParamGenerator<T7>::iterator begin7_; + const typename ParamGenerator<T7>::iterator end7_; + typename ParamGenerator<T7>::iterator current7_; + ParamType current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; + const ParamGenerator<T6> g6_; + const ParamGenerator<T7> g7_; +}; // class CartesianProductGenerator7 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, + T7, T8> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> ParamType; + + CartesianProductGenerator8(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5, + const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7, + const ParamGenerator<T8>& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5, + const ParamGenerator<T6>& g6, + const typename ParamGenerator<T6>::iterator& current6, + const ParamGenerator<T7>& g7, + const typename ParamGenerator<T7>::iterator& current7, + const ParamGenerator<T8>& g8, + const typename ParamGenerator<T8>::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + const typename ParamGenerator<T6>::iterator begin6_; + const typename ParamGenerator<T6>::iterator end6_; + typename ParamGenerator<T6>::iterator current6_; + const typename ParamGenerator<T7>::iterator begin7_; + const typename ParamGenerator<T7>::iterator end7_; + typename ParamGenerator<T7>::iterator current7_; + const typename ParamGenerator<T8>::iterator begin8_; + const typename ParamGenerator<T8>::iterator end8_; + typename ParamGenerator<T8>::iterator current8_; + ParamType current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; + const ParamGenerator<T6> g6_; + const ParamGenerator<T7> g7_; + const ParamGenerator<T8> g8_; +}; // class CartesianProductGenerator8 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, + T7, T8, T9> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9> ParamType; + + CartesianProductGenerator9(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5, + const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7, + const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5, + const ParamGenerator<T6>& g6, + const typename ParamGenerator<T6>::iterator& current6, + const ParamGenerator<T7>& g7, + const typename ParamGenerator<T7>::iterator& current7, + const ParamGenerator<T8>& g8, + const typename ParamGenerator<T8>::iterator& current8, + const ParamGenerator<T9>& g9, + const typename ParamGenerator<T9>::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + const typename ParamGenerator<T6>::iterator begin6_; + const typename ParamGenerator<T6>::iterator end6_; + typename ParamGenerator<T6>::iterator current6_; + const typename ParamGenerator<T7>::iterator begin7_; + const typename ParamGenerator<T7>::iterator end7_; + typename ParamGenerator<T7>::iterator current7_; + const typename ParamGenerator<T8>::iterator begin8_; + const typename ParamGenerator<T8>::iterator end8_; + typename ParamGenerator<T8>::iterator current8_; + const typename ParamGenerator<T9>::iterator begin9_; + const typename ParamGenerator<T9>::iterator end9_; + typename ParamGenerator<T9>::iterator current9_; + ParamType current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; + const ParamGenerator<T6> g6_; + const ParamGenerator<T7> g7_; + const ParamGenerator<T8> g8_; + const ParamGenerator<T9> g9_; +}; // class CartesianProductGenerator9 + + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, + T7, T8, T9, T10> > { + public: + typedef ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> ParamType; + + CartesianProductGenerator10(const ParamGenerator<T1>& g1, + const ParamGenerator<T2>& g2, const ParamGenerator<T3>& g3, + const ParamGenerator<T4>& g4, const ParamGenerator<T5>& g5, + const ParamGenerator<T6>& g6, const ParamGenerator<T7>& g7, + const ParamGenerator<T8>& g8, const ParamGenerator<T9>& g9, + const ParamGenerator<T10>& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface<ParamType>* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface<ParamType>* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<ParamType> { + public: + Iterator(const ParamGeneratorInterface<ParamType>* base, + const ParamGenerator<T1>& g1, + const typename ParamGenerator<T1>::iterator& current1, + const ParamGenerator<T2>& g2, + const typename ParamGenerator<T2>::iterator& current2, + const ParamGenerator<T3>& g3, + const typename ParamGenerator<T3>::iterator& current3, + const ParamGenerator<T4>& g4, + const typename ParamGenerator<T4>::iterator& current4, + const ParamGenerator<T5>& g5, + const typename ParamGenerator<T5>::iterator& current5, + const ParamGenerator<T6>& g6, + const typename ParamGenerator<T6>::iterator& current6, + const ParamGenerator<T7>& g7, + const typename ParamGenerator<T7>::iterator& current7, + const ParamGenerator<T8>& g8, + const typename ParamGenerator<T8>::iterator& current8, + const ParamGenerator<T9>& g9, + const typename ParamGenerator<T9>::iterator& current9, + const ParamGenerator<T10>& g10, + const typename ParamGenerator<T10>::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface<ParamType>* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface<ParamType>* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface<ParamType>& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType<const Iterator>(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface<ParamType>* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator<T1>::iterator begin1_; + const typename ParamGenerator<T1>::iterator end1_; + typename ParamGenerator<T1>::iterator current1_; + const typename ParamGenerator<T2>::iterator begin2_; + const typename ParamGenerator<T2>::iterator end2_; + typename ParamGenerator<T2>::iterator current2_; + const typename ParamGenerator<T3>::iterator begin3_; + const typename ParamGenerator<T3>::iterator end3_; + typename ParamGenerator<T3>::iterator current3_; + const typename ParamGenerator<T4>::iterator begin4_; + const typename ParamGenerator<T4>::iterator end4_; + typename ParamGenerator<T4>::iterator current4_; + const typename ParamGenerator<T5>::iterator begin5_; + const typename ParamGenerator<T5>::iterator end5_; + typename ParamGenerator<T5>::iterator current5_; + const typename ParamGenerator<T6>::iterator begin6_; + const typename ParamGenerator<T6>::iterator end6_; + typename ParamGenerator<T6>::iterator current6_; + const typename ParamGenerator<T7>::iterator begin7_; + const typename ParamGenerator<T7>::iterator end7_; + typename ParamGenerator<T7>::iterator current7_; + const typename ParamGenerator<T8>::iterator begin8_; + const typename ParamGenerator<T8>::iterator end8_; + typename ParamGenerator<T8>::iterator current8_; + const typename ParamGenerator<T9>::iterator begin9_; + const typename ParamGenerator<T9>::iterator end9_; + typename ParamGenerator<T9>::iterator current9_; + const typename ParamGenerator<T10>::iterator begin10_; + const typename ParamGenerator<T10>::iterator end10_; + typename ParamGenerator<T10>::iterator current10_; + ParamType current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator<T1> g1_; + const ParamGenerator<T2> g2_; + const ParamGenerator<T3> g3_; + const ParamGenerator<T4> g4_; + const ParamGenerator<T5> g5_; + const ParamGenerator<T6> g6_; + const ParamGenerator<T7> g7_; + const ParamGenerator<T8> g8_; + const ParamGenerator<T9> g9_; + const ParamGenerator<T10> g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN<T> to ParamGenerator<U> if T is +// convertible to U. +// +template <class Generator1, class Generator2> +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template <typename T1, typename T2> + operator ParamGenerator< ::std::tr1::tuple<T1, T2> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2> >( + new CartesianProductGenerator2<T1, T2>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template <class Generator1, class Generator2, class Generator3> +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template <typename T1, typename T2, typename T3> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3> >( + new CartesianProductGenerator3<T1, T2, T3>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template <class Generator1, class Generator2, class Generator3, + class Generator4> +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template <typename T1, typename T2, typename T3, typename T4> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4> >( + new CartesianProductGenerator4<T1, T2, T3, T4>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5> +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5> >( + new CartesianProductGenerator5<T1, T2, T3, T4, T5>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5, class Generator6> +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6> >( + new CartesianProductGenerator6<T1, T2, T3, T4, T5, T6>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_), + static_cast<ParamGenerator<T6> >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5, class Generator6, class Generator7> +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, + T7> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7> >( + new CartesianProductGenerator7<T1, T2, T3, T4, T5, T6, T7>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_), + static_cast<ParamGenerator<T6> >(g6_), + static_cast<ParamGenerator<T7> >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5, class Generator6, class Generator7, + class Generator8> +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, + T8> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8> >( + new CartesianProductGenerator8<T1, T2, T3, T4, T5, T6, T7, T8>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_), + static_cast<ParamGenerator<T6> >(g6_), + static_cast<ParamGenerator<T7> >(g7_), + static_cast<ParamGenerator<T8> >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5, class Generator6, class Generator7, + class Generator8, class Generator9> +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, + T9> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, + T9> >( + new CartesianProductGenerator9<T1, T2, T3, T4, T5, T6, T7, T8, T9>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_), + static_cast<ParamGenerator<T6> >(g6_), + static_cast<ParamGenerator<T7> >(g7_), + static_cast<ParamGenerator<T8> >(g8_), + static_cast<ParamGenerator<T9> >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template <class Generator1, class Generator2, class Generator3, + class Generator4, class Generator5, class Generator6, class Generator7, + class Generator8, class Generator9, class Generator10> +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> + operator ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, + T9, T10> >() const { + return ParamGenerator< ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, + T9, T10> >( + new CartesianProductGenerator10<T1, T2, T3, T4, T5, T6, T7, T8, T9, + T10>( + static_cast<ParamGenerator<T1> >(g1_), + static_cast<ParamGenerator<T2> >(g2_), + static_cast<ParamGenerator<T3> >(g3_), + static_cast<ParamGenerator<T4> >(g4_), + static_cast<ParamGenerator<T5> >(g5_), + static_cast<ParamGenerator<T6> >(g6_), + static_cast<ParamGenerator<T7> >(g7_), + static_cast<ParamGenerator<T8> >(g8_), + static_cast<ParamGenerator<T9> >(g9_), + static_cast<ParamGenerator<T10> >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam<int> { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template <typename T, typename IncrementT> +internal::ParamGenerator<T> Range(T start, T end, IncrementT step) { + return internal::ParamGenerator<T>( + new internal::RangeGenerator<T, IncrementT>(start, end, step)); +} + +template <typename T> +internal::ParamGenerator<T> Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list<char> GetParameterChars() { +// ::std::list<char> list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list<char> l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template <typename ForwardIterator> +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits<ForwardIterator> + ::value_type ParamType; + return internal::ParamGenerator<ParamType>( + new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end)); +} + +template <typename T, size_t N> +internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template <class Container> +internal::ParamGenerator<typename Container::value_type> ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template <typename T1> +internal::ValueArray1<T1> Values(T1 v1) { + return internal::ValueArray1<T1>(v1); +} + +template <typename T1, typename T2> +internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) { + return internal::ValueArray2<T1, T2>(v1, v2); +} + +template <typename T1, typename T2, typename T3> +internal::ValueArray3<T1, T2, T3> Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3<T1, T2, T3>(v1, v2, v3); +} + +template <typename T1, typename T2, typename T3, typename T4> +internal::ValueArray4<T1, T2, T3, T4> Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4<T1, T2, T3, T4>(v1, v2, v3, v4); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5> +internal::ValueArray5<T1, T2, T3, T4, T5> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5<T1, T2, T3, T4, T5>(v1, v2, v3, v4, v5); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6> +internal::ValueArray6<T1, T2, T3, T4, T5, T6> Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6<T1, T2, T3, T4, T5, T6>(v1, v2, v3, v4, v5, v6); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7> +internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7> Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7<T1, T2, T3, T4, T5, T6, T7>(v1, v2, v3, v4, v5, + v6, v7); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8> +internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8<T1, T2, T3, T4, T5, T6, T7, T8>(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9> +internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10> +internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11> +internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, + T11> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, + T11>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12> +internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13> +internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, + T13> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14> +internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15> +internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16> +internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17> +internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18> +internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18>(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19> +internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19>(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20> +internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20>(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21> +internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22> +internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22> Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23> +internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24> +internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25> +internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25> Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25>(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26> +internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27> +internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, + T27> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28> +internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, + T28> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29> +internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30> +internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31> +internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32> +internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32>(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33> +internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33>(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34> +internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34>(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35> +internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35>(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36> +internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36>(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37> +internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37> Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37>(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38> +internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38>(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39> +internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39> Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39>(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40> +internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40> Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41> +internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, + T41> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42> +internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, + T42> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43> +internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, + T43> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44> +internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45> +internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45>(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46> +internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45, T46>(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47> +internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45, T46, T47>(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48> +internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45, T46, T47, T48>(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49> +internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48, T49> Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45, T46, T47, T48, T49>(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template <typename T1, typename T2, typename T3, typename T4, typename T5, + typename T6, typename T7, typename T8, typename T9, typename T10, + typename T11, typename T12, typename T13, typename T14, typename T15, + typename T16, typename T17, typename T18, typename T19, typename T20, + typename T21, typename T22, typename T23, typename T24, typename T25, + typename T26, typename T27, typename T28, typename T29, typename T30, + typename T31, typename T32, typename T33, typename T34, typename T35, + typename T36, typename T37, typename T38, typename T39, typename T40, + typename T41, typename T42, typename T43, typename T44, typename T45, + typename T46, typename T47, typename T48, typename T49, typename T50> +internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, + T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, + T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, + T44, T45, T46, T47, T48, T49, T50> Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, + T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, + T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, + T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50>(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam<bool> { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator<bool> Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam<tuple<const char*, Color> > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam<tuple(bool, bool)> > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template <typename Generator1, typename Generator2> +internal::CartesianProductHolder2<Generator1, Generator2> Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2<Generator1, Generator2>( + g1, g2); +} + +template <typename Generator1, typename Generator2, typename Generator3> +internal::CartesianProductHolder3<Generator1, Generator2, Generator3> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3<Generator1, Generator2, Generator3>( + g1, g2, g3); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4> +internal::CartesianProductHolder4<Generator1, Generator2, Generator3, + Generator4> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4<Generator1, Generator2, Generator3, + Generator4>( + g1, g2, g3, g4); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5> +internal::CartesianProductHolder5<Generator1, Generator2, Generator3, + Generator4, Generator5> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5<Generator1, Generator2, Generator3, + Generator4, Generator5>( + g1, g2, g3, g4, g5); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5, typename Generator6> +internal::CartesianProductHolder6<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6>( + g1, g2, g3, g4, g5, g6); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5, typename Generator6, + typename Generator7> +internal::CartesianProductHolder7<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7>( + g1, g2, g3, g4, g5, g6, g7); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5, typename Generator6, + typename Generator7, typename Generator8> +internal::CartesianProductHolder8<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8>( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5, typename Generator6, + typename Generator7, typename Generator8, typename Generator9> +internal::CartesianProductHolder9<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8, + Generator9> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8, Generator9>( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template <typename Generator1, typename Generator2, typename Generator3, + typename Generator4, typename Generator5, typename Generator6, + typename Generator7, typename Generator8, typename Generator9, + typename Generator10> +internal::CartesianProductHolder10<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8, Generator9, + Generator10> Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10<Generator1, Generator2, Generator3, + Generator4, Generator5, Generator6, Generator7, Generator8, Generator9, + Generator10>( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder<test_case_name>(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator<test_case_name::ParamType> \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder<test_case_name>(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include <iosfwd> +#include <vector> + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure // Failed and the test should be terminated. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, + const char* a_file_name, + int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) { + } + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { return file_name_.c_str(); } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static internal::String ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // NULL if the source file is unknown. + internal::String file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + internal::String summary_; // The test failure summary. + internal::String message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector<TestPartResult> array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template <typename T> +class FooTest : public testing::Test { + public: + ... + typedef std::list<T> List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test case, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types<char, int, unsigned int> MyTypes; +TYPED_TEST_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use to + // visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template <typename T> +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types<char, int, unsigned int> MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types<int>) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template <typename gtest_TypeParam_> \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName<gtest_TypeParam_> { \ + private: \ + typedef CaseName<gtest_TypeParam_> TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", #CaseName, #TestName, 0); \ + template <typename gtest_TypeParam_> \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +# define TYPED_TEST_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template <typename gtest_TypeParam_> \ + class TestName : public CaseName<gtest_TypeParam_> { \ + private: \ + typedef CaseName<gtest_TypeParam_> TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template <typename gtest_TypeParam_> \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName<gtest_TypeParam_>::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types<int>) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase<CaseName, \ + GTEST_CASE_NAMESPACE_(CaseName)::gtest_AllTests_, \ + ::testing::internal::TypeList< Types >::type>::Register(\ + #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class WindowsDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const String& message); + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared in gtest-internal.h but defined here, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template <typename T> +String StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + // Used in the EXPECT_TRUE/FALSE(bool_expression). + explicit AssertionResult(bool success) : success_(success) {} + + // Returns true iff the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template <typename T> AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + internal::scoped_ptr< ::std::string> message_; + + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test. Only the last value for a given + // key is remembered. + // These are public static so they can be called from utility functions + // that are not members of the test fixture. + // The arguments are const char* instead strings, as Google Test is used + // on platforms where string doesn't compile. + // + // Note that a driving consideration for these RecordProperty methods + // was to produce xml output suited to the Greenspan charting utility, + // which at present will only chart values that fit in a 32-bit int. It + // is the user's responsibility to restrict their values to 32-bit ints + // if they intend them to be used with Greenspan. + static void RecordProperty(const char* key, const char* value); + static void RecordProperty(const char* key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const char* a_key, const char* a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const char* new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + internal::String key_; + // The value supplied by the user. + internal::String value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range + // from 0 to test_property_count() - 1. If i is not in that range, aborts + // the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + + // Gets the vector of TestPartResults. + const std::vector<TestPartResult>& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector<TestProperty>& test_properties() const { + return test_properties_; + } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. + void RecordProperty(const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; + + // The vector of TestPartResults + std::vector<TestPartResult> test_part_results_; + // The vector of TestProperties + std::vector<TestProperty> test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } + + // Returns true if this test should run, that is if the test is not disabled + // (or it is disabled but the also_run_disabled_tests flag has been specified) + // and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: + +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, const char* name, + const char* type_param, + const char* value_param, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const char* test_case_name, const char* name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr<const ::std::string> type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr<const ::std::string> value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector<TestInfo*>& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector<TestInfo*>& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + internal::String name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr<const ::std::string> type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector<TestInfo*> test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector<int> test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestCase; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const; + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const; + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + +#if GTEST_HAS_PARAM_TEST + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry(); +#endif // GTEST_HAS_PARAM_TEST + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace); + + // Adds a TestProperty to the current TestResult object. If the result already + // contains a property with the same key, the value will be updated. + void RecordPropertyForCurrentTest(const char* key, const char* value); + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and funcions are friends as they need to access private + // members of UnitTest. + friend class Test; + friend class internal::AssertHelper; + friend class internal::ScopedTrace; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const internal::String& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace(); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +namespace internal { + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char*, and print it as a C string when it is compared against an +// std::string object, for example. +// +// The default implementation ignores the type of the other operand. +// Some specialized versions are used to handle formatting wide or +// narrow C strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template <typename T1, typename T2> +String FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + // C++Builder compiles this incorrectly if the namespace isn't explicitly + // given. + return ::testing::PrintToString(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template <typename T1, typename T2> +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on + // signed/unsigned mismatch. +#endif + + if (expected == actual) { + return AssertionSuccess(); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template <bool lhs_is_null_literal> +class EqHelper { + public: + // This templatized version is for the general case. + template <typename T1, typename T2> + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper<true> { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template <typename T1, typename T2> + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf<!is_pointer<T2>::value>::type* = 0) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template <typename T> + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast<T*>(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template <typename T1, typename T2>\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=); +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=); +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, < ); +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=); +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, > ); + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template <typename RawType> +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint<RawType> lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream expected_ss; + expected_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << expected; + + ::std::stringstream actual_ss; + actual_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StringStreamToString(&expected_ss), + StringStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + String const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +#if GTEST_HAS_PARAM_TEST +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam<int> { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template <typename T> +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface<bool>::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { return *parameter_; } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface<T> and Test. + template <class TestClass> friend class internal::ParameterizedTestFactory; +}; + +template <typename T> +const T* WithParamInterface<T>::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template <typename T> +class TestWithParam : public Test, public WithParamInterface<T> { +}; + +#endif // GTEST_HAS_PARAM_TEST + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. +// +// Examples: +// +// EXPECT_TRUE(server.StatusIsOK()); +// ASSERT_FALSE(server.HasPendingRequest(port)) +// << "There are still pending requests " << "on port " << port; + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 09/24/2010 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template <typename Pred, + typename T1> +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2> +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3> +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3, + typename T4> +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template <typename Pred, + typename T1, + typename T2, + typename T3, + typename T4, + typename T5> +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \ + expected, actual) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C String Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq<type1, type2>() compiles iff type1 and type2 are +// the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq<T1, T2> by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template <typename T> class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq<int, T>(); } +// }; +// +// the code: +// +// void Test1() { Foo<bool> foo; } +// +// will NOT generate a compiler error, as Foo<bool>::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo<bool> foo; foo.Bar(); } +// +// to cause a compiler error. +template <typename T1, typename T2> +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper<T1, T2>(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_case_name, test_name)\ + GTEST_TEST_(test_case_name, test_name, \ + ::testing::Test, ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId<test_fixture>()) + +// Use this macro in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). + +#define RUN_ALL_TESTS()\ + (::testing::UnitTest::GetInstance()->Run()) + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/test/gtest/gtest_main.cc b/test/gtest/gtest_main.cc new file mode 100644 index 0000000..a09bbe0 --- /dev/null +++ b/test/gtest/gtest_main.cc @@ -0,0 +1,39 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <iostream> + +#include "gtest/gtest.h" + +GTEST_API_ int main(int argc, char **argv) { + std::cout << "Running main() from gtest_main.cc\n"; + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..60e2417 --- /dev/null +++ b/test/main.c @@ -0,0 +1,802 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <time.h> + +#include "../htp/bstr.h" +#include "../htp/htp.h" +#include "test.h" + +char *home = NULL; + +int callback_transaction_start(htp_connp_t *connp) { + printf("-- Callback: transaction_start\n"); +} + +int callback_request_line(htp_connp_t *connp) { + printf("-- Callback: request_line\n"); +} + +int callback_request_headers(htp_connp_t *connp) { + printf("-- Callback: request_headers\n"); + bstr *raw = htp_tx_get_request_headers_raw(connp->in_tx); + fprint_raw_data(stdout, "REQUEST HEADERS RAW 1", bstr_ptr(raw), bstr_len(raw)); +} + +int callback_request_body_data(htp_tx_data_t *d) { + /* + if (d->data != NULL) { + printf("-- Callback: request_body_data\n"); + fprint_raw_data(stdout, __FUNCTION__, d->data, d->len); + } else { + printf("-- Callback: request_body_data (LAST)\n"); + } + */ +} + +int callback_request_trailer(htp_connp_t *connp) { + printf("-- Callback: request_trailer\n"); +} + +int callback_request(htp_connp_t *connp) { + printf("-- Callback: request\n"); +} + +int callback_response_line(htp_connp_t *connp) { + printf("-- Callback: response_line\n"); +} + +int callback_response_headers(htp_connp_t *connp) { + printf("-- Callback: response_headers\n"); +} + +int callback_response_body_data(htp_tx_data_t *d) { + if (d->data != NULL) { + printf("-- Callback: response_body_data\n"); + fprint_raw_data(stdout, __FUNCTION__, d->data, d->len); + } else { + printf("-- Callback: response_body_data (LAST)\n"); + } +} + +int callback_request_file_data(htp_file_data_t *file_data) { + if (file_data->data != NULL) { + printf("-- Callback: request_file_data\n"); + fprint_raw_data(stdout, __FUNCTION__, file_data->data, file_data->len); + } else { + printf("-- Callback: request_file_data (LAST)\n"); + } +} + +int callback_response_trailer(htp_connp_t *connp) { + printf("-- Callback: response_trailer\n"); +} + +int callback_response(htp_connp_t *connp) { + printf("-- Callback: response\n"); +} + +int callback_response_destroy(htp_connp_t *connp) { + htp_tx_destroy(connp->out_tx); + printf("-- Destroyed transaction\n"); +} + +int callback_log(htp_log_t *log) { + htp_print_log(stdout, log); +} + +static void print_tx(htp_connp_t *connp, htp_tx_t *tx) { + char *request_line = bstr_util_strdup_to_c(tx->request_line); + htp_header_t *h_user_agent = htp_table_get_c(tx->request_headers, "user-agent"); + htp_header_t *h_referer = htp_table_get_c(tx->request_headers, "referer"); + char *referer, *user_agent; + char buf[256]; + + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + + strftime(buf, 255, "%d/%b/%Y:%T %z", tmp); + + if (h_user_agent == NULL) user_agent = strdup("-"); + else { + user_agent = bstr_util_strdup_to_c(h_user_agent->value); + } + + if (h_referer == NULL) referer = strdup("-"); + else { + referer = bstr_util_strdup_to_c(h_referer->value); + } + + printf("%s - - [%s] \"%s\" %i %zu \"%s\" \"%s\"\n", connp->conn->client_addr, buf, + request_line, tx->response_status_number, tx->response_message_len, + referer, user_agent); + + free(referer); + free(user_agent); + free(request_line); +} + +static int run_directory(char *dirname, htp_cfg_t *cfg) { + struct dirent *entry; + char buf[1025]; + DIR *d = opendir(dirname); + htp_connp_t *connp; + + if (d == NULL) { + printf("Failed to open directory: %s\n", dirname); + return -1; + } + + while ((entry = readdir(d)) != NULL) { + if (strncmp(entry->d_name, "stream", 6) == 0) { + int rc = test_run(dirname, entry->d_name, cfg, &connp); + + if (rc < 0) { + if (connp != NULL) { + htp_log_t *last_error = htp_connp_get_last_error(connp); + if (last_error != NULL) { + printf(" -- failed: %s\n", last_error->msg); + } else { + printf(" -- failed: ERROR NOT AVAILABLE\n"); + } + + return 0; + } else { + return -1; + } + } else { + printf(" -- %zu transaction(s)\n", htp_list_size(connp->conn->transactions)); + + for (int i = 0, n = htp_list_size(connp->conn->transactions); i < n; i++) { + htp_tx_t *tx = htp_list_get(connp->conn->transactions, i); + + printf(" "); + print_tx(connp, tx); + } + + printf("\n"); + + htp_connp_destroy_all(connp); + } + } + } + + closedir(d); + + return 1; +} + +int main_dir(int argc, char** argv) { + //int main(int argc, char** argv) { + htp_cfg_t *cfg = htp_config_create(); + htp_config_register_log(cfg, callback_log); + htp_config_register_response_complete(cfg, callback_response_destroy); + + run_directory("C:\\http_traces\\run1", cfg); + //run_directory("/home/ivanr/work/traces/run3/", cfg); + + htp_config_destroy(cfg); +} + +#define RUN_TEST(X, Y) \ + {\ + tests++; \ + printf("---------------------------------\n"); \ + printf("Test: " #X "\n"); \ + int rc = X(Y); \ + if (rc < 0) { \ + printf(" Failed with %i\n", rc); \ + failures++; \ + } \ + printf("\n"); \ + } + +/** + * Dummy entry point. + */ +int main(int argc, char** argv) { + return EXIT_SUCCESS; +} + +int main_path_decoding_tests(int argc, char** argv) { + htp_cfg_t *cfg = htp_config_create(); + htp_connp_t *connp = htp_connp_create(cfg); + htp_tx_t *tx = htp_tx_create(connp); + char *str; + bstr *path = NULL; + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven"); + cfg->path_case_insensitive = 1; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + cfg->path_backslash_separators = 1; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + cfg->path_backslash_separators = 1; + cfg->path_encoded_separators_decode = 1; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + cfg->path_backslash_separators = 1; + cfg->path_encoded_separators_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_REMOVE_PERCENT; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven/%u0074"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + cfg->path_backslash_separators = 1; + cfg->path_encoded_separators_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PROCESS_INVALID; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); + + // + path = bstr_dup_c("/One\\two///ThRee%2ffive%5csix/se%xxven/%u0074%u0100"); + cfg->path_case_insensitive = 1; + cfg->path_compress_separators = 1; + cfg->path_backslash_separators = 1; + cfg->path_encoded_separators_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + cfg->path_u_encoding_decode = 1; + + str = bstr_util_strdup_to_c(path); + printf("Before: %s\n", str); + free(str); + htp_decode_path_inplace(cfg, tx, path); + str = bstr_util_strdup_to_c(path); + printf("After: %s\n\n", str); + free(str); + bstr_free(path); +} + +void encode_utf8_2(uint8_t *data, uint32_t i) { + i = i & 0x7ff; + data[0] = 0xc0 + (i >> 6); + data[1] = 0x80 + (i & 0x3f); +} + +void encode_utf8_3(uint8_t *data, uint32_t i) { + i = i & 0xffff; + data[0] = 0xe0 + (i >> 12); + data[1] = 0x80 + ((i >> 6) & 0x3f); + data[2] = 0x80 + (i & 0x3f); +} + +void encode_utf8_4(uint8_t *data, uint32_t i) { + i = i & 0x10ffff; + data[0] = 0xf0 + (i >> 18); + data[1] = 0x80 + ((i >> 12) & 0x3f); + data[2] = 0x80 + ((i >> 6) & 0x3f); + data[3] = 0x80 + (i & 0x3f); +} + +int main_utf8_decoder_tests(int argc, char** argv) { + htp_cfg_t *cfg = htp_config_create(); + htp_connp_t *connp = htp_connp_create(cfg); + htp_tx_t *tx = htp_tx_create(connp); + + bstr *path = NULL; + + path = bstr_dup_c("//////////"); + uint8_t *data = bstr_ptr(path); + + int i = 0; + + for (i = 0; i < 0x80; i++) { + memset(data, 0x2f, 10); + tx->flags = 0; + encode_utf8_2(data, i); + htp_utf8_validate_path(tx, path); + if (tx->flags != HTP_PATH_UTF8_OVERLONG) { + printf("#2 i %i data %x %x flags %x\n", i, (uint8_t) data[0], (uint8_t) data[1], tx->flags); + } + } + + for (i = 0; i < 0x800; i++) { + memset(data, 0x2f, 10); + tx->flags = 0; + encode_utf8_3(data, i); + htp_utf8_validate_path(tx, path); + if (tx->flags != HTP_PATH_UTF8_OVERLONG) { + printf("#3 i %x data %x %x %x flags %x\n", i, (uint8_t) data[0], (uint8_t) data[1], (uint8_t) data[2], tx->flags); + } + } + + for (i = 0; i < 0x10000; i++) { + memset(data, 0x2f, 10); + tx->flags = 0; + encode_utf8_4(data, i); + htp_utf8_validate_path(tx, path); + if ((i >= 0xff00) && (i <= 0xffff)) { + if (tx->flags != (HTP_PATH_UTF8_OVERLONG | HTP_PATH_HALF_FULL_RANGE)) { + printf("#4 i %x data %x %x %x %x flags %x\n", i, (uint8_t) data[0], (uint8_t) data[1], (uint8_t) data[2], (uint8_t) data[3], tx->flags); + } + } else { + if (tx->flags != HTP_PATH_UTF8_OVERLONG) { + printf("#4 i %x data %x %x %x %x flags %x\n", i, (uint8_t) data[0], (uint8_t) data[1], (uint8_t) data[2], (uint8_t) data[3], tx->flags); + } + } + } + + bstr_free(&path); +} + +#define PATH_DECODE_TEST_BEFORE(NAME) \ + test_name = NAME; \ + tests++; \ + expected_status = 0; \ + expected_flags = -1; \ + success = 0; \ + cfg = htp_config_create(); \ + connp = htp_connp_create(cfg); \ + tx = htp_tx_create(connp); + +#define PATH_DECODE_TEST_AFTER() \ + htp_decode_path_inplace(cfg, tx, input); \ + htp_utf8_decode_path_inplace(cfg, tx, input); \ + if (bstr_cmp(input, expected) == 0) success = 1; \ + printf("[%2i] %s: %s\n", tests, (success == 1 ? "SUCCESS" : "FAILURE"), test_name); \ + if ((success == 0)||((expected_status != 0)&&(expected_status != tx->response_status_expected_number))) { \ + char *s1 = bstr_util_strdup_to_c(input); \ + char *s2 = bstr_util_strdup_to_c(expected); \ + printf(" Output: [%s]\n", s1); \ + printf(" Expected: [%s]\n", s2); \ + if (expected_status != 0) { \ + printf(" Expected status %i; got %i\n", expected_status, tx->response_status_expected_number); \ + } \ + if (expected_flags != -1) { \ + printf(" Expected flags 0x%x; got 0x%x\n", expected_flags, tx->flags); \ + } \ + free(s2); \ + free(s1); \ + failures++; \ + } \ + htp_tx_destroy(tx); \ + htp_config_destroy(cfg); \ + bstr_free(&expected); \ + bstr_free(&input); + +int main_path_tests(int argc, char** argv) { + htp_cfg_t *cfg = NULL; + htp_connp_t *connp = NULL; + htp_tx_t *tx = NULL; + bstr *input = NULL; + bstr *expected = NULL; + int success = 0; + int tests = 0; + int failures = 0; + int expected_status = 0; + int expected_flags = 0; + char *test_name = NULL; + + PATH_DECODE_TEST_BEFORE("URL-decoding"); + input = bstr_dup_c("/%64est"); + expected = bstr_dup_c("/dest"); + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid URL-encoded, preserve %"); + input = bstr_dup_c("/%xxest"); + expected = bstr_dup_c("/%xxest"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid URL-encoded, remove %"); + input = bstr_dup_c("/%xxest"); + expected = bstr_dup_c("/xxest"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_REMOVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid URL-encoded (end of string, test 1), preserve %"); + input = bstr_dup_c("/test/%2"); + expected = bstr_dup_c("/test/%2"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid URL-encoded (end of string, test 2), preserve %"); + input = bstr_dup_c("/test/%"); + expected = bstr_dup_c("/test/%"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid URL-encoded, preserve % and 400"); + input = bstr_dup_c("/%xxest"); + expected = bstr_dup_c("/%xxest"); + expected_status = 400; + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + cfg->path_invalid_encoding_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("%u decoding (expected not to decode; 400)"); + input = bstr_dup_c("/%u0064"); + expected = bstr_dup_c("/%u0064"); + expected_flags = HTP_PATH_INVALID_ENCODING; + expected_status = 400; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + cfg->path_invalid_encoding_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("%u decoding (decode; 400)"); + input = bstr_dup_c("/%u0064"); + expected = bstr_dup_c("/d"); + expected_status = 400; + expected_flags = HTP_PATH_OVERLONG_U; + cfg->path_u_encoding_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("%u decoding (also overlong)"); + input = bstr_dup_c("/%u0064"); + expected = bstr_dup_c("/d"); + expected_flags = HTP_PATH_OVERLONG_U; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding, leave; preserve percent"); + input = bstr_dup_c("/%uXXXX---"); + expected = bstr_dup_c("/%uXXXX---"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding, decode invalid; preserve percent"); + input = bstr_dup_c("/%uXXXX---"); + expected = bstr_dup_c("/?---"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PROCESS_INVALID; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding, decode invalid; preserve percent; 400"); + input = bstr_dup_c("/%uXXXX---"); + expected = bstr_dup_c("/?---"); + expected_flags = HTP_PATH_INVALID_ENCODING; + expected_status = 400; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + cfg->path_invalid_encoding_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding (not enough data 1), preserve percent"); + input = bstr_dup_c("/%u123"); + expected = bstr_dup_c("/%u123"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding (not enough data 2), preserve percent"); + input = bstr_dup_c("/%u12"); + expected = bstr_dup_c("/%u12"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid %u decoding (not enough data 3), preserve percent"); + input = bstr_dup_c("/%u1"); + expected = bstr_dup_c("/%u1"); + expected_flags = HTP_PATH_INVALID_ENCODING; + cfg->path_u_encoding_decode = 1; + cfg->path_invalid_encoding_handling = HTP_URL_DECODE_PRESERVE_PERCENT; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("%u decoding, best-fit mapping"); + input = bstr_dup_c("/%u0107"); + expected = bstr_dup_c("/c"); + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("%u decoding, 404 to UCS-2 characters"); + input = bstr_dup_c("/%u0107"); + expected = bstr_dup_c("/c"); + expected_status = 404; + cfg->path_u_encoding_decode = 1; + cfg->path_unicode_unwanted = HTP_UNWANTED_404; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Forward slash (URL-encoded), not expect to decode"); + input = bstr_dup_c("/one%2ftwo"); + expected = bstr_dup_c("/one%2ftwo"); + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Forward slash (URL-encoded), expect to decode"); + input = bstr_dup_c("/one%2ftwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Forward slash (URL-encoded), expect not do decode and 404"); + input = bstr_dup_c("/one%2ftwo"); + expected = bstr_dup_c("/one%2ftwo"); + expected_status = 404; + cfg->path_encoded_separators_decode = 0; + cfg->path_encoded_separators_unwanted = HTP_UNWANTED_404; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Forward slash (%u-encoded), expect to decode"); + input = bstr_dup_c("/one%u002ftwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Forward slash (%u-encoded, fullwidth), expect to decode"); + input = bstr_dup_c("/one%uff0ftwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Backslash (URL-encoded), not a separator; expect to decode"); + input = bstr_dup_c("/one%5ctwo"); + expected = bstr_dup_c("/one\\two"); + cfg->path_encoded_separators_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Backslash (URL-encoded), as path segment separator"); + input = bstr_dup_c("/one%5ctwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + cfg->path_backslash_separators = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Backslash (not encoded), as path segment separator"); + input = bstr_dup_c("/one\\two"); + expected = bstr_dup_c("/one/two"); + cfg->path_backslash_separators = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Backslash (%u-encoded), as path segment separator"); + input = bstr_dup_c("/one%u005ctwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + cfg->path_backslash_separators = 1; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Backslash (%u-encoded, fullwidth), as path segment separator"); + input = bstr_dup_c("/one%uff3ctwo"); + expected = bstr_dup_c("/one/two"); + cfg->path_encoded_separators_decode = 1; + cfg->path_backslash_separators = 1; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid UTF-8 encoding, encoded"); + input = bstr_dup_c("/%f7test"); + expected = bstr_dup_c("/\xf7test"); + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Invalid UTF-8 encoding, encoded (400)"); + input = bstr_dup_c("/%f7test"); + expected = bstr_dup_c("/\xf7test"); + expected_status = 400; + expected_flags = HTP_PATH_UTF8_INVALID; + cfg->path_utf8_invalid_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (raw) in path; leave"); + input = bstr_dup_mem("/test\0text", 10); + expected = bstr_dup_mem("/test\0text", 10); + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (raw) in path; terminate path"); + input = bstr_dup_mem("/test\0text", 10); + expected = bstr_dup_c("/test"); + cfg->path_nul_raw_terminates = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (raw) in path; 400"); + input = bstr_dup_mem("/test\0text", 10); + expected = bstr_dup_mem("/test\0text", 10); + cfg->path_nul_raw_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (URL-encoded) in path; leave"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_mem("/test\0text", 10); + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (URL-encoded) in path; terminate path"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_c("/test"); + cfg->path_nul_encoded_terminates = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (URL-encoded) in path; 400"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_mem("/test\0text", 10); + cfg->path_nul_encoded_unwanted = HTP_UNWANTED_404; + expected_status = 400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (URL-encoded) in path; 404"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_mem("/test\0text", 10); + cfg->path_nul_encoded_unwanted = HTP_UNWANTED_404; + expected_status = 404; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (%u-encoded) in path; terminate path"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_c("/test"); + cfg->path_nul_encoded_terminates = 1; + cfg->path_u_encoding_decode = 1; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (%u-encoded) in path; 400"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_mem("/test\0text", 10); + cfg->path_nul_encoded_unwanted = HTP_UNWANTED_404; + cfg->path_u_encoding_decode = 1; + expected_status = 400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("NUL byte (%u-encoded) in path; 404"); + input = bstr_dup_c("/test%00text"); + expected = bstr_dup_mem("/test\0text", 10); + cfg->path_nul_encoded_unwanted = HTP_UNWANTED_404; + cfg->path_u_encoding_decode = 1; + expected_status = 404; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Control char in path, encoded (no effect)"); + input = bstr_dup_c("/%01test"); + expected = bstr_dup_c("/\x01test"); + cfg->path_control_chars_unwanted = HTP_UNWANTED_IGNORE; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Control char in path, raw (no effect)"); + input = bstr_dup_c("/\x01test"); + expected = bstr_dup_c("/\x01test"); + cfg->path_control_chars_unwanted = HTP_UNWANTED_IGNORE; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Control char in path, encoded (400)"); + input = bstr_dup_c("/%01test"); + expected = bstr_dup_c("/\x01test"); + expected_status = 400; + cfg->path_control_chars_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("Control char in path, raw (400)"); + input = bstr_dup_c("/\x01test"); + expected = bstr_dup_c("/\x01test"); + expected_status = 400; + cfg->path_control_chars_unwanted = HTP_UNWANTED_400; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("UTF-8; overlong 2-byte sequence"); + input = bstr_dup_c("/%c1%b4est"); + expected = bstr_dup_c("/test"); + expected_flags = HTP_PATH_UTF8_OVERLONG; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("UTF-8; overlong 3-byte sequence"); + input = bstr_dup_c("/%e0%81%b4est"); + expected = bstr_dup_c("/test"); + expected_flags = HTP_PATH_UTF8_OVERLONG; + PATH_DECODE_TEST_AFTER(); + + PATH_DECODE_TEST_BEFORE("UTF-8; overlong 4-byte sequence"); + input = bstr_dup_c("/%f0%80%81%b4est"); + expected = bstr_dup_c("/test"); + expected_flags = HTP_PATH_UTF8_OVERLONG; + PATH_DECODE_TEST_AFTER(); + + printf("\n"); + printf("Total tests: %i, %i failure(s).\n", tests, failures); +} diff --git a/test/pcaptohtp.py b/test/pcaptohtp.py new file mode 100644 index 0000000..3686363 --- /dev/null +++ b/test/pcaptohtp.py @@ -0,0 +1,17 @@ +import sys +import binascii + +# Transforms a pcap into a test file for libhtp +# tshark -Tfields -e tcp.dstport -e tcp.payload -r input.pcap > input.txt +# python pcaptohtp.py input.txt > input.t + +f = open(sys.argv[1]) +for l in f.readlines(): + portAndPl=l.split() + if len(portAndPl) == 2: + # determine request or response based on port + if portAndPl[0] == "80": + print(">>>") + else: + print("<<<") + print(binascii.unhexlify(portAndPl[1].replace(":",""))) diff --git a/test/test-tcpick.c b/test/test-tcpick.c new file mode 100644 index 0000000..b0bc14f --- /dev/null +++ b/test/test-tcpick.c @@ -0,0 +1,351 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <ctype.h> +#include <dirent.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> + +#include "../htp/htp.h" + +#define CLIENT 1 +#define SERVER 2 + +static int parse_filename(const char *filename, char **remote_addr, char **local_addr) { + char *copy = strdup(filename); + char *p, *saveptr; + + char *start = copy; + char *q = strrchr(copy, '/'); + if (q != NULL) start = q; + + q = strrchr(start, '\\'); + if (q != NULL) start = q; + + int count = 0; + p = strtok_r(start, "_", &saveptr); + while (p != NULL) { + count++; + // printf("%i %s\n", count, p); + + switch (count) { + case 3: + *remote_addr = strdup(p); + break; + case 4: + *local_addr = strdup(p); + break; + } + + p = strtok_r(NULL, "_", &saveptr); + } + + free(copy); + + return 0; +} + +static int parse_chunk_info(char *buf, size_t *response_offset, size_t *response_len) { + char *p = buf; + size_t lastlen; + + while ((*p != ']') && (p != '\0')) p++; + if (*p == '\0') return -1; + p++; + + while (isspace(*p)) p++; + + *response_offset = bstr_util_mem_to_pint(p, strlen(p), 10, &lastlen); + + p += lastlen; + + while ((*p != '(') && (p != '\0')) p++; + if (*p == '\0') return -1; + p++; + + *response_len = bstr_util_mem_to_pint(p, strlen(p), 10, &lastlen); + + return 1; +} + +static int tcpick_run_file(const char *filename, htp_cfg_t *cfg, htp_connp_t **connp) { + struct timeval tv; + char buf[1025]; + int first = -1, current = -1; + char *remote_addr, *local_addr; + + char *request_last_chunk = NULL; + char *response_last_chunk = NULL; + size_t request_offset, request_len; + size_t request_last_offset = 0, request_last_len = 0; + size_t response_offset, response_len; + size_t response_last_offset = 0, response_last_len = 0; + + if (parse_filename(filename, &remote_addr, &local_addr) < 0) { + printf("Failed to parse filename: %s\n", filename); + return -1; + } + + FILE *f = fopen(filename, "rb"); + if (f == NULL) { + printf("Unable to open file: %s\n", filename); + return -1; + } + + gettimeofday(&tv, NULL); + + // Create parser + *connp = htp_connp_create(cfg); + + // Find all chunks and feed them to the parser + while (fgets(buf, 1024, f) != NULL) { + // Ignore empty lines + if (buf[0] == LF) { + continue; + } + + if (strncmp(buf, "[server", 7) == 0) { + current = SERVER; + } else { + current = CLIENT; + } + + if (first == -1) { + first = current; + + if (first == SERVER) { + htp_connp_open(*connp, local_addr, 80, remote_addr, 80, &tv); + } else { + htp_connp_open(*connp, remote_addr, 80, local_addr, 80, &tv); + } + } + + int len = 0; + + if (first == current) { + if (parse_chunk_info(buf, &request_offset, &request_len) < 0) { + printf("Invalid line: %s", buf); + fclose(f); + htp_connp_destroy_all(*connp); + *connp = NULL; + return -1; + } + + len = request_len; + + // printf("# Request offset %i len %i\n", request_offset, request_len); + } else { + if (parse_chunk_info(buf, &response_offset, &response_len) < 0) { + printf("Invalid line: %s", buf); + fclose(f); + htp_connp_destroy_all(*connp); + *connp = NULL; + return -1; + } + + len = response_len; + + // printf("# Response offset %i len %i\n", response_offset, response_len); + } + + // printf("Len: %i\n", len); + + if (len <= 0) { + printf("Invalid length: %i\n", len); + fclose(f); + htp_connp_destroy_all(*connp); + *connp = NULL; + return -1; + } + + char *data = malloc(len); + if (data == NULL) { + printf("Failed to allocate %i bytes\n", len); + fclose(f); + htp_connp_destroy_all(*connp); + *connp = NULL; + return -1; + } + + int read = fread(data, 1, len, f); + if (read != len) { + // printf("Failed to read %i bytes (got %i)\n", len, read); + fclose(f); + htp_connp_destroy_all(*connp); + *connp = NULL; + return -1; + } + + if (first == current) { + if ((request_last_chunk == NULL) || (request_len != request_last_len) || (memcmp(data, request_last_chunk, request_len) != 0)) { + // printf("# Parse request data: %i byte(s)\n", len); + if (htp_connp_req_data(*connp, &tv, data, len) == HTP_ERROR) { + fclose(f); + return -1; + } + } + + request_last_offset = request_offset; + request_last_len = request_len; + if (request_last_chunk != NULL) { + free(request_last_chunk); + } + request_last_chunk = data; + } else { + if ((response_last_chunk == NULL) || (response_len != response_last_len) || (memcmp(data, response_last_chunk, response_len) != 0)) { + // printf("# Parse response data: %i byte(s)\n", len); + if (htp_connp_res_data(*connp, &tv, data, len) == HTP_ERROR) { + fclose(f); + return -1; + } + } + + response_last_offset = response_offset; + response_last_len = response_len; + if (response_last_chunk != NULL) { + free(response_last_chunk); + } + response_last_chunk = data; + } + } + + fclose(f); + + htp_connp_close(*connp, &tv); + + return 1; +} + +static void print_tx(htp_connp_t *connp, htp_tx_t *tx) { + char *request_line = bstr_util_strdup_to_c(tx->request_line); + htp_header_t *h_user_agent = htp_table_get_c(tx->request_headers, "user-agent"); + htp_header_t *h_referer = htp_table_get_c(tx->request_headers, "referer"); + char *referer, *user_agent; + char buf[256]; + + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + + strftime(buf, 255, "%d/%b/%Y:%T %z", tmp); + + if (h_user_agent == NULL) user_agent = strdup("-"); + else { + user_agent = bstr_util_strdup_to_c(h_user_agent->value); + } + + if (h_referer == NULL) referer = strdup("-"); + else { + referer = bstr_util_strdup_to_c(h_referer->value); + } + + printf("%s - - [%s] \"%s\" %i %zu \"%s\" \"%s\"\n", connp->conn->client_addr, buf, + request_line, tx->response_status_number, tx->response_message_len, + referer, user_agent); + + free(referer); + free(user_agent); + free(request_line); +} + +static int run_file(char *filename, htp_cfg_t *cfg) { + htp_connp_t *connp; + + fprintf(stdout, "Running file %s", filename); + + int rc = tcpick_run_file(filename, cfg, &connp); + if (rc < 0) { + if (connp != NULL) { + htp_log_t *last_error = htp_connp_get_last_error(connp); + if (last_error != NULL) { + printf(" -- failed: %s\n", last_error->msg); + } else { + printf(" -- failed: ERROR NOT AVAILABLE\n"); + } + + return 0; + } else { + return -1; + } + } else { + printf(" -- %zu transaction(s)\n", htp_list_size(connp->conn->transactions)); + + for (int i = 0, n = htp_list_size(connp->conn->transactions); i < n; i++) { + htp_tx_t *tx = htp_list_get(connp->conn->transactions, i); + + printf(" "); + print_tx(connp, tx); + } + + printf("\n"); + + htp_connp_destroy_all(connp); + + return 1; + } +} + +static int run_directory(char *dirname, htp_cfg_t *cfg) { + struct dirent *entry; + char buf[1025]; + DIR *d = opendir(dirname); + + if (d == NULL) { + printf("Failed to open directory: %s\n", dirname); + return -1; + } + + while ((entry = readdir(d)) != NULL) { + if (strncmp(entry->d_name, "tcpick", 6) == 0) { + strncpy(buf, dirname, 1024); + strncat(buf, "/", 1024 - strlen(buf)); + strncat(buf, entry->d_name, 1024 - strlen(buf)); + + // fprintf(stderr, "Filename: %s\n", buf); + run_file(buf, cfg); + //if (run_file(buf, cfg) <= 0) { + // closedir(d); + // return 0; + //} + } + } + + closedir(d); + + return 1; +} diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..8c12254 --- /dev/null +++ b/test/test.c @@ -0,0 +1,444 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <assert.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "../htp/htp.h" +#include "test.h" + +/** + * Destroys a test. + * + * @param[in] test + */ +static void test_destroy(test_t *test) { + if (test->buf != NULL) { + free(test->buf); + test->buf = NULL; + } +} + +/** + * Checks if there's a chunk boundary at the given position. + * + * @param[in] test + * @param[in] pos + * @return Zero if there is no boundary, SERVER or CLIENT if a boundary + * was found, and a negative value on error (e.g., not enough data + * to determine if a boundary is present). + */ +static int test_is_boundary(test_t *test, size_t pos) { + // Check that there's enough room + if (pos + 3 >= test->len) return -1; + + if ((test->buf[pos] == '<') && (test->buf[pos + 1] == '<' || test->buf[pos + 1] == '>') && (test->buf[pos + 2] == '<')) { + if (test->buf[pos + 3] == '\n') { + return SERVER; + } + + if (test->buf[pos + 3] == '\r') { + if (pos + 4 >= test->len) return -1; + else if (test->buf[pos + 4] == '\n') { + return SERVER; + } + } + } + + if ((test->buf[pos] == '>') && (test->buf[pos + 1] == '>' || test->buf[pos + 1] == '<') && (test->buf[pos + 2] == '>')) { + if (test->buf[pos + 3] == '\n') { + return CLIENT; + } + + if (test->buf[pos + 3] == '\r') { + if (pos + 4 >= test->len) return -1; + else if (test->buf[pos + 4] == '\n') { + return CLIENT; + } + } + } + + return 0; +} + +/** + * Initializes test by loading the entire data file into a memory block. + * + * @param[in] test + * @param[in] filename + * @return Non-negative value on success, negative value on error. + */ +static int test_init(test_t *test, const char *filename, int clone_count) { + memset(test, 0, sizeof (test_t)); + + int fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) return -1; + + struct stat buf; + if (fstat(fd, &buf) < 0) { + close(fd); + return -1; + } + + test->buf = malloc(buf.st_size * clone_count + clone_count - 1); + test->len = 0; + test->pos = 0; + + // Check that we received our memory. + assert(test->buf != NULL); + + int bytes_read = 0; + while ((bytes_read = read(fd, test->buf + test->len, buf.st_size - test->len)) > 0) { + test->len += bytes_read; + } + + if ((int)test->len != buf.st_size) { + free(test->buf); + close(fd); + return -2; + } + + close(fd); + + int i = 1; + for (i = 1; i < clone_count; i++) { + test->buf[i * buf.st_size + (i-1)] = '\n'; + memcpy(test->buf + i * buf.st_size + i, test->buf, buf.st_size); + } + + test->len = buf.st_size * clone_count + clone_count - 1; + + return 1; +} + +static void test_start(test_t *test) { + test->pos = 0; +} + +/** + * Finds the next data chunk in the given test. + * + * @param[in] test + * @return One if a chunk is found or zero if there are no more chunks in the test. On + * success, test->chunk will point to the beginning of the chunk, while + * test->chunk_len will contain its length. + */ +int test_next_chunk(test_t *test) { + if (test->pos >= test->len) { + return 0; + } + + test->chunk = NULL; + int isgap = 0; + + while (test->pos < test->len) { + // Do we need to start another chunk? + if (test->chunk == NULL) { + // Are we at a boundary + test->chunk_direction = test_is_boundary(test, test->pos); + if (test->chunk_direction <= 0) { + // Error + return -1; + } + + if (test->buf[test->pos + 1] != test->buf[test->pos + 2]) { + isgap = 1; + } else { + isgap = 0; + } + // Move over the boundary + test->pos += 4; + if (test->pos >= test->len) { + return 0; + } + if (test->buf[test->pos-1] == '\r') test->pos++; + if (test->pos >= test->len) { + return 0; + } + + // Start new chunk + test->chunk = test->buf + test->pos; + test->chunk_offset = test->pos; + // if it is empty (boundary already), continue to next chunk + if (test_is_boundary(test, test->pos) > 0) { + test->chunk = NULL; + continue; + } + } + + // Are we at the end of a line? + if (test->buf[test->pos] == '\n') { + int r = test_is_boundary(test, test->pos + 1); + if ((r == CLIENT) || (r == SERVER)) { + // We got ourselves a chunk + test->chunk_len = test->pos - test->chunk_offset; + + // Remove one '\r' (in addition to the '\n' that we've already removed), + // which belongs to the next boundary + if ((test->chunk_len > 0) && (test->chunk[test->chunk_len - 1] == '\r')) { + test->chunk_len--; + } + + // Position at the next boundary line + test->pos++; + if (test->pos >= test->len) { + return 0; + } + if (isgap) { + test->chunk = NULL; + } + + return 1; + } + } + + test->pos++; + } + + + if (test->chunk != NULL) { + test->chunk_len = test->pos - test->chunk_offset; + if (isgap) { + test->chunk = NULL; + } + return 1; + } + + return 0; +} + +static int parse_filename(const char *filename, char **remote_addr, int *remote_port, char **local_addr, int *local_port) { + char *copy = strdup(filename); + char *p, *saveptr; + + char *start = copy; + char *q = strrchr(copy, '/'); + if (q != NULL) start = q; + + q = strrchr(start, '\\'); + if (q != NULL) start = q; + + int count = 0; + p = strtok_r(start, "_", &saveptr); + while (p != NULL) { + count++; + // printf("%i %s\n", count, p); + + switch (count) { + case 2: + *remote_addr = strdup(p); + break; + case 3: + *remote_port = atoi(p); + break; + case 4: + *local_addr = strdup(p); + break; + case 5: + *local_port = atoi(p); + break; + } + + p = strtok_r(NULL, "_", &saveptr); + } + + free(copy); + + return 0; +} + +/** + * Runs a single test. + * + * @param[in] filename + * @param[in] cfg + * @return A pointer to the instance of htp_connp_t created during + * the test, or NULL if the test failed for some reason. + */ +int test_run_ex(const char *testsdir, const char *testname, htp_cfg_t *cfg, htp_connp_t **connp, int clone_count) { + char filename[1025]; + test_t test; + struct timeval tv_start, tv_end; + int rc; + + *connp = NULL; + + strncpy(filename, testsdir, 1024); + strncat(filename, "/", 1024 - strlen(filename)); + strncat(filename, testname, 1024 - strlen(filename)); + + // printf("Filename: %s\n", filename); + + // Initinialize test + + rc = test_init(&test, filename, clone_count); + if (rc < 0) { + return rc; + } + + gettimeofday(&tv_start, NULL); + + test_start(&test); + + // Create parser + *connp = htp_connp_create(cfg); + if (*connp == NULL) { + fprintf(stderr, "Failed to create connection parser\n"); + exit(1); + } + + htp_connp_set_user_data(*connp, (void *) 0x02); + + // Does the filename contain connection metdata? + if (strncmp(testname, "stream", 6) == 0) { + // It does; use it + char *remote_addr = NULL, *local_addr = NULL; + int remote_port = -1, local_port = -1; + + parse_filename(testname, &remote_addr, &remote_port, &local_addr, &local_port); + htp_connp_open(*connp, (const char *) remote_addr, remote_port, (const char *) local_addr, local_port, &tv_start); + free(remote_addr); + free(local_addr); + } else { + // No connection metadata; provide some fake information instead + htp_connp_open(*connp, (const char *) "127.0.0.1", 10000, (const char *) "127.0.0.1", 80, &tv_start); + } + + // Find all chunks and feed them to the parser + int in_data_other = 0; + char *in_data = NULL; + size_t in_data_len = 0; + size_t in_data_offset = 0; + + int out_data_other = 0; + char *out_data = NULL; + size_t out_data_len = 0; + size_t out_data_offset = 0; + + for (;;) { + if (test_next_chunk(&test) <= 0) { + break; + } + + if (test.chunk_direction == CLIENT) { + if (in_data_other) { + test_destroy(&test); + fprintf(stderr, "Unable to buffer more than one inbound chunk.\n"); + return -1; + } + + rc = htp_connp_req_data(*connp, &tv_start, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + test_destroy(&test); + return -101; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + in_data_other = 1; + in_data = test.chunk; + in_data_len = test.chunk_len; + in_data_offset = htp_connp_req_data_consumed(*connp); + } + } else { + if (out_data_other) { + rc = htp_connp_res_data(*connp, &tv_start, out_data + out_data_offset, out_data_len - out_data_offset); + if (rc == HTP_STREAM_ERROR) { + test_destroy(&test); + return -104; + } + + out_data_other = 0; + } + + rc = htp_connp_res_data(*connp, &tv_start, test.chunk, test.chunk_len); + if (rc == HTP_STREAM_ERROR) { + test_destroy(&test); + return -102; + } + if (rc == HTP_STREAM_DATA_OTHER) { + // Parser needs to see the outbound stream in order to continue + // parsing the inbound stream. + out_data_other = 1; + out_data = test.chunk; + out_data_len = test.chunk_len; + out_data_offset = htp_connp_res_data_consumed(*connp); + // printf("# YYY out offset is %d\n", out_data_offset); + } + + if (in_data_other) { + rc = htp_connp_req_data(*connp, &tv_start, in_data + in_data_offset, in_data_len - in_data_offset); + if (rc == HTP_STREAM_ERROR) { + test_destroy(&test); + return -103; + } + + in_data_other = 0; + } + } + } + + if (out_data_other) { + rc = htp_connp_res_data(*connp, &tv_start, out_data + out_data_offset, out_data_len - out_data_offset); + if (rc == HTP_STREAM_ERROR) { + test_destroy(&test); + return -104; + } + out_data_other = 0; + } + + gettimeofday(&tv_end, NULL); + + // Close the connection + htp_connp_close(*connp, &tv_end); + + // Clean up + test_destroy(&test); + + return 1; +} + +int test_run(const char *testsdir, const char *testname, htp_cfg_t *cfg, htp_connp_t **connp) { + return test_run_ex(testsdir, testname, cfg, connp, 1); +} diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..bb9ebaf --- /dev/null +++ b/test/test.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#ifndef _TEST_H +#define _TEST_H + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#define UNKNOWN 0 +#define CLIENT 1 +#define SERVER 2 + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct test_t test_t; + +struct test_t { + char *buf; + size_t pos; + size_t len; + + char *chunk; + size_t chunk_offset; + size_t chunk_len; + int chunk_direction; +}; + +int test_run(const char *testsdir, const char *testname, htp_cfg_t *cfg, htp_connp_t **connp); +int test_run_ex(const char *testsdir, const char *testname, htp_cfg_t *cfg, htp_connp_t **connp, int clone_count); + +int test_next_chunk(test_t *test); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEST_H */ + diff --git a/test/test_bench.cpp b/test/test_bench.cpp new file mode 100644 index 0000000..29b6cf7 --- /dev/null +++ b/test/test_bench.cpp @@ -0,0 +1,78 @@ +/***************************************************************************
+ * Copyright (c) 2009-2010 Open Information Security Foundation
+ * Copyright (c) 2010-2013 Qualys, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * - Neither the name of the Qualys, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***************************************************************************/
+
+/**
+ * @file
+ *
+ * @author Ivan Ristic <ivanr@webkreator.com>
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+#include <htp/htp_private.h>
+#include "test.h"
+
+class Benchmark : public testing::Test {
+protected:
+
+ virtual void SetUp() {
+ home = getenv("srcdir");
+ if (home == NULL) {
+ fprintf(stderr, "This program needs environment variable 'srcdir' set.");
+ exit(EXIT_FAILURE);
+ }
+
+ cfg = htp_config_create();
+ htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2);
+ htp_config_register_urlencoded_parser(cfg);
+ htp_config_register_multipart_parser(cfg);
+ }
+
+ virtual void TearDown() {
+ htp_connp_destroy_all(connp);
+ htp_config_destroy(cfg);
+ }
+
+ htp_connp_t *connp;
+
+ htp_cfg_t *cfg;
+
+ char *home;
+};
+
+TEST_F(Benchmark, ConnectionWithManyTransactions) {
+ int rc = test_run_ex(home, "01-get.t", cfg, &connp, 2000);
+ ASSERT_GE(rc, 0);
+
+ ASSERT_EQ(2000, htp_list_size(connp->conn->transactions));
+}
\ No newline at end of file diff --git a/test/test_bstr.cpp b/test/test_bstr.cpp new file mode 100644 index 0000000..791affa --- /dev/null +++ b/test/test_bstr.cpp @@ -0,0 +1,598 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @brief Test for the bstr code. + * + * @author Craig Forbes <cforbes@qualys.com> + */ + +#include <iostream> +#include <gtest/gtest.h> +#include <htp/htp_private.h> + +TEST(BstrTest, Alloc) { + bstr *p1; + p1 = bstr_alloc(10); + EXPECT_EQ(10, bstr_size(p1)); + EXPECT_EQ(0, bstr_len(p1)); + bstr_free(p1); +} + +TEST(BstrTest, ExpandLocal) { + bstr *p1; + bstr *p2; + + p1 = bstr_alloc(10); + p2 = bstr_expand(p1, 100); + ASSERT_NE((void *)NULL, p2); + EXPECT_EQ(100, bstr_size(p2)); + EXPECT_EQ(0, bstr_len(p2)); + + bstr_free(p2); +} + +TEST(BstrTest, ExpandSmaller) { + bstr *p1; + bstr *p2; + + p1 = bstr_alloc(100); + p2 = bstr_expand(p1, 10); + ASSERT_TRUE(p2 == NULL); + + bstr_free(p1); +} + +TEST(BstrTest, ExpandPtr) { + bstr *b; + b = (bstr*) malloc(sizeof(bstr)); + ASSERT_NE((bstr*)NULL, b); + b->realptr = (unsigned char*) malloc(10); + b->len = 0; + b->size = 10; + ASSERT_NE((unsigned char*)NULL, bstr_ptr(b)); + + bstr *p2 = bstr_expand(b, 100); + EXPECT_TRUE(p2 == NULL); + + free(b->realptr); + bstr_free(b); +} + +/* +// For the time being, expansion is not allowed +// when data is externally stored. This feature +// is currently only used when wrapping existing +// memory areas. +TEST(BstrTest, ExpandPtr) { + bstr *b; + b = (bstr*) malloc(sizeof(bstr)); + ASSERT_NE((bstr*)NULL, b); + b->ptr = (unsigned char*) malloc(10); + b->len = 0; + b->size = 10; + ASSERT_NE((unsigned char*)NULL, bstr_ptr(b)); + + bstr *p2; + p2 = bstr_expand(b, 100); + EXPECT_TRUE(p2 != NULL); + EXPECT_EQ(100, bstr_size(p2)); + EXPECT_EQ(0, bstr_len(p2)); + + free(p2->ptr); + bstr_free(p2); +} +*/ + +TEST(BstrTest, DupC) { + bstr *p1; + p1 = bstr_dup_c("arfarf"); + + EXPECT_EQ(6, bstr_size(p1)); + EXPECT_EQ(6, bstr_len(p1)); + EXPECT_EQ(0, memcmp("arfarf", bstr_ptr(p1), 6)); + + bstr_free(p1); +} + +TEST(BstrTest, DupStr) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("s0123456789abcdefghijklmnopqrstuvwxyz"); + p2 = bstr_dup(p1); + + EXPECT_EQ(bstr_len(p1), bstr_len(p2)); + EXPECT_EQ(0, memcmp(bstr_ptr(p1), bstr_ptr(p2), bstr_len(p1))); + + bstr_free(p1); + bstr_free(p2); +} + +TEST(BstrTest, DupBin) { + bstr *src = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + bstr *dst; + dst = bstr_dup(src); + + EXPECT_EQ(bstr_len(src), bstr_len(dst)); + EXPECT_EQ(0, memcmp(bstr_ptr(src), bstr_ptr(dst), bstr_len(src))); + + bstr_free(src); + bstr_free(dst); +} + +TEST(BstrTest, DupEx) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("0123456789abcdefghijkl"); + p2 = bstr_dup_ex(p1, 4, 10); + + EXPECT_EQ(10, bstr_size(p2)); + EXPECT_EQ(10, bstr_len(p2)); + EXPECT_EQ(0, memcmp("456789abcd", bstr_ptr(p2),10)); + + bstr_free(p1); + bstr_free(p2); +} + +TEST(BstrTest, DupMem) { + bstr *dst; + dst = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 18); + EXPECT_EQ(0, memcmp("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", bstr_ptr(dst), 18)); + + bstr_free(dst); +} + +TEST(BstrTest, DupLower) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("0123456789ABCDEFGhIJKL"); + p2 = bstr_dup_lower(p1); + + EXPECT_EQ(0, memcmp("0123456789abcdefghijkl", bstr_ptr(p2), 22)); + + bstr_free(p1); + bstr_free(p2); +} + +TEST(BstrTest, ChrRchr) { + bstr *p1 = bstr_dup_c("0123456789abcdefghijklmnopqrstuvwxyz"); + EXPECT_EQ(13, bstr_chr(p1, 'd')); + EXPECT_EQ(-1, bstr_chr(p1, '?')); + EXPECT_EQ(13, bstr_chr(p1, 'd')); + EXPECT_EQ(-1, bstr_chr(p1, '?')); + + bstr_free(p1); +} + +TEST(BstrTest, Cmp) { + bstr *p1; + bstr *p2; + bstr *p3; + bstr *p4; + p1 = bstr_dup_c("arfarf"); + p2 = bstr_dup_c("arfarf"); + p3 = bstr_dup_c("arfArf"); + p4 = bstr_dup_c("arfarf2"); + + EXPECT_EQ(0, bstr_cmp(p1,p1)); + EXPECT_EQ(0, bstr_cmp(p1,p2)); + EXPECT_EQ(0, bstr_cmp(p2,p1)); + EXPECT_EQ(1, bstr_cmp(p1,p3)); + EXPECT_EQ(-1, bstr_cmp(p3,p1)); + EXPECT_EQ(-1, bstr_cmp(p1,p4)); + EXPECT_EQ(1, bstr_cmp(p4,p1)); + + bstr_free(p1); + bstr_free(p2); + bstr_free(p3); + bstr_free(p4); +} + +TEST(BstrTest, CmpNocase) { + bstr *p1; + bstr *p2; + bstr *p3; + p1 = bstr_dup_c("arfarf"); + p2 = bstr_dup_c("arfarf"); + p3 = bstr_dup_c("arfArf"); + + EXPECT_EQ(0, bstr_cmp_nocase(p1,p1)); + EXPECT_EQ(0, bstr_cmp_nocase(p1,p2)); + EXPECT_EQ(0, bstr_cmp_nocase(p2,p1)); + EXPECT_EQ(0, bstr_cmp_nocase(p1,p3)); + EXPECT_EQ(0, bstr_cmp_nocase(p3,p1)); + + bstr_free(p1); + bstr_free(p2); + bstr_free(p3); +} + +TEST(BstrTest, CmpC) { + bstr *p1; + p1 = bstr_dup_c("arfarf"); + EXPECT_EQ(0, bstr_cmp_c(p1, "arfarf")); + EXPECT_EQ(-1, bstr_cmp_c(p1, "arfarf2")); + EXPECT_EQ(1, bstr_cmp_c(p1, "arf")); + EXPECT_EQ(-1, bstr_cmp_c(p1, "not equal")); + + bstr_free(p1); +} + +TEST(BstrTest, CmpCNocase) { + bstr *p1; + p1 = bstr_dup_c("arfarf"); + EXPECT_EQ(0, bstr_cmp_c_nocase(p1, "arfarf")); + EXPECT_EQ(0, bstr_cmp_c_nocase(p1, "arfARF")); + EXPECT_EQ(1, bstr_cmp_c_nocase(p1, "ArF")); + EXPECT_EQ(-1, bstr_cmp_c_nocase(p1, "Not equal")); + + bstr_free(p1); +} + +TEST(BstrTest, CmpEx) { + const char *s1 = "arfarf12345"; + const char *s2 = "arfarF2345"; + + EXPECT_EQ(0, bstr_util_cmp_mem(s1, 5, s2, 5)); + EXPECT_EQ(1, bstr_util_cmp_mem(s1, 6, s2, 6)); + EXPECT_EQ(1, bstr_util_cmp_mem(s1, 5, s2, 4)); + EXPECT_EQ(-1, bstr_util_cmp_mem(s2, 4, s1, 5)); +} + +TEST(BstrTest, CmpNocaseEx) { + const char *s1 = "arfarf12345"; + const char *s2 = "arfarF2345"; + + EXPECT_EQ(0, bstr_util_cmp_mem_nocase(s1, 6, s2, 6)); + EXPECT_EQ(1, bstr_util_cmp_mem_nocase(s1, 6, s2, 5)); + EXPECT_EQ(-1, bstr_util_cmp_mem_nocase(s2, 5, s1, 6)); +} + +TEST(BstrTest, CmpMem) { + bstr *s = bstr_dup_c("arfArf"); + EXPECT_EQ(0, bstr_cmp_mem(s, "arfArf", 6)); + bstr_free(s); +} + +TEST(BstrTest, ToLowercase) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("aRf3ArF"); + p2 = bstr_to_lowercase(p1); + + EXPECT_EQ(p1, p2); + EXPECT_EQ(1, bstr_cmp_c(p1, "aRf3ArF")); + EXPECT_EQ(0, bstr_cmp_c(p1, "arf3arf")); + + bstr_free(p1); +} + +TEST(BstrTest, Add) { + bstr *src1; + bstr *src2; + bstr *dest; + + src1 = bstr_dup_c("testtest"); + src2 = bstr_dup_c("0123456789abcdefghijklmnopqrstuvwxyz"); + dest = bstr_add(src1, src2); + + EXPECT_EQ(0, bstr_cmp_c(dest, "testtest0123456789abcdefghijklmnopqrstuvwxyz")); + + // src1 is either invalid or the same as dest after bstr_add + bstr_free(src2); + bstr_free(dest); +} + +TEST(BstrTest, AddC) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("testtest"); + p2 = bstr_add_c(p1, "1234"); + + EXPECT_EQ(0, bstr_cmp_c(p2, "testtest1234")); + + bstr_free(p2); +} + +TEST(BstrTest, AddMem) { + bstr *p1; + bstr *p2; + p1 = bstr_dup_c("testtest"); + p2 = bstr_add_mem(p1, "12345678", 4); + + EXPECT_EQ(0, bstr_cmp_c(p2, "testtest1234")); + + bstr_free(p2); +} + +TEST(BstrTest, AddNoex) { + bstr *p1; + bstr *p2; + bstr *p3; + p1 = bstr_alloc(10); + p1 = bstr_add_c(p1, "12345"); + p2 = bstr_dup_c("abcdef"); + p3 = bstr_add_noex(p1,p2); + + EXPECT_EQ(p1,p3); + EXPECT_EQ(0,bstr_cmp_c(p3,"12345abcde")); + bstr_free(p1); + bstr_free(p2); +} + +TEST(BstrTest, AddCNoex) { + bstr *p1; + bstr *p2; + p1 = bstr_alloc(10); + p1 = bstr_add_c(p1, "12345"); + p2 = bstr_add_c_noex(p1,"abcdefghijk"); + + EXPECT_EQ(p1,p2); + EXPECT_EQ(0,bstr_cmp_c(p2,"12345abcde")); + + bstr_free(p1); +} + +TEST(BstrTest, AddMemNoex) { + bstr *p1; + bstr *p2; + p1 = bstr_alloc(10); + p1 = bstr_add_c(p1, "12345"); + p2 = bstr_add_mem_noex(p1,"abcdefghijklmnop",6); + + EXPECT_EQ(p1,p2); + EXPECT_EQ(0,bstr_cmp_c(p2,"12345abcde")); + + bstr_free(p1); +} + +TEST(BstrTest, IndexOf) { + bstr *haystack = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + bstr *p1 = bstr_dup_c("NOPQ"); + bstr *p2 = bstr_dup_c("siej"); + bstr *p3 = bstr_dup_c("TUVWXYZ"); + bstr *p4 = bstr_dup_c("nopq"); + EXPECT_EQ(13, bstr_index_of(haystack, p1)); + EXPECT_EQ(-1, bstr_index_of(haystack, p2)); + EXPECT_EQ(-1, bstr_index_of(haystack, p3)); + + EXPECT_EQ(-1, bstr_index_of(haystack, p4)); + EXPECT_EQ(13, bstr_index_of_nocase(haystack, p4)); + + EXPECT_EQ(16, bstr_index_of_c(haystack, "QRS")); + EXPECT_EQ(-1, bstr_index_of_c(haystack, "qrs")); + EXPECT_EQ(16, bstr_index_of_c_nocase(haystack, "qrs")); + + EXPECT_EQ(16, bstr_index_of_mem(haystack, "QRSSDF",3)); + EXPECT_EQ(-1, bstr_index_of_mem(haystack, "qrssdf",3)); + EXPECT_EQ(16, bstr_index_of_mem_nocase(haystack, "qrssdf",3)); + + bstr_free(p1); + bstr_free(p2); + bstr_free(p3); + bstr_free(p4); + bstr_free(haystack); +} + +TEST(BstrTest, MemIndexOf) { + EXPECT_EQ(0, bstr_util_mem_index_of_c("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20, "ABC")); + EXPECT_EQ(-1, bstr_util_mem_index_of_c("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20, "ABD")); + EXPECT_EQ(-1, bstr_util_mem_index_of_c("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20, "CBA")); +} + +TEST(BstrTest, BeginsWith) { + bstr *haystack = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + bstr *p1 = bstr_dup_c("ABCD"); + bstr *p2 = bstr_dup_c("aBcD"); + + EXPECT_EQ(1, bstr_begins_with(haystack,p1)); + EXPECT_NE(1, bstr_begins_with(haystack,p2)); + EXPECT_EQ(1, bstr_begins_with_nocase(haystack,p2)); + + EXPECT_EQ(1, bstr_begins_with_c(haystack, "AB")); + EXPECT_NE(1, bstr_begins_with_c(haystack, "ab")); + EXPECT_EQ(1, bstr_begins_with_c_nocase(haystack, "ab")); + + EXPECT_EQ(1, bstr_begins_with_mem(haystack, "ABq",2)); + EXPECT_NE(1, bstr_begins_with_mem(haystack, "abq",2)); + EXPECT_EQ(1, bstr_begins_with_mem_nocase(haystack, "abq",2)); + + bstr_free(p1); + bstr_free(p2); + bstr_free(haystack); +} + +TEST(BstrTest, BeginsWith2) { + bstr *haystack = bstr_dup_c("ABC"); + bstr *p1 = bstr_dup_c("ABCD"); + bstr *p2 = bstr_dup_c("EDFG"); + + EXPECT_EQ(0, bstr_begins_with_mem(haystack, bstr_ptr(p1), bstr_len(p1))); + EXPECT_EQ(0, bstr_begins_with_mem_nocase(haystack, bstr_ptr(p1), bstr_len(p1))); + EXPECT_EQ(0, bstr_begins_with_mem_nocase(haystack, bstr_ptr(p2), bstr_len(p2))); + + bstr_free(p1); + bstr_free(p2); + bstr_free(haystack); +} + +TEST(BstrTest, CharAt) { + bstr *str = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + EXPECT_EQ('\000', bstr_char_at(str, 12)); + EXPECT_EQ(-1, bstr_char_at(str, 45)); + + bstr_free(str); +} + +TEST(BstrTest, CharAtEnd) { + bstr *str = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + EXPECT_EQ('T', bstr_char_at_end(str, 0)); + EXPECT_EQ('\000', bstr_char_at_end(str, 7)); + EXPECT_EQ(-1, bstr_char_at_end(str, bstr_len(str))); + + bstr_free(str); +} + +TEST(BstrTest, Chop) { + bstr *p1 = bstr_dup_c("abcdef"); + bstr *p2 = bstr_alloc(10); + bstr_chop(p1); + EXPECT_EQ(0, bstr_cmp_c(p1,"abcde")); + + bstr_chop(p2); + EXPECT_EQ(0, bstr_len(p2)); + + bstr_free(p1); + bstr_free(p2); +} + +TEST(BstrTest, AdjustLen) { + bstr *p1 = bstr_dup_c("abcdef"); + + bstr_adjust_len(p1, 3); + EXPECT_EQ(3, bstr_len(p1)); + EXPECT_EQ(0, bstr_cmp_c(p1,"abc")); + + bstr_free(p1); +} + +TEST(BstrTest, ToPint) { + size_t lastlen; + + EXPECT_EQ(-1, bstr_util_mem_to_pint("abc", 3, 10, &lastlen)); + EXPECT_EQ(-2, bstr_util_mem_to_pint("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 40, 16, &lastlen)); + EXPECT_EQ(0x7fffffffffffffffLL, bstr_util_mem_to_pint("7fffffffffffffff", 16, 16, &lastlen)); + EXPECT_EQ(-2, bstr_util_mem_to_pint("9223372036854775808", 19, 10, &lastlen)); + EXPECT_EQ(-2, bstr_util_mem_to_pint("555555555555555555555555555555", 30, 10, &lastlen)); + EXPECT_EQ(0xabc, bstr_util_mem_to_pint("abc", 3, 16, &lastlen)); + EXPECT_EQ(4, lastlen); + EXPECT_EQ(0xabc, bstr_util_mem_to_pint("ABC", 3, 16, &lastlen)); + EXPECT_EQ(131, bstr_util_mem_to_pint("abc", 3, 12, &lastlen)); + EXPECT_EQ(2, lastlen); + EXPECT_EQ(83474, bstr_util_mem_to_pint("83474abc", 8, 10, &lastlen)); + EXPECT_EQ(5, lastlen); + EXPECT_EQ(5, bstr_util_mem_to_pint("0101", 4, 2, &lastlen)); + EXPECT_EQ(5, lastlen); + EXPECT_EQ(5, bstr_util_mem_to_pint("0101", 4, 2, &lastlen)); + EXPECT_EQ(5, lastlen); +} + +TEST(BstrTest, DupToC) { + char *c; + bstr *str = bstr_dup_mem("ABCDEFGHIJKL\000NOPQRSTUVWXYZ", 20); + + c = bstr_util_memdup_to_c("1234\0006789", 9); + EXPECT_STREQ("1234\\06789", c); + free(c); + + c = bstr_util_strdup_to_c(str); + EXPECT_STREQ("ABCDEFGHIJKL\\0NOPQRST", c); + + free(c); + bstr_free(str); +} + +TEST(BstrTest, RChr) { + bstr *b = bstr_dup_c("---I---I---"); + + EXPECT_EQ(bstr_rchr(b, 'I'), 7); + EXPECT_EQ(bstr_rchr(b, 'M'), -1); + + bstr_free(b); +} + +TEST(BstrTest, AdjustRealPtr) { + bstr *b = bstr_dup_c("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + char c[] = "0123456789"; + + bstr_adjust_realptr(b, c); + bstr_adjust_len(b, strlen(c)); + + EXPECT_TRUE((char *)bstr_ptr(b) == c); + + bstr_free(b); +} + +TEST(BstrTest, UtilMemTrim) { + char d[] = " \r\t0123456789\f\v "; + char *data = &d[0]; + size_t len = strlen(data); + + bstr_util_mem_trim((unsigned char **)&data, &len); + + EXPECT_EQ(0, bstr_util_cmp_mem(data, len, "0123456789", 10)); +} + +TEST(BstrTest, Wrap) { + bstr *s = bstr_wrap_c("ABC"); + EXPECT_EQ(0, bstr_cmp_mem(s, "ABC", 3)); + bstr_free(s); +} + +TEST(BstrBuilder, CreateDestroy) { + bstr_builder_t *bb = bstr_builder_create(); + EXPECT_EQ(0, bstr_builder_size(bb)); + + bstr_builder_append_c(bb, "ABC"); + + bstr_builder_destroy(bb); +} + +TEST(BstrBuilder, Append) { + bstr_builder_t *bb = bstr_builder_create(); + bstr *str1 = bstr_dup_c("0123456789"); + bstr *str2 = bstr_dup_c("abcdefghijklmnopqrstuvwxyz"); + + EXPECT_EQ(0, bstr_builder_size(bb)); + + bstr_builder_appendn(bb, str1); + bstr_builder_append_c(bb, "#"); + bstr_builder_appendn(bb, str2); + bstr_builder_append_c(bb, "#"); + bstr_builder_append_mem(bb, "!@#$%^&*()", 4); + + EXPECT_EQ(5, bstr_builder_size(bb)); + + bstr *result = bstr_builder_to_str(bb); + EXPECT_EQ(42, bstr_len(result)); + + EXPECT_EQ(0, memcmp("0123456789#abcdefghijklmnopqrstuvwxyz#!@#$", + bstr_ptr(result),42)); + bstr_free(result); + + bstr_builder_clear(bb); + EXPECT_EQ(0, bstr_builder_size(bb)); + + bstr_builder_destroy(bb); +} diff --git a/test/test_gunzip.cpp b/test/test_gunzip.cpp new file mode 100644 index 0000000..5c081ab --- /dev/null +++ b/test/test_gunzip.cpp @@ -0,0 +1,270 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <iostream> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <gtest/gtest.h> +#include <htp/htp_private.h> +#include <htp/htp_decompressors.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static htp_status_t GUnzip_decompressor_callback(htp_tx_data_t *d) { + bstr **output = (bstr **) htp_tx_get_user_data(d->tx); + *output = bstr_dup_mem(d->data, d->len); + + return HTP_OK; +} + +class GUnzip : public testing::Test { +protected: + + virtual htp_status_t decompressFile(const char *f) { + // Construct complete file name + + char filename[1025]; + strncpy(filename, home, 1024); + strncat(filename, "/", 1024 - strlen(filename)); + strncat(filename, f, 1024 - strlen(filename)); + + // Load test data + + int fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + //FAIL() << "Unable to open test file"; + return HTP_ERROR; + } + + struct stat statbuf; + if (fstat(fd, &statbuf) < 0) { + //FAIL() << "Unable to stat test file"; + return HTP_ERROR; + } + + htp_tx_data_t d; + d.tx = tx; + d.len = statbuf.st_size; + d.data = (const unsigned char *) malloc(d.len); + if (d.data == NULL) { + //FAIL() << "Memory allocation failed"; + return HTP_ERROR; + } + + ssize_t bytes_read = read(fd, (void *) d.data, d.len); + if ((bytes_read < 0)||((size_t)bytes_read != d.len)) { + //FAIL() << "Reading from test file failed"; + close(fd); + return HTP_ERROR; + } + + close(fd); + + // Decompress + + htp_status_t rc = htp_gzip_decompressor_decompress(decompressor, &d); + + free((void *)d.data); + + return rc; + } + + virtual void SetUp() { + home = getenv("srcdir"); + if (home == NULL) { + fprintf(stderr, "This program needs environment variable 'srcdir' set."); + exit(EXIT_FAILURE); + } + + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2); + + connp = htp_connp_create(cfg); + tx = htp_connp_tx_create(connp); + htp_tx_set_user_data(tx, &output); + + decompressor = htp_gzip_decompressor_create(connp, HTP_COMPRESSION_GZIP); + decompressor->callback = GUnzip_decompressor_callback; + + o_boxing_wizards = bstr_dup_c("The five boxing wizards jump quickly."); + output = NULL; + } + + virtual void TearDown() { + bstr_free(output); + bstr_free(o_boxing_wizards); + htp_gzip_decompressor_destroy(decompressor); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + } + + bstr *output; + + bstr *o_boxing_wizards; + + htp_connp_t *connp; + + htp_tx_t *tx; + + htp_cfg_t *cfg; + + char *home; + + htp_decompressor_t *decompressor; +}; + +TEST_F(GUnzip, Minimal) { + htp_status_t rc = decompressFile("gztest-01-minimal.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, FNAME) { + htp_status_t rc = decompressFile("gztest-02-fname.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +#if 0 + +TEST_F(GUnzip, FCOMMENT) { + htp_status_t rc = decompressFile("gztest-03-fcomment.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, FHCRC) { + htp_status_t rc = decompressFile("gztest-04-fhcrc.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} +#endif + +TEST_F(GUnzip, FEXTRA) { + htp_status_t rc = decompressFile("gztest-05-fextra.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, FTEXT) { + htp_status_t rc = decompressFile("gztest-06-ftext.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +#if 0 + +TEST_F(GUnzip, FRESERVED1) { + htp_status_t rc = decompressFile("gztest-07-freserved1.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, FRESERVED2) { + htp_status_t rc = decompressFile("gztest-08-freserved2.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, FRESERVED3) { + htp_status_t rc = decompressFile("gztest-09-freserved3.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} +#endif + +TEST_F(GUnzip, Multipart) { + htp_status_t rc = decompressFile("gztest-10-multipart.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +#if 0 + +TEST_F(GUnzip, InvalidMethod) { + htp_status_t rc = decompressFile("gztest-11-invalid-method.gz.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, InvalidCrc) { + htp_status_t rc = decompressFile("gztest-12-invalid-crc32.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, InvalidInputSize) { + htp_status_t rc = decompressFile("gztest-13-invalid-isize.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} +#endif + +TEST_F(GUnzip, InvalidExtraFlags) { + htp_status_t rc = decompressFile("gztest-14-invalid-xfl.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} + +TEST_F(GUnzip, InvalidHeaderCrc) { + htp_status_t rc = decompressFile("gztest-15-invalid-fhcrc.gz"); + ASSERT_EQ(rc, HTP_OK); + ASSERT_TRUE(output != NULL); + ASSERT_TRUE(bstr_cmp(o_boxing_wizards, output) == 0); +} diff --git a/test/test_hybrid.cpp b/test/test_hybrid.cpp new file mode 100644 index 0000000..d807b57 --- /dev/null +++ b/test/test_hybrid.cpp @@ -0,0 +1,873 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <iostream> +#include <gtest/gtest.h> +#include <htp/htp_private.h> +#include "test.h" + +class HybridParsing_Get_User_Data { + +public: + + // Request callback indicators. + int callback_REQUEST_START_invoked; + int callback_REQUEST_LINE_invoked; + int callback_REQUEST_HEADERS_invoked; + int callback_REQUEST_COMPLETE_invoked; + + // Response callback indicators. + int callback_RESPONSE_START_invoked; + int callback_RESPONSE_LINE_invoked; + int callback_RESPONSE_HEADERS_invoked; + int callback_RESPONSE_COMPLETE_invoked; + + // Transaction callback indicators. + int callback_TRANSACTION_COMPLETE_invoked; + + // Response body handling fields. + int response_body_chunks_seen; + int response_body_correctly_received; + + HybridParsing_Get_User_Data() { + Reset(); + } + + void Reset() { + this->callback_REQUEST_START_invoked = 0; + this->callback_REQUEST_LINE_invoked = 0; + this->callback_REQUEST_HEADERS_invoked = 0; + this->callback_REQUEST_COMPLETE_invoked = 0; + this->callback_RESPONSE_START_invoked = 0; + this->callback_RESPONSE_LINE_invoked = 0; + this->callback_RESPONSE_HEADERS_invoked = 0; + this->callback_RESPONSE_COMPLETE_invoked = 0; + this->callback_TRANSACTION_COMPLETE_invoked = 0; + this->response_body_chunks_seen = 0; + this->response_body_correctly_received = 0; + } +}; + +static int HybridParsing_Get_Callback_REQUEST_START(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_REQUEST_START_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_REQUEST_LINE(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_REQUEST_LINE_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_REQUEST_HEADERS(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_REQUEST_HEADERS_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_REQUEST_COMPLETE(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_REQUEST_COMPLETE_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_RESPONSE_START(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_RESPONSE_START_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_RESPONSE_LINE(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_RESPONSE_LINE_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_RESPONSE_HEADERS(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_RESPONSE_HEADERS_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_RESPONSE_BODY_DATA(htp_tx_data_t *d) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(d->tx); + + // Don't do anything if in errored state. + if (user_data->response_body_correctly_received == -1) return HTP_ERROR; + + switch (user_data->response_body_chunks_seen) { + case 0: + if ((d->len == 9) && (memcmp(d->data, "<h1>Hello", 9) == 0)) { + user_data->response_body_chunks_seen++; + } else { + SCOPED_TRACE("Mismatch in 1st chunk"); + user_data->response_body_correctly_received = -1; + } + break; + case 1: + if ((d->len == 1) && (memcmp(d->data, " ", 1) == 0)) { + user_data->response_body_chunks_seen++; + } else { + SCOPED_TRACE("Mismatch in 2nd chunk"); + user_data->response_body_correctly_received = -1; + } + break; + case 2: + if ((d->len == 11) && (memcmp(d->data, "World!</h1>", 11) == 0)) { + user_data->response_body_chunks_seen++; + user_data->response_body_correctly_received = 1; + } else { + SCOPED_TRACE("Mismatch in 3rd chunk"); + user_data->response_body_correctly_received = -1; + } + break; + default: + SCOPED_TRACE("Seen more than 3 chunks"); + user_data->response_body_correctly_received = -1; + break; + } + + return HTP_OK; +} + +static int HybridParsing_Get_Callback_RESPONSE_COMPLETE(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_RESPONSE_COMPLETE_invoked; + return HTP_OK; +} + +static int HybridParsing_Get_Callback_TRANSACTION_COMPLETE(htp_tx_t *tx) { + struct HybridParsing_Get_User_Data *user_data = (struct HybridParsing_Get_User_Data *) htp_tx_get_user_data(tx); + ++user_data->callback_TRANSACTION_COMPLETE_invoked; + return HTP_OK; +} + +class HybridParsing : public testing::Test { + +protected: + + virtual void SetUp() { + testing::Test::SetUp(); + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2); + htp_config_register_urlencoded_parser(cfg); + htp_config_register_multipart_parser(cfg); + + connp = htp_connp_create(cfg); + htp_connp_open(connp, "127.0.0.1", 32768, "127.0.0.1", 80, NULL); + connp_open = true; + user_data.Reset(); + } + + virtual void TearDown() { + CloseConnParser( ); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + testing::Test::TearDown(); + } + + void CloseConnParser() { + if (connp_open) { + htp_connp_close(connp, NULL); + connp_open = false; + } + } + + void RegisterUserCallbacks() { + // Request callbacks + htp_config_register_request_start(cfg, HybridParsing_Get_Callback_REQUEST_START); + htp_config_register_request_line(cfg, HybridParsing_Get_Callback_REQUEST_LINE); + htp_config_register_request_headers(cfg, HybridParsing_Get_Callback_REQUEST_HEADERS); + htp_config_register_request_complete(cfg, HybridParsing_Get_Callback_REQUEST_COMPLETE); + + // Response callbacks + htp_config_register_response_start(cfg, HybridParsing_Get_Callback_RESPONSE_START); + htp_config_register_response_line(cfg, HybridParsing_Get_Callback_RESPONSE_LINE); + htp_config_register_response_headers(cfg, HybridParsing_Get_Callback_RESPONSE_HEADERS); + htp_config_register_response_body_data(cfg, HybridParsing_Get_Callback_RESPONSE_BODY_DATA); + htp_config_register_response_complete(cfg, HybridParsing_Get_Callback_RESPONSE_COMPLETE); + + // Transaction calllbacks + htp_config_register_transaction_complete(cfg, HybridParsing_Get_Callback_TRANSACTION_COMPLETE); + } + + htp_connp_t *connp; + htp_cfg_t *cfg; + bool connp_open; + + // This must not be in a test stack frame as it will persist to TearDown + // as htp user data. + HybridParsing_Get_User_Data user_data; +}; + +/** + * Test hybrid mode with one complete GET transaction; request then response + * with a body. Most features are tested, including query string parameters and callbacks. + */ +TEST_F(HybridParsing, GetTest) { + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Configure user data and callbacks + htp_tx_set_user_data(tx, &user_data); + + // Register callbacks + RegisterUserCallbacks(); + + // Request begins + htp_tx_state_request_start(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_START_invoked); + + // Request line data + htp_tx_req_set_method(tx, "GET", 3, HTP_ALLOC_COPY); + htp_tx_req_set_method_number(tx, HTP_M_GET); + htp_tx_req_set_uri(tx, "/?p=1&q=2", 9, HTP_ALLOC_COPY); + htp_tx_req_set_protocol(tx, "HTTP/1.1", 8, HTP_ALLOC_COPY); + htp_tx_req_set_protocol_number(tx, HTP_PROTOCOL_1_1); + htp_tx_req_set_protocol_0_9(tx, 0); + + // Request line complete + htp_tx_state_request_line(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_LINE_invoked); + + // Check request line data + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=1&q=2")); + ASSERT_TRUE(tx->request_protocol != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_protocol, "HTTP/1.1")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/")); + + ASSERT_TRUE(tx->parsed_uri->query != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->query, "p=1&q=2")); + + // Check parameters + htp_param_t *param_p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + htp_param_t *param_q = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); + + // Request headers + htp_tx_req_set_header(tx, "Host", 4, "www.example.com", 15, HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "Connection", 10, "keep-alive", 10, HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "User-Agent", 10, "Mozilla/5.0", 11, HTP_ALLOC_COPY); + + // Request headers complete + htp_tx_state_request_headers(tx); + + // Check headers + ASSERT_EQ(1, user_data.callback_REQUEST_HEADERS_invoked); + + htp_header_t *h_host = (htp_header_t *) htp_table_get_c(tx->request_headers, "host"); + ASSERT_TRUE(h_host != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_host->value, "www.example.com")); + + htp_header_t *h_connection = (htp_header_t *) htp_table_get_c(tx->request_headers, "connection"); + ASSERT_TRUE(h_connection != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_connection->value, "keep-alive")); + + htp_header_t *h_ua = (htp_header_t *) htp_table_get_c(tx->request_headers, "user-agent"); + ASSERT_TRUE(h_ua != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_ua->value, "Mozilla/5.0")); + + // Request complete + htp_tx_state_request_complete(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_COMPLETE_invoked); + + // Response begins + htp_tx_state_response_start(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_START_invoked); + + // Response line data + htp_tx_res_set_status_line(tx, "HTTP/1.1 200 OK", 15, HTP_ALLOC_COPY); + ASSERT_EQ(0, bstr_cmp_c(tx->response_protocol, "HTTP/1.1")); + ASSERT_EQ(HTP_PROTOCOL_1_1, tx->response_protocol_number); + ASSERT_EQ(200, tx->response_status_number); + ASSERT_EQ(0, bstr_cmp_c(tx->response_message, "OK")); + + htp_tx_res_set_protocol_number(tx, HTP_PROTOCOL_1_0); + ASSERT_EQ(HTP_PROTOCOL_1_0, tx->response_protocol_number); + + htp_tx_res_set_status_code(tx, 500); + ASSERT_EQ(500, tx->response_status_number); + + htp_tx_res_set_status_message(tx, "Internal Server Error", 21, HTP_ALLOC_COPY); + ASSERT_EQ(0, bstr_cmp_c(tx->response_message, "Internal Server Error")); + + // Response line complete + htp_tx_state_response_line(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_LINE_invoked); + + // Response header data + htp_tx_res_set_header(tx, "Content-Type", 12, "text/html", 9, HTP_ALLOC_COPY); + htp_tx_res_set_header(tx, "Server", 6, "Apache", 6, HTP_ALLOC_COPY); + + // Response headers complete + htp_tx_state_response_headers(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_HEADERS_invoked); + + // Check response headers + htp_header_t *h_content_type = (htp_header_t *) htp_table_get_c(tx->response_headers, "content-type"); + ASSERT_TRUE(h_content_type != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_content_type->value, "text/html")); + + htp_header_t *h_server = (htp_header_t *) htp_table_get_c(tx->response_headers, "server"); + ASSERT_TRUE(h_server != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_server->value, "Apache")); + + // Response body data + htp_tx_res_process_body_data(tx, "<h1>Hello", 9); + htp_tx_res_process_body_data(tx, " ", 1); + htp_tx_res_process_body_data(tx, "World!</h1>", 11); + ASSERT_EQ(1, user_data.response_body_correctly_received); + + // Check that the API is rejecting NULL data. + ASSERT_EQ(HTP_ERROR, htp_tx_res_process_body_data(tx, NULL, 1)); + + // Trailing response headers + htp_tx_res_set_headers_clear(tx); + ASSERT_EQ(0, htp_table_size(tx->response_headers)); + + htp_tx_res_set_header(tx, "Content-Type", 12, "text/html", 9, HTP_ALLOC_COPY); + htp_tx_res_set_header(tx, "Server", 6, "Apache", 6, HTP_ALLOC_COPY); + + // Check trailing response headers + h_content_type = (htp_header_t *) htp_table_get_c(tx->response_headers, "content-type"); + ASSERT_TRUE(h_content_type != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_content_type->value, "text/html")); + + h_server = (htp_header_t *) htp_table_get_c(tx->response_headers, "server"); + ASSERT_TRUE(h_server != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_server->value, "Apache")); + + htp_tx_state_response_complete(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_COMPLETE_invoked); +} + +/** + * Use a POST request in order to test request body processing and parameter parsing. + */ +TEST_F(HybridParsing, PostUrlecodedTest) { + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Request begins + htp_tx_state_request_start(tx); + + // Request line data + htp_tx_req_set_method(tx, "POST", 4, HTP_ALLOC_COPY); + htp_tx_req_set_method_number(tx, HTP_M_GET); + htp_tx_req_set_uri(tx, "/", 1, HTP_ALLOC_COPY); + htp_tx_req_set_protocol(tx, "HTTP/1.1", 8, HTP_ALLOC_COPY); + htp_tx_req_set_protocol_number(tx, HTP_PROTOCOL_1_1); + htp_tx_req_set_protocol_0_9(tx, 0); + + // Request line complete + htp_tx_state_request_line(tx); + + // Configure headers to trigger the URLENCODED parser + htp_tx_req_set_header(tx, "Content-Type", 12, HTP_URLENCODED_MIME_TYPE, + strlen(HTP_URLENCODED_MIME_TYPE), HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "Content-Length", 14, "7", 1, HTP_ALLOC_COPY); + + // Request headers complete + htp_tx_state_request_headers(tx); + + // Send request body + htp_tx_req_process_body_data(tx, "p=1", 3); + htp_tx_req_process_body_data(tx, NULL, 0); + htp_tx_req_process_body_data(tx, "&", 1); + htp_tx_req_process_body_data(tx, "q=2", 3); + + // Check that the API is rejecting NULL data. + ASSERT_EQ(HTP_ERROR, htp_tx_req_process_body_data(tx, NULL, 1)); + + // Trailing request headers + htp_tx_req_set_headers_clear(tx); + ASSERT_EQ(0, htp_table_size(tx->request_headers)); + + htp_tx_req_set_header(tx, "Host", 4, "www.example.com", 15, HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "Connection", 10, "keep-alive", 10, HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "User-Agent", 10, "Mozilla/5.0", 11, HTP_ALLOC_COPY); + + htp_header_t *h_host = (htp_header_t *) htp_table_get_c(tx->request_headers, "host"); + ASSERT_TRUE(h_host != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_host->value, "www.example.com")); + + htp_header_t *h_connection = (htp_header_t *) htp_table_get_c(tx->request_headers, "connection"); + ASSERT_TRUE(h_connection != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_connection->value, "keep-alive")); + + htp_header_t *h_ua = (htp_header_t *) htp_table_get_c(tx->request_headers, "user-agent"); + ASSERT_TRUE(h_ua != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_ua->value, "Mozilla/5.0")); + + // Request complete + htp_tx_state_request_complete(tx); + + // Check parameters + + htp_param_t *param_p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + htp_param_t *param_q = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); +} + +static char HybridParsing_CompressedResponse[] = + "H4sIAAAAAAAAAG2PwQ6CMBBE73xFU++tXk2pASliAiEhPegRYUOJYEktEP5eqB6dy2ZnJ5O3LJFZ" + "yj2WiCBah7zKVPBMT1AjCf2gTWnabmH0e/AY/QXDPLqj8HLO07zw8S52wkiKm1zXvRPeeg//2lbX" + "kwpQrauxh5dFqnyj3uVYgJJCxD5W1g5HSud5Jo3WTQek0mR8UgNlDYZOLcz0ZMuH3y+YKzDAaMDJ" + "SrihOVL32QceVXUy4QAAAA=="; + +static void HybridParsing_CompressedResponse_Setup(htp_tx_t *tx) { + htp_tx_state_request_start(tx); + + htp_tx_req_set_method(tx, "GET", 3, HTP_ALLOC_REUSE); + htp_tx_req_set_method_number(tx, HTP_M_GET); + htp_tx_req_set_uri(tx, "/", 1, HTP_ALLOC_COPY); + htp_tx_req_set_protocol(tx, "HTTP/1.1", 8, HTP_ALLOC_REUSE); + htp_tx_req_set_protocol_number(tx, HTP_PROTOCOL_1_1); + htp_tx_req_set_protocol_0_9(tx, 0); + + htp_tx_state_request_line(tx); + htp_tx_state_request_headers(tx); + htp_tx_state_request_complete(tx); + + htp_tx_state_response_start(tx); + + htp_tx_res_set_status_line(tx, "HTTP/1.1 200 OK", 15, HTP_ALLOC_REUSE); + htp_tx_res_set_header(tx, "Content-Encoding", 16, "gzip", 4, HTP_ALLOC_REUSE); + htp_tx_res_set_header(tx, "Content-Length", 14, "187", 3, HTP_ALLOC_REUSE); + + htp_tx_state_response_headers(tx); + + bstr *body = htp_base64_decode_mem(HybridParsing_CompressedResponse, strlen(HybridParsing_CompressedResponse)); + ASSERT_TRUE(body != NULL); + + htp_tx_res_process_body_data(tx, bstr_ptr(body), bstr_len(body)); + bstr_free(body); + + htp_tx_state_response_complete(tx); +} + +/** + * Test with a compressed response body and decompression enabled. + */ +TEST_F(HybridParsing, CompressedResponse) { + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + HybridParsing_CompressedResponse_Setup(tx); + + ASSERT_EQ(187, tx->response_message_len); + ASSERT_EQ(225, tx->response_entity_len); +} + +/** + * Test with a compressed response body and decompression disabled. + */ +TEST_F(HybridParsing, CompressedResponseNoDecompression) { + // Disable decompression + htp_config_set_response_decompression(cfg, 0); + + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + HybridParsing_CompressedResponse_Setup(tx); + + ASSERT_EQ(187, tx->response_message_len); + ASSERT_EQ(187, tx->response_entity_len); +} + +static int HybridParsing_ForcedDecompressionTest_Callback_RESPONSE_HEADERS(htp_tx_t *tx) { + tx->response_content_encoding_processing = HTP_COMPRESSION_GZIP; + return HTP_OK; +} + +/** + * Test forced decompression. + */ +TEST_F(HybridParsing, ForcedDecompression) { + // Disable decompression + htp_config_set_response_decompression(cfg, 0); + + // Register a callback that will force decompression + htp_config_register_response_headers(cfg, HybridParsing_ForcedDecompressionTest_Callback_RESPONSE_HEADERS); + + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + HybridParsing_CompressedResponse_Setup(tx); + + ASSERT_EQ(187, tx->response_message_len); + ASSERT_EQ(225, tx->response_entity_len); +} + +static int HybridParsing_DisableDecompressionTest_Callback_RESPONSE_HEADERS(htp_tx_t *tx) { + tx->response_content_encoding_processing = HTP_COMPRESSION_NONE; + return HTP_OK; +} + +/** + * Test disabling decompression from a callback. + */ +TEST_F(HybridParsing, DisableDecompression) { + // Disable decompression + htp_config_set_response_decompression(cfg, 0); + + // Register a callback that will force decompression + htp_config_register_response_headers(cfg, HybridParsing_DisableDecompressionTest_Callback_RESPONSE_HEADERS); + + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + HybridParsing_CompressedResponse_Setup(tx); + + ASSERT_EQ(187, tx->response_message_len); + ASSERT_EQ(187, tx->response_entity_len); +} + +TEST_F(HybridParsing, ParamCaseSensitivity) { + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Request begins + htp_tx_state_request_start(tx); + + // Request line data + htp_tx_req_set_method(tx, "GET", 3, HTP_ALLOC_COPY); + htp_tx_req_set_method_number(tx, HTP_M_GET); + htp_tx_req_set_uri(tx, "/?p=1&Q=2", 9, HTP_ALLOC_COPY); + htp_tx_req_set_protocol(tx, "HTTP/1.1", 8, HTP_ALLOC_COPY); + htp_tx_req_set_protocol_number(tx, HTP_PROTOCOL_1_1); + htp_tx_req_set_protocol_0_9(tx, 0); + + // Request line complete + htp_tx_state_request_line(tx); + + // Check the parameters. + + htp_param_t *param_p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + param_p = htp_tx_req_get_param(tx, "P", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + htp_param_t *param_q = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); + + param_q = htp_tx_req_get_param_ex(tx, HTP_SOURCE_QUERY_STRING, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); + + param_q = htp_tx_req_get_param_ex(tx, HTP_SOURCE_QUERY_STRING, "Q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); +} + +/** + * Use a POST request in order to test request body processing and parameter + * parsing. In hybrid mode, we expect that the body arrives to us dechunked. + */ +TEST_F(HybridParsing, PostUrlecodedChunked) { + // Create a new LibHTP transaction. + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Request begins. + htp_tx_state_request_start(tx); + + // Request line data. + htp_tx_req_set_method(tx, "POST", 4, HTP_ALLOC_COPY); + htp_tx_req_set_method_number(tx, HTP_M_GET); + htp_tx_req_set_uri(tx, "/", 1, HTP_ALLOC_COPY); + htp_tx_req_set_protocol(tx, "HTTP/1.1", 8, HTP_ALLOC_COPY); + htp_tx_req_set_protocol_number(tx, HTP_PROTOCOL_1_1); + htp_tx_req_set_protocol_0_9(tx, 0); + htp_tx_state_request_line(tx); + + // Configure headers to trigger the URLENCODED parser. + htp_tx_req_set_header(tx, "Content-Type", 12, HTP_URLENCODED_MIME_TYPE, + strlen(HTP_URLENCODED_MIME_TYPE), HTP_ALLOC_COPY); + htp_tx_req_set_header(tx, "Transfer-Encoding", 17, "chunked", 7, HTP_ALLOC_COPY); + + // Request headers complete. + htp_tx_state_request_headers(tx); + + // Send request body. + htp_tx_req_process_body_data(tx, "p=1", 3); + htp_tx_req_process_body_data(tx, "&", 1); + htp_tx_req_process_body_data(tx, "q=2", 3); + + // Request complete. + htp_tx_state_request_complete(tx); + + // Check the parameters. + + htp_param_t *param_p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + htp_param_t *param_q = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); +} + +TEST_F(HybridParsing, RequestLineParsing1) { + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Request begins + htp_tx_state_request_start(tx); + + // Request line data + htp_tx_req_set_line(tx, "GET /?p=1&q=2 HTTP/1.0", 22, HTP_ALLOC_COPY); + + // Request line complete + htp_tx_state_request_line(tx); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=1&q=2")); + ASSERT_EQ(0, bstr_cmp_c(tx->request_protocol, "HTTP/1.0")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->query, "p=1&q=2")); + + // Check parameters + htp_param_t *param_p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(param_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_p->value, "1")); + + htp_param_t *param_q = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(param_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(param_q->value, "2")); +} + +TEST_F(HybridParsing, RequestLineParsing2) { + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Feed data to the parser. + + htp_tx_state_request_start(tx); + htp_tx_req_set_line(tx, "GET /", 5, HTP_ALLOC_COPY); + htp_tx_state_request_line(tx); + + // Check the results now. + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(1, tx->is_protocol_0_9); + ASSERT_EQ(HTP_PROTOCOL_0_9, tx->request_protocol_number); + ASSERT_TRUE(tx->request_protocol == NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/")); +} + +TEST_F(HybridParsing, ParsedUriSupplied) { + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Feed data to the parser. + + htp_tx_state_request_start(tx); + htp_tx_req_set_line(tx, "GET /?p=1&q=2 HTTP/1.0", 22, HTP_ALLOC_COPY); + + htp_uri_t *u = htp_uri_alloc(); + u->path = bstr_dup_c("/123"); + htp_tx_req_set_parsed_uri(tx, u); + + htp_tx_state_request_line(tx); + + // Check the results now. + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_TRUE(tx->request_protocol != NULL); + ASSERT_EQ(HTP_PROTOCOL_1_0, tx->request_protocol_number); + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=1&q=2")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/123")); +} + + +class HybridParsingNoOpen : public testing::Test { +protected: + + virtual void SetUp() { + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_GENERIC); + } + + virtual void TearDown() { + htp_config_destroy(cfg); + } + + htp_cfg_t *cfg; + + // This must not be in a test stack frame as it will persist to TearDown + // as htp user data. + HybridParsing_Get_User_Data user_data; +}; + +/** + * Test hybrid mode with one complete GET transaction; request then response + * with no body. Used to crash in htp_connp_close(). + */ +TEST_F(HybridParsing, TestRepeatCallbacks) +{ + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Configure user data and callbacks + htp_tx_set_user_data(tx, &user_data); + + // Request callbacks + RegisterUserCallbacks(); + + // Request begins + htp_tx_state_request_start(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_START_invoked); + + // Request line data + htp_tx_req_set_line(tx, "GET / HTTP/1.0", 14, HTP_ALLOC_COPY); + + // Request line complete + htp_tx_state_request_line(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_LINE_invoked); + + // Check request line data + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/")); + ASSERT_TRUE(tx->request_protocol != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_protocol, "HTTP/1.0")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/")); + + // Request headers complete + htp_tx_state_request_headers(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_HEADERS_invoked); + + // Request complete + htp_tx_state_request_complete(tx); + ASSERT_EQ(1, user_data.callback_REQUEST_COMPLETE_invoked); + + // Response begins + htp_tx_state_response_start(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_START_invoked); + + // Response line data + htp_tx_res_set_status_line(tx, "HTTP/1.1 200 OK\r\n", 17, HTP_ALLOC_COPY); + + // Response line complete + htp_tx_state_response_line(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_LINE_invoked); + + // Response headers complete + htp_tx_state_response_headers(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_HEADERS_invoked); + + // Response complete + htp_tx_state_response_complete(tx); + ASSERT_EQ(1, user_data.callback_RESPONSE_COMPLETE_invoked); + + ASSERT_EQ(htp_tx_destroy(tx), HTP_OK); + + // Close connection + CloseConnParser(); + + ASSERT_EQ(1, user_data.callback_REQUEST_START_invoked); + ASSERT_EQ(1, user_data.callback_REQUEST_LINE_invoked); + ASSERT_EQ(1, user_data.callback_REQUEST_HEADERS_invoked); + ASSERT_EQ(1, user_data.callback_REQUEST_COMPLETE_invoked); + ASSERT_EQ(1, user_data.callback_RESPONSE_START_invoked); + ASSERT_EQ(1, user_data.callback_RESPONSE_LINE_invoked); + ASSERT_EQ(1, user_data.callback_RESPONSE_HEADERS_invoked); + ASSERT_EQ(1, user_data.callback_RESPONSE_COMPLETE_invoked); + ASSERT_EQ(1, user_data.callback_TRANSACTION_COMPLETE_invoked); +} + +/** + * Try to delete a transaction before it is complete. + */ +TEST_F(HybridParsing, DeleteTransactionBeforeComplete) +{ + // Create a new LibHTP transaction + htp_tx_t *tx = htp_connp_tx_create(connp); + ASSERT_TRUE(tx != NULL); + + // Request begins + htp_tx_state_request_start(tx); + + // Request line data + htp_tx_req_set_line(tx, "GET / HTTP/1.0", 14, HTP_ALLOC_COPY); + + ASSERT_EQ(htp_tx_destroy(tx), HTP_ERROR); + + // Close connection + CloseConnParser(); +} diff --git a/test/test_main.cpp b/test/test_main.cpp new file mode 100644 index 0000000..73b8686 --- /dev/null +++ b/test/test_main.cpp @@ -0,0 +1,2148 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <iostream> +#include <gtest/gtest.h> +#include <htp/htp_private.h> +#include "test.h" + +class ConnectionParsing : public testing::Test { +protected: + + virtual void SetUp() { + home = getenv("srcdir"); + if (home == NULL) { + fprintf(stderr, "This program needs environment variable 'srcdir' set."); + exit(EXIT_FAILURE); + } + + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2); + htp_config_register_urlencoded_parser(cfg); + htp_config_register_multipart_parser(cfg); + } + + virtual void TearDown() { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + } + + htp_connp_t *connp; + + htp_cfg_t *cfg; + + char *home; +}; + +TEST_F(ConnectionParsing, AdHoc) { + int rc = test_run(home, "00-adhoc.t", cfg, &connp); + ASSERT_GE(rc, 0); +} + +TEST_F(ConnectionParsing, Get) { + int rc = test_run(home, "01-get.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=%20")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->query != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->query, "p=%20")); + + htp_param_t *p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p->value, " ")); +} + +TEST_F(ConnectionParsing, ApacheHeaderParsing) { + int rc = test_run(home, "02-header-test-apache2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(9, htp_table_size(tx->request_headers)); + + // Check every header + int count = 0; + bstr *key = NULL; + htp_header_t *h = NULL; + + for (int i = 0, n = htp_table_size(tx->request_headers); i < n; i++) { + h = (htp_header_t *) htp_table_get_index(tx->request_headers, i, &key); + + switch (count) { + case 0: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Invalid-Folding")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "1")); + break; + case 1: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Valid-Folding")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "2 2")); + break; + case 2: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Normal-Header")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "3")); + break; + case 3: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Invalid Header Name")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "4")); + break; + case 4: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Same-Name-Headers")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "5, 6")); + break; + case 5: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Empty-Value-Header")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "")); + break; + case 6: + ASSERT_EQ(0, bstr_cmp_c(h->name, "")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "8, ")); + break; + case 7: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Header-With-LWS-After")); + ASSERT_EQ(0, bstr_cmp_c(h->value, "9")); + break; + case 8: + ASSERT_EQ(0, bstr_cmp_c(h->name, "Header-With-NUL")); + ASSERT_EQ(0, bstr_cmp_c_nocasenorzero(h->value, "BEFOREAFTER")); + break; + } + + count++; + } +} + +TEST_F(ConnectionParsing, PostUrlencoded) { + int rc = test_run(home, "03-post-urlencoded.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + htp_param_t *p = htp_tx_req_get_param(tx1, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p->value, "0123456789")); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx1->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx1->response_progress); + + htp_header_t *h = (htp_header_t *)htp_table_get_c(tx1->response_headers, "Server"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + ASSERT_EQ(0, bstr_cmp_c(h->value, "Apache")); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx2->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx2->response_progress); + + h = (htp_header_t *)htp_table_get_c(tx2->response_headers, "Server"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + ASSERT_EQ(0, bstr_cmp_c(h->value, "Apache")); +} + +TEST_F(ConnectionParsing, PostUrlencodedChunked) { + int rc = test_run(home, "04-post-urlencoded-chunked.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + htp_param_t *p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p->value, "0123456789")); + + ASSERT_EQ(25, tx->request_message_len); + + ASSERT_EQ(12, tx->request_entity_len); +} + +TEST_F(ConnectionParsing, Expect) { + int rc = test_run(home, "05-expect.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + // The interim header from the 100 response should not be among the final headers. + htp_header_t *h = (htp_header_t *) htp_table_get_c(tx->request_headers, "Header1"); + ASSERT_TRUE(h == NULL); +} + +TEST_F(ConnectionParsing, UriNormal) { + int rc = test_run(home, "06-uri-normal.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); +} + +TEST_F(ConnectionParsing, PipelinedConn) { + int rc = test_run(home, "07-pipelined-connection.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + ASSERT_TRUE(connp->conn->flags & HTP_CONN_PIPELINED); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); +} + +TEST_F(ConnectionParsing, NotPipelinedConn) { + int rc = test_run(home, "08-not-pipelined-connection.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + ASSERT_FALSE(connp->conn->flags & HTP_CONN_PIPELINED); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_FALSE(tx->flags & HTP_MULTI_PACKET_HEAD); +} + +TEST_F(ConnectionParsing, MultiPacketRequest) { + int rc = test_run(home, "09-multi-packet-request-head.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_MULTI_PACKET_HEAD); +} + +TEST_F(ConnectionParsing, HeaderHostParsing) { + int rc = test_run(home, "10-host-in-headers.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(4, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + ASSERT_TRUE(tx1->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx1->request_hostname, "www.example.com")); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + ASSERT_TRUE(tx2->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx2->request_hostname, "www.example.com.")); + + htp_tx_t *tx3 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 2); + ASSERT_TRUE(tx3 != NULL); + ASSERT_TRUE(tx3->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx3->request_hostname, "www.example.com")); + + htp_tx_t *tx4 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 3); + ASSERT_TRUE(tx4 != NULL); + ASSERT_TRUE(tx4->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx4->request_hostname, "www.example.com")); +} + +TEST_F(ConnectionParsing, ResponseWithoutContentLength) { + int rc = test_run(home, "11-response-stream-closure.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); +} + +TEST_F(ConnectionParsing, FailedConnectRequest) { + int rc = test_run(home, "12-connect-request.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "CONNECT")); + + ASSERT_EQ(405, tx->response_status_number); +} + +TEST_F(ConnectionParsing, CompressedResponseContentType) { + int rc = test_run(home, "13-compressed-response-gzip-ct.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(187, tx->response_message_len); + + ASSERT_EQ(225, tx->response_entity_len); +} + +TEST_F(ConnectionParsing, CompressedResponseChunked) { + int rc = test_run(home, "14-compressed-response-gzip-chunked.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(28261, tx->response_message_len); + + ASSERT_EQ(159590, tx->response_entity_len); +} + +TEST_F(ConnectionParsing, SuccessfulConnectRequest) { + int rc = test_run(home, "15-connect-complete.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + // TODO: Update the test_run() function to provide better + // simulation of real traffic. At the moment, it does not + // invoke inbound parsing after outbound parsing returns + // HTP_DATA_OTHER, which is why the check below fails. + //ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "CONNECT")); + + ASSERT_EQ(200, tx->response_status_number); +} + +TEST_F(ConnectionParsing, ConnectRequestWithExtraData) { + int rc = test_run(home, "16-connect-extra.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx1)); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx2)); +} + +TEST_F(ConnectionParsing, Multipart) { + int rc = test_run(home, "17-multipart-1.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + htp_param_t *field1 = htp_tx_req_get_param(tx, "field1", 6); + ASSERT_TRUE(field1 != NULL); + ASSERT_EQ(0, bstr_cmp_c(field1->value, "0123456789")); + + htp_param_t *field2 = htp_tx_req_get_param(tx, "field2", 6); + ASSERT_TRUE(field2 != NULL); + ASSERT_EQ(0, bstr_cmp_c(field2->value, "9876543210")); +} + +TEST_F(ConnectionParsing, CompressedResponseDeflate) { + int rc = test_run(home, "18-compressed-response-deflate.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(755, tx->response_message_len); + + ASSERT_EQ(1433, tx->response_entity_len); +} + +TEST_F(ConnectionParsing, UrlEncoded) { + int rc = test_run(home, "19-urlencoded-test.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "POST")); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=1&q=2")); + + htp_param_t *body_p = htp_tx_req_get_param_ex(tx, HTP_SOURCE_BODY, "p", 1); + ASSERT_TRUE(body_p != NULL); + ASSERT_EQ(0, bstr_cmp_c(body_p->value, "3")); + + htp_param_t *body_q = htp_tx_req_get_param_ex(tx, HTP_SOURCE_BODY, "q", 1); + ASSERT_TRUE(body_q != NULL); + ASSERT_EQ(0, bstr_cmp_c(body_q->value, "4")); + + htp_param_t *body_z = htp_tx_req_get_param_ex(tx, HTP_SOURCE_BODY, "z", 1); + ASSERT_TRUE(body_z != NULL); + ASSERT_EQ(0, bstr_cmp_c(body_z->value, "5")); +} + +TEST_F(ConnectionParsing, AmbiguousHost) { + int rc = test_run(home, "20-ambiguous-host.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(5, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + ASSERT_TRUE(htp_tx_is_complete(tx1)); + ASSERT_FALSE(tx1->flags & HTP_HOST_AMBIGUOUS); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + ASSERT_TRUE(htp_tx_is_complete(tx2)); + ASSERT_TRUE(tx2->flags & HTP_HOST_AMBIGUOUS); + ASSERT_TRUE(tx2->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx2->request_hostname, "example.com")); + + htp_tx_t *tx3 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 2); + ASSERT_TRUE(tx3 != NULL); + ASSERT_TRUE(htp_tx_is_complete(tx3)); + ASSERT_FALSE(tx3->flags & HTP_HOST_AMBIGUOUS); + ASSERT_TRUE(tx3->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx3->request_hostname, "www.example.com")); + ASSERT_EQ(8001, tx3->request_port_number); + + htp_tx_t *tx4 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 3); + ASSERT_TRUE(tx4 != NULL); + ASSERT_TRUE(htp_tx_is_complete(tx4)); + ASSERT_TRUE(tx4->flags & HTP_HOST_AMBIGUOUS); + ASSERT_TRUE(tx4->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx4->request_hostname, "www.example.com")); + ASSERT_EQ(8002, tx4->request_port_number); + + htp_tx_t *tx5 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 4); + ASSERT_TRUE(tx5 != NULL); + ASSERT_TRUE(htp_tx_is_complete(tx5)); + ASSERT_FALSE(tx5->flags & HTP_HOST_AMBIGUOUS); + ASSERT_TRUE(tx5->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx5->request_hostname, "www.example.com")); + ASSERT_EQ(80, tx5->request_port_number); +} + +TEST_F(ConnectionParsing, Http_0_9) { + int rc = test_run(home, "21-http09.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + ASSERT_FALSE(connp->conn->flags & HTP_CONN_HTTP_0_9_EXTRA); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); +} + +TEST_F(ConnectionParsing, PhpParamProcessing) { + cfg->parameter_processor = htp_php_parameter_processor; + + int rc = test_run(home, "22-php-param-processing.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + htp_param_t *p1 = htp_tx_req_get_param(tx, "p_q_", 4); + ASSERT_TRUE(p1 != NULL); + ASSERT_EQ(0, bstr_cmp_c(p1->value, "1")); + + htp_param_t *p2 = htp_tx_req_get_param(tx, "q", 1); + ASSERT_TRUE(p2 != NULL); + ASSERT_EQ(0, bstr_cmp_c(p2->value, "2")); + + htp_param_t *p3 = htp_tx_req_get_param(tx, "z_w", 3); + ASSERT_TRUE(p3 != NULL); + ASSERT_EQ(0, bstr_cmp_c(p3->value, "3")); +} + +TEST_F(ConnectionParsing, Http11HostMissing) { + int rc = test_run(home, "22-http_1_1-host_missing", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_HOST_MISSING); +} + +TEST_F(ConnectionParsing, Http_0_9_Multiple) { + int rc = test_run(home, "23-http09-multiple.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); +} + +TEST_F(ConnectionParsing, Http_0_9_Explicit) { + int rc = test_run(home, "24-http09-explicit.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, tx->is_protocol_0_9); +} + +TEST_F(ConnectionParsing, SmallChunks) { + int rc = test_run(home, "25-small-chunks.t", cfg, &connp); + ASSERT_GE(rc, 0); +} + +static int ConnectionParsing_RequestHeaderData_REQUEST_HEADER_DATA(htp_tx_data_t *d) { + static int counter = 0; + + switch (counter) { + case 0: + if (!((d->len == 11) && (memcmp(d->data, "User-Agent:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 0"); + counter = -1; + } + break; + + case 1: + if (!((d->len == 5) && (memcmp(d->data, " Test", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -1; + } + break; + + case 2: + if (!((d->len == 5) && (memcmp(d->data, " User", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 2"); + counter = -1; + } + break; + + case 3: + if (!((d->len == 30) && (memcmp(d->data, " Agent\nHost: www.example.com\n\n", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 3"); + counter = -1; + } + break; + + default: + if (counter >= 0) { + SCOPED_TRACE("Seen more than 4 chunks"); + counter = -1; + } + break; + } + + if (counter >= 0) { + counter++; + } + + htp_tx_set_user_data(d->tx, &counter); + + return HTP_OK; +} + +TEST_F(ConnectionParsing, RequestHeaderData) { + htp_config_register_request_header_data(cfg, ConnectionParsing_RequestHeaderData_REQUEST_HEADER_DATA); + + int rc = test_run(home, "26-request-headers-raw.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + int *counter = (int *) htp_tx_get_user_data(tx); + ASSERT_TRUE(counter != NULL); + ASSERT_EQ(4, *counter); +} + +static int ConnectionParsing_RequestTrailerData_REQUEST_TRAILER_DATA(htp_tx_data_t *d) { + static int counter = 0; + + switch (counter) { + case 0: + if (!((d->len == 7) && (memcmp(d->data, "Cookie:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 0"); + counter = -1; + } + break; + + case 1: + if (!((d->len == 6) && (memcmp(d->data, " 2\r\n\r\n", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -2; + } + break; + + default: + if (counter >= 0) { + SCOPED_TRACE("Seen more than 4 chunks"); + counter = -3; + } + break; + } + + if (counter >= 0) { + counter++; + } + + htp_tx_set_user_data(d->tx, &counter); + + return HTP_OK; +} + +TEST_F(ConnectionParsing, RequestTrailerData) { + htp_config_register_request_trailer_data(cfg, ConnectionParsing_RequestTrailerData_REQUEST_TRAILER_DATA); + + int rc = test_run(home, "27-request-trailer-raw.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + int *counter = (int *) htp_tx_get_user_data(tx); + ASSERT_TRUE(counter != NULL); + ASSERT_EQ(2, *counter); +} + +static int ConnectionParsing_ResponseHeaderData_RESPONSE_HEADER_DATA(htp_tx_data_t *d) { + static int counter = 0; + + switch (counter) { + case 0: + if (!((d->len == 5) && (memcmp(d->data, "Date:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 0"); + counter = -1; + } + break; + + case 1: + if (!((d->len == 5) && (memcmp(d->data, " Mon,", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -2; + } + break; + + case 2: + if (!((d->len == 34) && (memcmp(d->data, " 31 Aug 2009 20:25:50 GMT\r\nServer:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 2"); + counter = -3; + } + break; + + case 3: + if (!((d->len == 83) && (memcmp(d->data, " Apache\r\nConnection: close\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 3"); + counter = -4; + } + break; + + default: + if (counter >= 0) { + SCOPED_TRACE("Seen more than 4 chunks"); + counter = -5; + } + break; + } + + if (counter >= 0) { + counter++; + } + + htp_tx_set_user_data(d->tx, &counter); + + return HTP_OK; +} + +TEST_F(ConnectionParsing, ResponseHeaderData) { + htp_config_register_response_header_data(cfg, ConnectionParsing_ResponseHeaderData_RESPONSE_HEADER_DATA); + + int rc = test_run(home, "28-response-headers-raw.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + int *counter = (int *) htp_tx_get_user_data(tx); + ASSERT_TRUE(counter != NULL); + ASSERT_EQ(4, *counter); +} + +static int ConnectionParsing_ResponseTrailerData_RESPONSE_TRAILER_DATA(htp_tx_data_t *d) { + static int counter = 0; + + switch (counter) { + case 0: + if (!((d->len == 11) && (memcmp(d->data, "Set-Cookie:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 0"); + counter = -1; + } + break; + + case 1: + if (!((d->len == 6) && (memcmp(d->data, " name=", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -2; + } + break; + + case 2: + if (!((d->len == 22) && (memcmp(d->data, "value\r\nAnother-Header:", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -3; + } + break; + + case 3: + if (!((d->len == 17) && (memcmp(d->data, " Header-Value\r\n\r\n", d->len) == 0))) { + SCOPED_TRACE("Mismatch in chunk 1"); + counter = -4; + } + break; + + default: + if (counter >= 0) { + SCOPED_TRACE("Seen more than 4 chunks"); + counter = -5; + } + break; + } + + if (counter >= 0) { + counter++; + } + + htp_tx_set_user_data(d->tx, &counter); + + return HTP_OK; +} + +TEST_F(ConnectionParsing, ResponseTrailerData) { + htp_config_register_response_trailer_data(cfg, ConnectionParsing_ResponseTrailerData_RESPONSE_TRAILER_DATA); + + int rc = test_run(home, "29-response-trailer-raw.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + int *counter = (int *) htp_tx_get_user_data(tx); + ASSERT_TRUE(counter != NULL); + ASSERT_EQ(4, *counter); +} + +TEST_F(ConnectionParsing, GetIPv6) { + int rc = test_run(home, "30-get-ipv6.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "http://[::1]:8080/?p=%20")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->hostname, "[::1]")); + ASSERT_EQ(8080, tx->parsed_uri->port_number); + + ASSERT_TRUE(tx->parsed_uri->query != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->query, "p=%20")); + + htp_param_t *p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p->value != NULL); + ASSERT_EQ(0, bstr_cmp_c(p->value, " ")); +} + +TEST_F(ConnectionParsing, GetRequestLineNul) { + int rc = test_run(home, "31-get-request-line-nul.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_uri != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=%20")); +} + +TEST_F(ConnectionParsing, InvalidHostname1) { + int rc = test_run(home, "32-invalid-hostname.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_HOSTH_INVALID); + ASSERT_TRUE(tx->flags & HTP_HOSTU_INVALID); + ASSERT_TRUE(tx->flags & HTP_HOST_INVALID); +} + +TEST_F(ConnectionParsing, InvalidHostname2) { + int rc = test_run(home, "33-invalid-hostname.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_FALSE(tx->flags & HTP_HOSTH_INVALID); + ASSERT_TRUE(tx->flags & HTP_HOSTU_INVALID); + ASSERT_TRUE(tx->flags & HTP_HOST_INVALID); +} + +TEST_F(ConnectionParsing, InvalidHostname3) { + int rc = test_run(home, "34-invalid-hostname.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_HOSTH_INVALID); + ASSERT_FALSE(tx->flags & HTP_HOSTU_INVALID); + ASSERT_TRUE(tx->flags & HTP_HOST_INVALID); +} + +TEST_F(ConnectionParsing, API_connp_get_connection) { + int rc = test_run(home, "34-invalid-hostname.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(connp->conn, htp_connp_get_connection(connp)); +} + +TEST_F(ConnectionParsing, EarlyResponse) { + int rc = test_run(home, "35-early-response.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); +} + +TEST_F(ConnectionParsing, InvalidRequest1) { + int rc = test_run(home, "36-invalid-request-1-invalid-c-l.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_HEADERS, tx->request_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_INVALID); + ASSERT_TRUE(tx->flags & HTP_REQUEST_INVALID_C_L); + + ASSERT_TRUE(tx->request_hostname != NULL); +} + +TEST_F(ConnectionParsing, InvalidRequest2) { + int rc = test_run(home, "37-invalid-request-2-t-e-and-c-l.t", cfg, &connp); + ASSERT_GE(rc, 0); // No error, flags only. + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_SMUGGLING); + + ASSERT_TRUE(tx->request_hostname != NULL); +} + +TEST_F(ConnectionParsing, InvalidRequest3) { + int rc = test_run(home, "38-invalid-request-3-invalid-t-e.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_HEADERS, tx->request_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_INVALID); + ASSERT_TRUE(tx->flags & HTP_REQUEST_INVALID_T_E); + + ASSERT_TRUE(tx->request_hostname != NULL); +} + +TEST_F(ConnectionParsing, AutoDestroyCrash) { + htp_config_set_tx_auto_destroy(cfg, 1); + + int rc = test_run(home, "39-auto-destroy-crash.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(4, htp_list_size(connp->conn->transactions)); +} + +TEST_F(ConnectionParsing, AuthBasic) { + int rc = test_run(home, "40-auth-basic.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_BASIC, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_auth_username, "ivanr")); + + ASSERT_TRUE(tx->request_auth_password != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_auth_password, "secret")); +} + +TEST_F(ConnectionParsing, AuthDigest) { + int rc = test_run(home, "41-auth-digest.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_DIGEST, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_auth_username, "ivanr")); + + ASSERT_TRUE(tx->request_auth_password == NULL); +} + +TEST_F(ConnectionParsing, AuthBearer) { + int rc = test_run(home, "100-auth-bearer.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_BEARER, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); +} + +TEST_F(ConnectionParsing, Unknown_MethodOnly) { + int rc = test_run(home, "42-unknown-method_only.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "HELLO")); + + ASSERT_TRUE(tx->request_uri == NULL); + + ASSERT_EQ(1, tx->is_protocol_0_9); +} + +TEST_F(ConnectionParsing, InvalidProtocol) { + int rc = test_run(home, "43-invalid-protocol.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_PROTOCOL_INVALID, tx->request_protocol_number); +} + +TEST_F(ConnectionParsing, AuthBasicInvalid) { + int rc = test_run(home, "44-auth-basic-invalid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_BASIC, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); + + ASSERT_TRUE(tx->flags & HTP_AUTH_INVALID); +} + +TEST_F(ConnectionParsing, AuthDigestUnquotedUsername) { + int rc = test_run(home, "45-auth-digest-unquoted-username.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_DIGEST, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); + + ASSERT_TRUE(tx->flags & HTP_AUTH_INVALID); +} + +TEST_F(ConnectionParsing, AuthDigestInvalidUsername1) { + int rc = test_run(home, "46-auth-digest-invalid-username.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_DIGEST, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); + + ASSERT_TRUE(tx->flags & HTP_AUTH_INVALID); +} + +TEST_F(ConnectionParsing, AuthUnrecognized) { + int rc = test_run(home, "47-auth-unrecognized.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_UNRECOGNIZED, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); +} + +TEST_F(ConnectionParsing, InvalidResponseHeaders1) { + int rc = test_run(home, "48-invalid-response-headers-1.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_EQ(8, htp_table_size(tx->response_headers)); + + htp_header_t *h_empty = (htp_header_t *) htp_table_get_c(tx->response_headers, ""); + ASSERT_TRUE(h_empty != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_empty->value, "No Colon")); + ASSERT_TRUE(h_empty->flags & HTP_FIELD_INVALID); + ASSERT_TRUE(h_empty->flags & HTP_FIELD_UNPARSEABLE); + + htp_header_t *h_lws = (htp_header_t *) htp_table_get_c(tx->response_headers, "Lws"); + ASSERT_TRUE(h_lws != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_lws->value, "After Header Name")); + ASSERT_TRUE(h_lws->flags & HTP_FIELD_INVALID); + + htp_header_t *h_nottoken = (htp_header_t *) htp_table_get_c(tx->response_headers, "Header@Name"); + ASSERT_TRUE(h_nottoken != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_nottoken->value, "Not Token")); + ASSERT_TRUE(h_nottoken->flags & HTP_FIELD_INVALID); +} + +TEST_F(ConnectionParsing, InvalidResponseHeaders2) { + int rc = test_run(home, "49-invalid-response-headers-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_EQ(6, htp_table_size(tx->response_headers)); + + htp_header_t *h_empty = (htp_header_t *) htp_table_get_c(tx->response_headers, ""); + ASSERT_TRUE(h_empty != NULL); + ASSERT_EQ(0, bstr_cmp_c(h_empty->value, "Empty Name")); + ASSERT_TRUE(h_empty->flags & HTP_FIELD_INVALID); +} + +TEST_F(ConnectionParsing, Util) { + int rc = test_run(home, "50-util.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + char *in_state = htp_connp_in_state_as_string(tx->connp); + ASSERT_TRUE(in_state != NULL); + + char *out_state = htp_connp_out_state_as_string(tx->connp); + ASSERT_TRUE(out_state != NULL); + + char *request_progress = htp_tx_request_progress_as_string(tx); + ASSERT_TRUE(request_progress != NULL); + + char *response_progress = htp_tx_response_progress_as_string(tx); + ASSERT_TRUE(response_progress != NULL); + + FILE *null = fopen("/dev/null", "w"); + ASSERT_TRUE(null != NULL); + + fprint_bstr(null, "test", NULL); + + fprint_bstr(null, "test", tx->request_line); + + fprint_raw_data(null, "test", bstr_ptr(tx->request_line), bstr_len(tx->request_line)); + + fprint_raw_data_ex(null, "test", bstr_ptr(tx->request_line), 0, bstr_len(tx->request_line)); + + // Message too long. + tx->connp->cfg->log_level = HTP_LOG_ERROR; + char long_message[1300]; + for (size_t i = 0; i < 1299; i++) { + long_message[i] = 'X'; + } + long_message[1299] = '\0'; + + htp_log(tx->connp, __FILE__, __LINE__, HTP_LOG_ERROR, 0, long_message); + ASSERT_TRUE(tx->connp->last_error != NULL); + ASSERT_TRUE(tx->connp->last_error->msg != NULL); + ASSERT_EQ(1023, strlen(tx->connp->last_error->msg)); + ASSERT_EQ('+', tx->connp->last_error->msg[1022]); + + // A message that should not be logged. + size_t log_message_count = htp_list_size(tx->connp->conn->messages); + tx->connp->cfg->log_level = HTP_LOG_NONE; + htp_log(tx->connp, __FILE__, __LINE__, HTP_LOG_ERROR, 0, "Log message"); + ASSERT_EQ(log_message_count, htp_list_size(tx->connp->conn->messages)); +} + +TEST_F(ConnectionParsing, GetIPv6Invalid) { + int rc = test_run(home, "51-get-ipv6-invalid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "http://[::1:8080/?p=%20")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->hostname, "[::1:8080")); +} + +TEST_F(ConnectionParsing, InvalidPath) { + int rc = test_run(home, "52-invalid-path.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + + ASSERT_TRUE(tx->request_uri != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "invalid/path?p=%20")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "invalid/path")); +} + +TEST_F(ConnectionParsing, PathUtf8_None) { + int rc = test_run(home, "53-path-utf8-none.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_FALSE(tx->flags & HTP_PATH_UTF8_VALID); + ASSERT_FALSE(tx->flags & HTP_PATH_UTF8_OVERLONG); + ASSERT_FALSE(tx->flags & HTP_PATH_HALF_FULL_RANGE); +} + +TEST_F(ConnectionParsing, PathUtf8_Valid) { + int rc = test_run(home, "54-path-utf8-valid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_VALID); +} + +TEST_F(ConnectionParsing, PathUtf8_Overlong2) { + int rc = test_run(home, "55-path-utf8-overlong-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); +} + +TEST_F(ConnectionParsing, PathUtf8_Overlong3) { + int rc = test_run(home, "56-path-utf8-overlong-3.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); +} + +TEST_F(ConnectionParsing, PathUtf8_Overlong4) { + int rc = test_run(home, "57-path-utf8-overlong-4.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); +} + +TEST_F(ConnectionParsing, PathUtf8_Invalid) { + int rc = test_run(home, "58-path-utf8-invalid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_INVALID); + ASSERT_FALSE(tx->flags & HTP_PATH_UTF8_VALID); +} + +TEST_F(ConnectionParsing, PathUtf8_FullWidth) { + int rc = test_run(home, "59-path-utf8-fullwidth.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_HALF_FULL_RANGE); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_Valid) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "54-path-utf8-valid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/Ristic.txt")); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_Overlong2) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "55-path-utf8-overlong-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/&.txt")); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_Overlong3) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "56-path-utf8-overlong-3.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/&.txt")); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_Overlong4) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "57-path-utf8-overlong-4.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_OVERLONG); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/&.txt")); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_Invalid) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "58-path-utf8-invalid.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_UTF8_INVALID); + ASSERT_FALSE(tx->flags & HTP_PATH_UTF8_VALID); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/Ristic?.txt")); +} + +TEST_F(ConnectionParsing, PathUtf8_Decode_FullWidth) { + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + + int rc = test_run(home, "59-path-utf8-fullwidth.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->flags & HTP_PATH_HALF_FULL_RANGE); + + ASSERT_TRUE(tx->parsed_uri != NULL); + ASSERT_TRUE(tx->parsed_uri->path != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->path, "/&.txt")); +} + +TEST_F(ConnectionParsing, RequestCookies) { + int rc = test_run(home, "60-request-cookies.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(3, htp_table_size(tx->request_cookies)); + + bstr *key = NULL; + bstr *value = NULL; + + value = (bstr *) htp_table_get_index(tx->request_cookies, 0, &key); + ASSERT_TRUE(key != NULL); + ASSERT_TRUE(value != NULL); + ASSERT_EQ(0, bstr_cmp_c(key, "p")); + ASSERT_EQ(0, bstr_cmp_c(value, "1")); + + value = (bstr *) htp_table_get_index(tx->request_cookies, 1, &key); + ASSERT_TRUE(key != NULL); + ASSERT_TRUE(value != NULL); + ASSERT_EQ(0, bstr_cmp_c(key, "q")); + ASSERT_EQ(0, bstr_cmp_c(value, "2")); + + value = (bstr *) htp_table_get_index(tx->request_cookies, 2, &key); + ASSERT_TRUE(key != NULL); + ASSERT_TRUE(value != NULL); + ASSERT_EQ(0, bstr_cmp_c(key, "z")); + ASSERT_EQ(0, bstr_cmp_c(value, "")); +} + +TEST_F(ConnectionParsing, EmptyLineBetweenRequests) { + int rc = test_run(home, "61-empty-line-between-requests.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx != NULL); + + /*part of previous request body ASSERT_EQ(1, tx->request_ignored_lines);*/ +} + +TEST_F(ConnectionParsing, PostNoBody) { + int rc = test_run(home, "62-post-no-body.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx1->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx1->response_progress); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx2->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx2->response_progress); +} + +TEST_F(ConnectionParsing, PostChunkedInvalid1) { + int rc = test_run(home, "63-post-chunked-invalid-1.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. +} + +TEST_F(ConnectionParsing, PostChunkedInvalid2) { + int rc = test_run(home, "64-post-chunked-invalid-2.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. +} + +TEST_F(ConnectionParsing, PostChunkedInvalid3) { + int rc = test_run(home, "65-post-chunked-invalid-3.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. +} + +TEST_F(ConnectionParsing, PostChunkedSplitChunk) { + int rc = test_run(home, "66-post-chunked-split-chunk.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + htp_param_t *p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(p != NULL); + ASSERT_TRUE(p->value != NULL); + ASSERT_EQ(0, bstr_cmp_c(p->value, "0123456789")); +} + +TEST_F(ConnectionParsing, LongRequestLine1) { + int rc = test_run(home, "67-long-request-line.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/0123456789/0123456789/")); +} + +TEST_F(ConnectionParsing, LongRequestLine2) { + htp_config_set_field_limits(cfg, 0, 16); + + int rc = test_run(home, "67-long-request-line.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_LINE, tx->request_progress); +} + +TEST_F(ConnectionParsing, InvalidRequestHeader) { + int rc = test_run(home, "68-invalid-request-header.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + htp_header_t *h = (htp_header_t *) htp_table_get_c(tx->request_headers, "Header-With-NUL"); + ASSERT_TRUE(h != NULL); + ASSERT_EQ(0, bstr_cmp_c_nocasenorzero(h->value, "BEFORE AFTER")); +} + +TEST_F(ConnectionParsing, TestGenericPersonality) { + htp_config_set_server_personality(cfg, HTP_SERVER_IDS); + + int rc = test_run(home, "02-header-test-apache2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); +} + +TEST_F(ConnectionParsing, LongResponseHeader) { + htp_config_set_field_limits(cfg, 0, 16); + + int rc = test_run(home, "69-long-response-header.t", cfg, &connp); + ASSERT_LT(rc, 0); // Expect error. + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + //error first ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_HEADERS, tx->response_progress); +} + +TEST_F(ConnectionParsing, ResponseInvalidChunkLength) { + int rc = test_run(home, "70-response-invalid-chunk-length.t", cfg, &connp); + ASSERT_EQ(rc, 1); // Expect success as we're very liberal +} + +TEST_F(ConnectionParsing, ResponseSplitChunk) { + int rc = test_run(home, "71-response-split-chunk.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, ResponseBody) { + int rc = test_run(home, "72-response-split-body.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, ResponseContainsTeAndCl) { + int rc = test_run(home, "73-response-te-and-cl.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_SMUGGLING); +} + +TEST_F(ConnectionParsing, ResponseMultipleCl) { + int rc = test_run(home, "74-response-multiple-cl.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_SMUGGLING); + + htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers, "Content-Length"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + ASSERT_TRUE(h->flags & HTP_FIELD_REPEATED); + + ASSERT_EQ(0, bstr_cmp_c(h->value, "12")); +} + +TEST_F(ConnectionParsing, ResponseMultipleClMismatch) { + int rc = test_run(home, "88-response-multiple-cl-mismatch.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_TRUE(tx->flags & HTP_REQUEST_SMUGGLING); + + htp_header_t *h = (htp_header_t *)htp_table_get_c(tx->response_headers, "Content-Length"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + ASSERT_TRUE(h->flags & HTP_FIELD_REPEATED); + + ASSERT_EQ(0, bstr_cmp_c(h->value, "12")); + + ASSERT_EQ(2, htp_list_size(tx->conn->messages)); + htp_log_t *log = (htp_log_t *) htp_list_get(tx->conn->messages, 1); + ASSERT_TRUE(log != NULL); + ASSERT_EQ(0, strcmp(log->msg, "Ambiguous response C-L value")); + ASSERT_EQ(HTP_LOG_WARNING, log->level); +} + +TEST_F(ConnectionParsing, ResponseInvalidCl) { + int rc = test_run(home, "75-response-invalid-cl.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + ASSERT_FALSE(tx->flags & HTP_REQUEST_SMUGGLING); +} + +TEST_F(ConnectionParsing, ResponseNoBody) { + int rc = test_run(home, "76-response-no-body.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx1->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx1->response_progress); + + htp_header_t *h = (htp_header_t *)htp_table_get_c(tx1->response_headers, "Server"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + + ASSERT_EQ(0, bstr_cmp_c(h->value, "Apache")); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx2->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx2->response_progress); + + ASSERT_TRUE(tx1 != tx2); +} + +TEST_F(ConnectionParsing, ResponseFoldedHeaders) { + int rc = test_run(home, "77-response-folded-headers.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx1 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx1 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx1->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx1->response_progress); + + htp_header_t *h = (htp_header_t *)htp_table_get_c(tx1->response_headers, "Server"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(h->value != NULL); + + ASSERT_EQ(0, bstr_cmp_c(h->value, "Apache Server")); + + htp_tx_t *tx2 = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx2 != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx2->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx2->response_progress); +} + +TEST_F(ConnectionParsing, ResponseNoStatusHeaders) { + int rc = test_run(home, "78-response-no-status-headers.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, ConnectInvalidHostport) { + int rc = test_run(home, "79-connect-invalid-hostport.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); +} + +TEST_F(ConnectionParsing, HostnameInvalid1) { + int rc = test_run(home, "80-hostname-invalid-1.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); +} + +TEST_F(ConnectionParsing, HostnameInvalid2) { + int rc = test_run(home, "81-hostname-invalid-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); +} + +TEST_F(ConnectionParsing, Put) { + int rc = test_run(home, "82-put.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_hostname, "www.example.com")); +} + +TEST_F(ConnectionParsing, AuthDigestInvalidUsername2) { + int rc = test_run(home, "83-auth-digest-invalid-username-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_EQ(HTP_AUTH_DIGEST, tx->request_auth_type); + + ASSERT_TRUE(tx->request_auth_username == NULL); + + ASSERT_TRUE(tx->request_auth_password == NULL); + + ASSERT_TRUE(tx->flags & HTP_AUTH_INVALID); +} + +TEST_F(ConnectionParsing, ResponseNoStatusHeaders2) { + int rc = test_run(home, "84-response-no-status-headers-2.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +/* +TEST_F(ConnectionParsing, ZeroByteRequestTimeout) { + int rc = test_run(home, "85-zero-byte-request-timeout.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_NOT_STARTED, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} +*/ + +TEST_F(ConnectionParsing, PartialRequestTimeout) { + int rc = test_run(home, "86-partial-request-timeout.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, IncorrectHostAmbiguousWarning) { + int rc = test_run(home, "87-issue-55-incorrect-host-ambiguous-warning.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->parsed_uri_raw != NULL); + + ASSERT_TRUE(tx->parsed_uri_raw->port != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri_raw->port, "443")); + + ASSERT_TRUE(tx->parsed_uri_raw->hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri_raw->hostname, "www.example.com")); + + ASSERT_EQ(443, tx->parsed_uri_raw->port_number); + + ASSERT_TRUE(tx->request_hostname != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_hostname, "www.example.com")); + + ASSERT_FALSE(tx->flags & HTP_HOST_AMBIGUOUS); +} + +TEST_F(ConnectionParsing, GetWhitespace) { + int rc = test_run(home, "89-get-whitespace.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, " GET")); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/?p=%20")); + + ASSERT_TRUE(tx->parsed_uri != NULL); + + ASSERT_TRUE(tx->parsed_uri->query != NULL); + + ASSERT_EQ(0, bstr_cmp_c(tx->parsed_uri->query, "p=%20")); + + htp_param_t *p = htp_tx_req_get_param(tx, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p->value, " ")); +} + +TEST_F(ConnectionParsing, RequestUriTooLarge) { + int rc = test_run(home, "90-request-uri-too-large.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, RequestInvalid) { + int rc = test_run(home, "91-request-unexpected-body.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "POST")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_NOT_STARTED, tx->response_progress); +} + +TEST_F(ConnectionParsing, Http_0_9_MethodOnly) { + int rc = test_run(home, "92-http_0_9-method_only.t", cfg, &connp); + ASSERT_GE(rc, 0); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + ASSERT_TRUE(tx->request_method != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + + ASSERT_EQ(0, bstr_cmp_c(tx->request_uri, "/")); + + ASSERT_EQ(1, tx->is_protocol_0_9); +} + +TEST_F(ConnectionParsing, CompressedResponseDeflateAsGzip) { + int rc = test_run(home, "93-compressed-response-deflateasgzip.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(755, tx->response_message_len); + + ASSERT_EQ(1433, tx->response_entity_len); +} + +TEST_F(ConnectionParsing, CompressedResponseMultiple) { + int rc = test_run(home, "94-compressed-response-multiple.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(51, tx->response_message_len); + + ASSERT_EQ(25, tx->response_entity_len); +} + +TEST_F(ConnectionParsing, CompressedResponseGzipAsDeflate) { + int rc = test_run(home, "95-compressed-response-gzipasdeflate.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(187, tx->response_message_len); + + ASSERT_EQ(225, tx->response_entity_len); +} + +#ifdef HAVE_LIBLZMA +TEST_F(ConnectionParsing, CompressedResponseLzma) { + int rc = test_run(home, "96-compressed-response-lzma.t", cfg, &connp); + + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(htp_tx_is_complete(tx)); + + ASSERT_EQ(90, tx->response_message_len); + + ASSERT_EQ(68, tx->response_entity_len); +} +#endif + +TEST_F(ConnectionParsing, RequestsCut) { + int rc = test_run(home, "97-requests-cut.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + + tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); +} + +TEST_F(ConnectionParsing, ResponsesCut) { + int rc = test_run(home, "98-responses-cut.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(200, tx->response_status_number); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "GET")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(200, tx->response_status_number); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +TEST_F(ConnectionParsing, Expect100) { + int rc = test_run(home, "99-expect-100.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(2, htp_list_size(connp->conn->transactions)); + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "PUT")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(401, tx->response_status_number); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 1); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(0, bstr_cmp_c(tx->request_method, "POST")); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(200, tx->response_status_number); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); +} + +// emplace_back needs at least C++ 11 +#if __cplusplus > 199711L +struct ResponseBodyDataCallback { + std::vector<std::string> data; +}; + +static int callback_RESPONSE_BODY_DATA(htp_tx_data_t *d) { + struct ResponseBodyDataCallback *user_data = (struct ResponseBodyDataCallback *) htp_tx_get_user_data(d->tx); + + if (!user_data) { + user_data = new ResponseBodyDataCallback(); + htp_tx_set_user_data(d->tx, user_data); + } + + if(d->data) user_data->data.emplace_back(std::string(reinterpret_cast<const char *>(d->data), d->len)); + + return HTP_OK; +} + +TEST_F(ConnectionParsing, ResponseBodyData) { + htp_config_register_response_body_data(cfg, callback_RESPONSE_BODY_DATA); + + int rc = test_run(home, "100-response-body-data.t", cfg, &connp); + ASSERT_GE(rc, 0); + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + htp_tx_t *tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + ASSERT_EQ(HTP_REQUEST_COMPLETE, tx->request_progress); + ASSERT_EQ(HTP_RESPONSE_COMPLETE, tx->response_progress); + + struct ResponseBodyDataCallback *user_data = (struct ResponseBodyDataCallback *) htp_tx_get_user_data(tx); + ASSERT_TRUE(user_data); + + ASSERT_EQ(3, user_data->data.size()); + EXPECT_EQ("1\n", user_data->data[0]); + EXPECT_EQ("23\n", user_data->data[1]); + EXPECT_EQ("4", user_data->data[2]); + + delete user_data; +} +#endif diff --git a/test/test_multipart.cpp b/test/test_multipart.cpp new file mode 100644 index 0000000..e423d2d --- /dev/null +++ b/test/test_multipart.cpp @@ -0,0 +1,1940 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <iostream> +#include <gtest/gtest.h> +#include <htp/htp_private.h> +#include "test.h" + +#include <htp/htp_multipart_private.h> + +class Multipart : public testing::Test { +protected: + + void parseRequest(char *headers[], char *data[]) { + size_t i; + + // Calculate body length. + size_t bodyLen = 0; + for (i = 0; data[i] != NULL; i++) { + bodyLen += strlen(data[i]); + } + + // Open connection + connp = htp_connp_create(cfg); + htp_connp_open(connp, "127.0.0.1", 32768, "127.0.0.1", 80, NULL); + + // Send headers. + + for (i = 0; headers[i] != NULL; i++) { + htp_connp_req_data(connp, NULL, headers[i], strlen(headers[i])); + } + + char buf[32]; + snprintf(buf, sizeof (buf), "Content-Length: %zu\r\n", bodyLen); + htp_connp_req_data(connp, NULL, buf, strlen(buf)); + + htp_connp_req_data(connp, NULL, (void *) "\r\n", 2); + + // Send data. + for (i = 0; data[i] != NULL; i++) { + htp_connp_req_data(connp, NULL, data[i], strlen(data[i])); + } + + ASSERT_EQ(1, htp_list_size(connp->conn->transactions)); + + tx = (htp_tx_t *) htp_list_get(connp->conn->transactions, 0); + ASSERT_TRUE(tx != NULL); + + ASSERT_TRUE(tx->request_mpartp != NULL); + mpartp = tx->request_mpartp; + body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + } + + void parseRequestThenVerify(char *headers[], char *data[]) { + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_TRUE(htp_list_size(body->parts) == 3); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_INCOMPLETE); + + // Field 1 + htp_multipart_part_t *field1 = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_TRUE(field1 != NULL); + ASSERT_EQ(MULTIPART_PART_TEXT, field1->type); + ASSERT_TRUE(field1->name != NULL); + ASSERT_TRUE(bstr_cmp_c(field1->name, "field1") == 0); + ASSERT_TRUE(field1->value != NULL); + ASSERT_TRUE(bstr_cmp_c(field1->value, "ABCDEF") == 0); + + // File 1 + htp_multipart_part_t *file1 = (htp_multipart_part_t *) htp_list_get(body->parts, 1); + ASSERT_TRUE(file1 != NULL); + ASSERT_EQ(MULTIPART_PART_FILE, file1->type); + ASSERT_TRUE(file1->name != NULL); + ASSERT_TRUE(bstr_cmp_c(file1->name, "file1") == 0); + ASSERT_TRUE(file1->file->filename != NULL); + ASSERT_TRUE(bstr_cmp_c(file1->file->filename, "file.bin") == 0); + + // Field 2 + htp_multipart_part_t *field2 = (htp_multipart_part_t *) htp_list_get(body->parts, 2); + ASSERT_TRUE(field2 != NULL); + ASSERT_EQ(MULTIPART_PART_TEXT, field2->type); + ASSERT_TRUE(field2->name != NULL); + ASSERT_TRUE(bstr_cmp_c(field2->name, "field2") == 0); + ASSERT_TRUE(field2->value != NULL); + ASSERT_TRUE(bstr_cmp_c(field2->value, "GHIJKL") == 0); + } + + void parseParts(char *parts[]) { + mpartp = htp_mpartp_create(cfg, bstr_dup_c("0123456789"), 0 /* flags */); + + size_t i = 0; + for (;;) { + if (parts[i] == NULL) break; + htp_mpartp_parse(mpartp, parts[i], strlen(parts[i])); + i++; + } + + htp_mpartp_finalize(mpartp); + + body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + } + + void parsePartsThenVerify(char *parts[]) { + parseParts(parts); + + // Examine the result + body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + + ASSERT_TRUE(htp_list_size(body->parts) == 2); + + for (size_t i = 0, n = htp_list_size(body->parts); i < n; i++) { + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, i); + + switch (i) { + case 0: + ASSERT_EQ(MULTIPART_PART_TEXT, part->type); + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "field1") == 0); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "ABCDEF") == 0); + break; + case 1: + ASSERT_EQ(MULTIPART_PART_TEXT, part->type); + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "field2") == 0); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "GHIJKL") == 0); + break; + } + } + } + + virtual void SetUp() { + cfg = htp_config_create(); + htp_config_set_server_personality(cfg, HTP_SERVER_APACHE_2); + htp_config_register_multipart_parser(cfg); + + connp = NULL; + mpartp = NULL; + body = NULL; + tx = NULL; + } + + virtual void TearDown() { + if (connp != NULL) { + htp_connp_destroy_all(connp); + } else if (mpartp != NULL) { + htp_mpartp_destroy(mpartp); + } + + if (cfg != NULL) { + htp_config_destroy(cfg); + } + } + + htp_tx_t *tx; + + htp_connp_t *connp; + + htp_multipart_t *body; + + htp_mpartp_t *mpartp; + + htp_cfg_t *cfg; +}; + +TEST_F(Multipart, Test1) { + mpartp = htp_mpartp_create(cfg, bstr_dup_c("---------------------------41184676334"), 0 /* flags */); + + char *parts[999]; + + size_t i = 0; + parts[i++] = (char *) "-----------------------------41184676334\r\n"; + parts[i++] = (char *) "Content-Disposition: form-data;\n name=\"field1\"\r\n"; + parts[i++] = (char *) "\r\n"; + parts[i++] = (char *) "0123456789\r\n-"; + parts[i++] = (char *) "-------------"; + parts[i++] = (char *) "---------------41184676334\r\n"; + parts[i++] = (char *) "Content-Disposition: form-data;\n name=\"field2\"\r\n"; + parts[i++] = (char *) "\r\n"; + parts[i++] = (char *) "0123456789\r\n-"; + parts[i++] = (char *) "-------------"; + parts[i++] = (char *) "--------------X\r\n"; + parts[i++] = (char *) "-----------------------------41184676334\r\n"; + parts[i++] = (char *) "Content-Disposition: form-data;\n"; + parts[i++] = (char *) " "; + parts[i++] = (char *) "name=\"field3\"\r\n"; + parts[i++] = (char *) "\r\n"; + parts[i++] = (char *) "9876543210\r\n"; + parts[i++] = (char *) "-----------------------------41184676334\r\n"; + parts[i++] = (char *) "Content-Disposition: form-data; name=\"file1\"; filename=\"New Text Document.txt\"\r\nContent-Type: text/plain\r\n\r\n"; + parts[i++] = (char *) "1FFFFFFFFFFFFFFFFFFFFFFFFFFF\r\n"; + parts[i++] = (char *) "2FFFFFFFFFFFFFFFFFFFFFFFFFFE\r"; + parts[i++] = (char *) "3FFFFFFFFFFFFFFFFFFFFFFFFFFF\r\n4FFFFFFFFFFFFFFFFFFFFFFFFF123456789"; + parts[i++] = (char *) "\r\n"; + parts[i++] = (char *) "-----------------------------41184676334\r\n"; + parts[i++] = (char *) "Content-Disposition: form-data; name=\"file2\"; filename=\"New Text Document.txt\"\r\n"; + parts[i++] = (char *) "Content-Type: text/plain\r\n"; + parts[i++] = (char *) "\r\n"; + parts[i++] = (char *) "FFFFFFFFFFFFFFFFFFFFFFFFFFFZ"; + parts[i++] = (char *) "\r\n-----------------------------41184676334--"; + parts[i++] = NULL; + + i = 0; + for (;;) { + if (parts[i] == NULL) break; + htp_mpartp_parse(mpartp, parts[i], strlen(parts[i])); + i++; + } + + htp_mpartp_finalize(mpartp); + + // Examine the result + htp_multipart_t *body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(5, htp_list_size(body->parts)); + + for (size_t i = 0, n = htp_list_size(body->parts); i < n; i++) { + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, i); + + switch (i) { + case 0: + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "field1") == 0); + ASSERT_EQ(MULTIPART_PART_TEXT, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "0123456789") == 0); + break; + case 1: + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "field2") == 0); + ASSERT_EQ(MULTIPART_PART_TEXT, part->type); + ASSERT_TRUE(bstr_cmp_c(part->value, "0123456789\r\n----------------------------X") == 0); + break; + case 2: + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "field3") == 0); + ASSERT_EQ(MULTIPART_PART_TEXT, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "9876543210") == 0); + break; + case 3: + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "file1") == 0); + ASSERT_EQ(MULTIPART_PART_FILE, part->type); + break; + case 4: + ASSERT_TRUE(part->name != NULL); + ASSERT_TRUE(bstr_cmp_c(part->name, "file2") == 0); + ASSERT_EQ(MULTIPART_PART_FILE, part->type); + break; + default: + FAIL() << "More parts than expected"; + break; + } + } + + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); + + htp_mpartp_destroy(mpartp); + mpartp = NULL; +} + +TEST_F(Multipart, Test2) { + mpartp = htp_mpartp_create(cfg, bstr_dup_c("BBB"), 0 /* flags */); + + const char *i1 = "x0000x\n--BBB\n\nx1111x\n--\nx2222x\n--"; + const char *i2 = "BBB\n\nx3333x\n--B"; + const char *i3 = "B\n\nx4444x\n--BB\r"; + const char *i4 = "\n--B"; + const char *i5 = "B"; + const char *i6 = "B\n\nx5555x\r"; + const char *i7 = "\n--x6666x\r"; + const char *i8 = "-"; + const char *i9 = "-"; + + htp_mpartp_parse(mpartp, i1, strlen(i1)); + htp_mpartp_parse(mpartp, i2, strlen(i2)); + htp_mpartp_parse(mpartp, i3, strlen(i3)); + htp_mpartp_parse(mpartp, i4, strlen(i4)); + htp_mpartp_parse(mpartp, i5, strlen(i5)); + htp_mpartp_parse(mpartp, i6, strlen(i6)); + htp_mpartp_parse(mpartp, i7, strlen(i7)); + htp_mpartp_parse(mpartp, i8, strlen(i8)); + htp_mpartp_parse(mpartp, i9, strlen(i9)); + htp_mpartp_finalize(mpartp); + + htp_multipart_t *body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(4, htp_list_size(body->parts)); + + for (size_t i = 0, n = htp_list_size(body->parts); i < n; i++) { + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, i); + + switch (i) { + case 0: + ASSERT_EQ(MULTIPART_PART_PREAMBLE, part->type); + + ASSERT_TRUE(bstr_cmp_c(part->value, "x0000x") == 0); + break; + case 1: + ASSERT_EQ(MULTIPART_PART_UNKNOWN, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "x1111x\n--\nx2222x") == 0); + break; + case 2: + ASSERT_EQ(MULTIPART_PART_UNKNOWN, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "x3333x\n--BB\n\nx4444x\n--BB") == 0); + break; + case 3: + ASSERT_EQ(MULTIPART_PART_UNKNOWN, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "x5555x\r\n--x6666x\r--") == 0); + break; + default: + FAIL(); + + } + } + + ASSERT_TRUE(body->flags & HTP_MULTIPART_INCOMPLETE); + + htp_mpartp_destroy(mpartp); + mpartp = NULL; +} + +TEST_F(Multipart, Test3) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n", + "--0", + "1", + "2", + "4: Value\r\n", + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); +} + +TEST_F(Multipart, BeginsWithoutLine) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); +} + +TEST_F(Multipart, BeginsWithCrLf) { + char *parts[] = { + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); +} + +TEST_F(Multipart, BeginsWithLf) { + char *parts[] = { + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); +} + +TEST_F(Multipart, CrLfLineEndings) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_FALSE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, LfLineEndings) { + char *parts[] = { + "--0123456789\n" + "Content-Disposition: form-data; name=\"field1\"\n" + "\n" + "ABCDEF" + "\n--0123456789\n" + "Content-Disposition: form-data; name=\"field2\"\n" + "\n" + "GHIJKL" + "\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_FALSE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, CrAndLfLineEndings1) { + char *parts[] = { + "--0123456789\n" + "Content-Disposition: form-data; name=\"field1\"\n" + "\n" + "ABCDEF" + "\r\n--0123456789\n" + "Content-Disposition: form-data; name=\"field2\"\n" + "\n" + "GHIJKL" + "\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, CrAndLfLineEndings2) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\n" + "\n" + "ABCDEF" + "\n--0123456789\n" + "Content-Disposition: form-data; name=\"field2\"\n" + "\n" + "GHIJKL" + "\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, CrAndLfLineEndings3) { + char *parts[] = { + "--0123456789\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, CrAndLfLineEndings4) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_LF_LINE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CRLF_LINE); +} + +TEST_F(Multipart, BoundaryInstanceWithLwsAfter) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789 \r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_BBOUNDARY_LWS_AFTER); +} + +TEST_F(Multipart, BoundaryInstanceWithNonLwsAfter1) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789 X \r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_BBOUNDARY_NLWS_AFTER); +} + +TEST_F(Multipart, BoundaryInstanceWithNonLwsAfter2) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789-\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_BBOUNDARY_NLWS_AFTER); +} + +TEST_F(Multipart, BoundaryInstanceWithNonLwsAfter3) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_BBOUNDARY_NLWS_AFTER); +} + +TEST_F(Multipart, WithPreamble) { + char *parts[] = { + "Preamble" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789 X \r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_HAS_PREAMBLE); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_TRUE(part != NULL); + ASSERT_EQ(MULTIPART_PART_PREAMBLE, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "Preamble") == 0); +} + +TEST_F(Multipart, WithEpilogue1) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--\r\n" + "Epilogue", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_HAS_EPILOGUE); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 2); + ASSERT_TRUE(part != NULL); + ASSERT_EQ(MULTIPART_PART_EPILOGUE, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "Epilogue") == 0); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_INCOMPLETE); + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); +} + +TEST_F(Multipart, WithEpilogue2) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--\r\n" + "Epi\nlogue", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_HAS_EPILOGUE); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 2); + ASSERT_TRUE(part != NULL); + ASSERT_EQ(MULTIPART_PART_EPILOGUE, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "Epi\nlogue") == 0); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_INCOMPLETE); + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); +} + +TEST_F(Multipart, WithEpilogue3) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--\r\n" + "Epi\r", + "\n--logue", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_HAS_EPILOGUE); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 2); + ASSERT_TRUE(part != NULL); + ASSERT_EQ(MULTIPART_PART_EPILOGUE, part->type); + ASSERT_TRUE(part->value != NULL); + ASSERT_TRUE(bstr_cmp_c(part->value, "Epi\r\n--logue") == 0); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_INCOMPLETE); + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); +} + +TEST_F(Multipart, WithEpilogue4) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--\r\n" + "Epilogue1" + "\r\n--0123456789--\r\n" + "Epilogue2", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(4, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_HAS_EPILOGUE); + + htp_multipart_part_t *ep1 = (htp_multipart_part_t *) htp_list_get(body->parts, 2); + ASSERT_TRUE(ep1 != NULL); + ASSERT_EQ(MULTIPART_PART_EPILOGUE, ep1->type); + ASSERT_TRUE(ep1->value != NULL); + ASSERT_TRUE(bstr_cmp_c(ep1->value, "Epilogue1") == 0); + + htp_multipart_part_t *ep2 = (htp_multipart_part_t *) htp_list_get(body->parts, 3); + ASSERT_TRUE(ep2 != NULL); + ASSERT_EQ(MULTIPART_PART_EPILOGUE, ep2->type); + ASSERT_TRUE(ep2->value != NULL); + ASSERT_TRUE(bstr_cmp_c(ep2->value, "Epilogue2") == 0); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_INCOMPLETE); + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); +} + +TEST_F(Multipart, HasLastBoundary) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(2, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY); +} + +TEST_F(Multipart, DoesNotHaveLastBoundary) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY); +} + +TEST_F(Multipart, PartAfterLastBoundary) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789--\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789", + NULL + }; + + parsePartsThenVerify(parts); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_SEEN_LAST_BOUNDARY); +} + +TEST_F(Multipart, UnknownPart) { + char *parts[] = { + "--0123456789\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789--", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(1, htp_list_size(body->parts)); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_EQ(MULTIPART_PART_UNKNOWN, part->type); +} + +TEST_F(Multipart, WithFile) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"; filename=\"test.bin\"\r\n" + "Content-Type: application/octet-stream \r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(2, htp_list_size(body->parts)); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 1); + ASSERT_EQ(MULTIPART_PART_FILE, part->type); + ASSERT_TRUE(part->content_type != NULL); + ASSERT_TRUE(bstr_cmp_c(part->content_type, "application/octet-stream") == 0); + ASSERT_TRUE(part->file != NULL); + ASSERT_TRUE(bstr_cmp_c(part->file->filename, "test.bin") == 0); + ASSERT_EQ(6, part->file->len); +} + +TEST_F(Multipart, WithFileExternallyStored) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"; filename=\"test.bin\"\r\n" + "Content-Type: application/octet-stream \r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + cfg->extract_request_files = 1; + cfg->tmpdir = "/tmp"; + + parseParts(parts); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(2, htp_list_size(body->parts)); + + htp_multipart_part_t *part = (htp_multipart_part_t *) htp_list_get(body->parts, 1); + ASSERT_EQ(MULTIPART_PART_FILE, part->type); + ASSERT_TRUE(part->content_type != NULL); + ASSERT_TRUE(bstr_cmp_c(part->content_type, "application/octet-stream") == 0); + ASSERT_TRUE(part->file != NULL); + ASSERT_TRUE(bstr_cmp_c(part->file->filename, "test.bin") == 0); + ASSERT_EQ(6, part->file->len); + + ASSERT_TRUE(part->file->tmpname != NULL); + + int fd = open(part->file->tmpname, O_RDONLY | O_BINARY); + ASSERT_TRUE(fd >= 0); + + struct stat statbuf; + ASSERT_TRUE((fstat(fd, &statbuf) >= 0)); + ASSERT_EQ(6, statbuf.st_size); + + char buf[7]; + ssize_t result = read(fd, buf, 6); + ASSERT_EQ(6, result); + buf[6] = '\0'; + + ASSERT_STREQ("GHIJKL", buf); + + close(fd); +} + +TEST_F(Multipart, PartHeadersEmptyLineBug) { + char *parts[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r", + "\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parsePartsThenVerify(parts); +} + +TEST_F(Multipart, CompleteRequest) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_FALSE(body->flags & HTP_MULTIPART_PART_HEADER_FOLDING); +} + +TEST_F(Multipart, InvalidHeader1) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Colon missing. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidHeader2) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Whitespace after header name. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition : form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidHeader3) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Whitespace before header name. + + char *data[] = { + "--0123456789\r\n" + " Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidHeader4) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Invalid header name; contains a space. + + char *data[] = { + "--0123456789\r\n" + "Content Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidHeader5) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // No header name. + + char *data[] = { + "--0123456789\r\n" + ": form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidHeader6) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // No header name. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: \r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, NulByte) { + mpartp = htp_mpartp_create(cfg, bstr_dup_c("0123456789"), 0 /* flags */); + + // NUL byte in the part header. + + char i1[] = "--0123456789\r\n" + "Content-Disposition: form-data; "; + char i2[] = ""; + char i3[] = + "name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--"; + + htp_mpartp_parse(mpartp, i1, strlen(i1)); + htp_mpartp_parse(mpartp, i2, 1); + htp_mpartp_parse(mpartp, i3, strlen(i3)); + htp_mpartp_finalize(mpartp); + + htp_multipart_t *body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_NUL_BYTE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_INVALID); +} + +TEST_F(Multipart, MultipleContentTypeHeadersEvasion) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data\r\n" + "Content-Type: boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(tx->request_content_type != NULL); + ASSERT_TRUE(bstr_cmp_c(tx->request_content_type, "multipart/form-data") == 0); +} + +TEST_F(Multipart, BoundaryNormal) { + char *inputs[] = { + "multipart/form-data; boundary=----WebKitFormBoundaryT4AfwQCOgIxNVwlD", + "multipart/form-data; boundary=---------------------------21071316483088", + "multipart/form-data; boundary=---------------------------7dd13e11c0452", + "multipart/form-data; boundary=----------2JL5oh7QWEDwyBllIRc7fh", + "multipart/form-data; boundary=----WebKitFormBoundaryre6zL3b0BelnTY5S", + NULL + }; + + char *outputs[] = { + "----WebKitFormBoundaryT4AfwQCOgIxNVwlD", + "---------------------------21071316483088", + "---------------------------7dd13e11c0452", + "----------2JL5oh7QWEDwyBllIRc7fh", + "----WebKitFormBoundaryre6zL3b0BelnTY5S", + NULL + }; + + for (size_t i = 0; inputs[i] != NULL; i++) { + bstr *input = bstr_dup_c(inputs[i]); + bstr *boundary = NULL; + uint64_t flags = 0; + + SCOPED_TRACE(inputs[i]); + + htp_status_t rc = htp_mpartp_find_boundary(input, &boundary, &flags); + ASSERT_EQ(HTP_OK, rc); + + ASSERT_TRUE(boundary != NULL); + ASSERT_TRUE(bstr_cmp_c(boundary, outputs[i]) == 0); + ASSERT_EQ(0, flags); + + bstr_free(boundary); + bstr_free(input); + } +} + +TEST_F(Multipart, BoundaryParsing) { + char *inputs[] = { + "multipart/form-data; boundary=1 ", + "multipart/form-data; boundary=1, boundary=2", + "multipart/form-data; boundary=\"1\"", + "multipart/form-data; boundary=\"1\" ", + "multipart/form-data; boundary=\"1", + NULL + }; + + char *outputs[] = { + "1", + "1", + "1", + "1", + "\"1", + NULL + }; + + for (size_t i = 0; inputs[i] != NULL; i++) { + bstr *input = bstr_dup_c(inputs[i]); + bstr *boundary = NULL; + uint64_t flags = 0; + + SCOPED_TRACE(inputs[i]); + + htp_status_t rc = htp_mpartp_find_boundary(input, &boundary, &flags); + ASSERT_EQ(HTP_OK, rc); + + ASSERT_TRUE(boundary != NULL); + ASSERT_TRUE(bstr_cmp_c(boundary, outputs[i]) == 0); + + bstr_free(boundary); + bstr_free(input); + } +} + +TEST_F(Multipart, BoundaryInvalid) { + char *inputs[] = { + "multipart/form-data boundary=1", + "multipart/form-data ; boundary=1", + "multipart/form-data, boundary=1", + "multipart/form-data , boundary=1", + "multipart/form-datax; boundary=1", + "multipart/; boundary=1", + "multipart; boundary=1", + "application/octet-stream; boundary=1", + "boundary=1", + "multipart/form-data; boundary", + "multipart/form-data; boundary=", + "multipart/form-data; boundaryX=", + "multipart/form-data; boundary=\"\"", + "multipart/form-data; bounDary=1", + "multipart/form-data; boundary=1; boundary=2", + "multipart/form-data; boundary=1 2", + "multipart/form-data boundary=01234567890123456789012345678901234567890123456789012345678901234567890123456789", + NULL + }; + + for (size_t i = 0; inputs[i] != NULL; i++) { + bstr *input = bstr_dup_c(inputs[i]); + bstr *boundary = NULL; + uint64_t flags = 0; + + SCOPED_TRACE(inputs[i]); + + htp_status_t rc = htp_mpartp_find_boundary(input, &boundary, &flags); + ASSERT_TRUE(rc != HTP_ERROR); + + ASSERT_TRUE(flags & HTP_MULTIPART_HBOUNDARY_INVALID); + + bstr_free(boundary); + bstr_free(input); + } +} + +TEST_F(Multipart, BoundaryUnusual) { + char *inputs[] = { + "multipart/form-data; boundary=1 ", + "multipart/form-data; boundary =1", + "multipart/form-data; boundary= 1", + "multipart/form-data; boundary=\"1\"", + "multipart/form-data; boundary=\" 1 \"", + //"multipart/form-data; boundary=1-2", + "multipart/form-data; boundary=\"1?2\"", + NULL + }; + + for (size_t i = 0; inputs[i] != NULL; i++) { + bstr *input = bstr_dup_c(inputs[i]); + bstr *boundary = NULL; + uint64_t flags = 0; + + SCOPED_TRACE(inputs[i]); + + htp_status_t rc = htp_mpartp_find_boundary(input, &boundary, &flags); + ASSERT_EQ(HTP_OK, rc); + + ASSERT_TRUE(boundary != NULL); + ASSERT_TRUE(flags & HTP_MULTIPART_HBOUNDARY_UNUSUAL); + + bstr_free(boundary); + bstr_free(input); + } +} + +TEST_F(Multipart, CaseInsitiveBoundaryMatching) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=grumpyWizards\r\n", + NULL + }; + + // The second boundary is all-lowercase and shouldn't be matched on. + char *data[] = { + "--grumpyWizards\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n-grumpywizards\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--grumpyWizards\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--grumpyWizards--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(2, htp_list_size(body->parts)); +} + +TEST_F(Multipart, FoldedContentDisposition) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\";\r\n" + " filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_FOLDING); +} + +TEST_F(Multipart, FoldedContentDisposition2) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\";\r\n" + "\rfilename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_FOLDING); +} + +TEST_F(Multipart, InvalidPartNoData) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // The first part terminates abruptly by the next boundary. This + // actually works in PHP because its part header parser will + // consume everything (even boundaries) until the next empty line. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + htp_multipart_part_t *field1 = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_TRUE(field1 != NULL); + ASSERT_EQ(MULTIPART_PART_UNKNOWN, field1->type); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INCOMPLETE); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidPartNoContentDisposition) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // A part without a Content-Disposition header. + + char *data[] = { + "--0123456789\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_UNKNOWN); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidPartMultipleCD) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // When we encounter a part with more than one C-D header, we + // don't know which one the backend will use. Thus, we raise + // HTP_MULTIPART_PART_INVALID. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "Content-Disposition: form-data; name=\"field3\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_REPEATED); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidPartUnknownHeader) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Unknown C-D header "Unknown". + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "Unknown: Header\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_HEADER_UNKNOWN); + ASSERT_TRUE(body->flags & HTP_MULTIPART_PART_INVALID); +} + +TEST_F(Multipart, InvalidContentDispositionMultipleParams1) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Two "name" parameters in a C-D header. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"; name=\"field3\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_PARAM_REPEATED); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_INVALID); +} + +TEST_F(Multipart, InvalidContentDispositionMultipleParams2) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Two "filename" parameters in a C-D header. + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"; filename=\"file2.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_PARAM_REPEATED); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_INVALID); +} + +TEST_F(Multipart, InvalidContentDispositionUnknownParam) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + // Unknown C-D parameter "test". + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\"; test=\"param\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_PARAM_UNKNOWN); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_INVALID); +} + +TEST_F(Multipart, InvalidContentDispositionSyntax) { + char *inputs[] = { + // Parameter value not quoted. + "form-data; name=field1", + // Using single quotes around parameter value. + "form-data; name='field1'", + // No semicolon after form-data in the C-D header. + "form-data name=\"field1\"", + // No semicolon after C-D parameter. + "form-data; name=\"file1\" filename=\"file.bin\"", + // Missing terminating quote in C-D parameter value. + "form-data; name=\"field1", + // Backslash as the last character in parameter value + "form-data; name=\"field1\\", + // C-D header does not begin with "form-data". + "invalid-syntax; name=\"field1", + // Escape the terminating double quote. + "name=\"field1\\\"", + // Incomplete header. + "form-data; ", + // Incomplete header. + "form-data; name", + // Incomplete header. + "form-data; name ", + // Incomplete header. + "form-data; name ?", + // Incomplete header. + "form-data; name=", + // Incomplete header. + "form-data; name= ", + NULL + }; + + for (size_t i = 0; inputs[i] != NULL; i++) { + SCOPED_TRACE(inputs[i]); + + mpartp = htp_mpartp_create(cfg, bstr_dup_c("123"), 0 /* flags */); + + htp_multipart_part_t *part = (htp_multipart_part_t *) calloc(1, sizeof (htp_multipart_part_t)); + part->headers = htp_table_create(4); + part->parser = mpartp; + + htp_header_t *h = (htp_header_t *) calloc(1, sizeof (htp_header_t)); + h->name = bstr_dup_c("Content-Disposition"); + h->value = bstr_dup_c(inputs[i]); + + htp_table_add(part->headers, h->name, h); + + htp_status_t rc = htp_mpart_part_parse_c_d(part); + ASSERT_EQ(HTP_DECLINED, rc); + + body = htp_mpartp_get_multipart(mpartp); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_SYNTAX_INVALID); + ASSERT_TRUE(body->flags & HTP_MULTIPART_CD_INVALID); + + htp_mpart_part_destroy(part, 0); + htp_mpartp_destroy(mpartp); + mpartp = NULL; + } +} + +TEST_F(Multipart, ParamValueEscaping) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"---\\\"---\\\\---\"\r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequest(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + ASSERT_EQ(3, htp_list_size(body->parts)); + + ASSERT_FALSE(body->flags & HTP_MULTIPART_CD_INVALID); + + htp_multipart_part_t *field1 = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_TRUE(field1 != NULL); + ASSERT_EQ(MULTIPART_PART_TEXT, field1->type); + ASSERT_TRUE(field1->name != NULL); + ASSERT_TRUE(bstr_cmp_c(field1->name, "---\"---\\---") == 0); + ASSERT_TRUE(field1->value != NULL); + ASSERT_TRUE(bstr_cmp_c(field1->value, "ABCDEF") == 0); +} + +TEST_F(Multipart, HeaderValueTrim) { + char *headers[] = { + "POST / HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=0123456789\r\n", + NULL + }; + + char *data[] = { + "--0123456789\r\n" + "Content-Disposition: form-data; name=\"field1\" \r\n" + "\r\n" + "ABCDEF" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"file.bin\"\r\n" + "\r\n" + "FILEDATA" + "\r\n--0123456789\r\n" + "Content-Disposition: form-data; name=\"field2\"\r\n" + "\r\n" + "GHIJKL" + "\r\n--0123456789--", + NULL + }; + + parseRequestThenVerify(headers, data); + + ASSERT_TRUE(body != NULL); + ASSERT_TRUE(body->parts != NULL); + + htp_multipart_part_t *field1 = (htp_multipart_part_t *) htp_list_get(body->parts, 0); + ASSERT_TRUE(field1 != NULL); + htp_header_t *h = (htp_header_t *) htp_table_get_c(field1->headers, "content-disposition"); + ASSERT_TRUE(h != NULL); + ASSERT_TRUE(bstr_cmp_c(h->value, "form-data; name=\"field1\" ") == 0); +} diff --git a/test/test_utils.cpp b/test/test_utils.cpp new file mode 100644 index 0000000..8eb1352 --- /dev/null +++ b/test/test_utils.cpp @@ -0,0 +1,1839 @@ +/*************************************************************************** + * Copyright (c) 2009-2010 Open Information Security Foundation + * Copyright (c) 2010-2013 Qualys, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + + * - Neither the name of the Qualys, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + ***************************************************************************/ + +/** + * @file + * @brief Tests for various utility functions. + * + * @author Craig Forbes <cforbes@qualys.com> + * @author Ivan Ristic <ivanr@webkreator.com> + */ + +#include <iostream> +#include <gtest/gtest.h> +#include <htp/htp_private.h> + +TEST(Base64, Single) { + EXPECT_EQ(62, htp_base64_decode_single('+')); + EXPECT_EQ(63, htp_base64_decode_single('/')); + EXPECT_EQ(-1, htp_base64_decode_single(',')); + EXPECT_EQ(-1, htp_base64_decode_single(0)); + EXPECT_EQ(-1, htp_base64_decode_single('~')); + EXPECT_EQ(26, htp_base64_decode_single('a')); + EXPECT_EQ(0, htp_base64_decode_single('A')); +} + +TEST(Base64, Decode) { + const char *input = "dGhpcyBpcyBhIHRlc3QuLg=="; + bstr *out = htp_base64_decode_mem(input, strlen(input)); + EXPECT_EQ(0, bstr_cmp_c(out, "this is a test..")); + bstr_free(out); +} + +TEST(UtilTest, Separator) { + EXPECT_EQ(0, htp_is_separator('a')); + EXPECT_EQ(0, htp_is_separator('^')); + EXPECT_EQ(0, htp_is_separator('-')); + EXPECT_EQ(0, htp_is_separator('_')); + EXPECT_EQ(0, htp_is_separator('&')); + EXPECT_EQ(1, htp_is_separator('(')); + EXPECT_EQ(1, htp_is_separator('\\')); + EXPECT_EQ(1, htp_is_separator('/')); + EXPECT_EQ(1, htp_is_separator('=')); + EXPECT_EQ(1, htp_is_separator('\t')); +} + +TEST(UtilTest, Text) { + EXPECT_EQ(1, htp_is_text('\t')); + EXPECT_EQ(1, htp_is_text('a')); + EXPECT_EQ(1, htp_is_text('~')); + EXPECT_EQ(1, htp_is_text(' ')); + EXPECT_EQ(0, htp_is_text('\n')); + EXPECT_EQ(0, htp_is_text('\r')); + EXPECT_EQ(0, htp_is_text('\r')); + EXPECT_EQ(0, htp_is_text(31)); +} + +TEST(UtilTest, Token) { + EXPECT_EQ(1, htp_is_token('a')); + EXPECT_EQ(1, htp_is_token('&')); + EXPECT_EQ(1, htp_is_token('+')); + EXPECT_EQ(0, htp_is_token('\t')); + EXPECT_EQ(0, htp_is_token('\n')); +} + +TEST(UtilTest, Chomp) { + char data[100]; + size_t len; + int result; + + strcpy(data, "test\r\n"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(2, result); + EXPECT_EQ(4, len); + + strcpy(data, "test\r\n\n"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(2, result); + EXPECT_EQ(4, len); + + strcpy(data, "test\r\n\r\n"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(2, result); + EXPECT_EQ(4, len); + + strcpy(data, "te\nst"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(0, result); + EXPECT_EQ(5, len); + + strcpy(data, "foo\n"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(1, result); + EXPECT_EQ(3, len); + + strcpy(data, "arfarf"); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(0, result); + EXPECT_EQ(6, len); + + strcpy(data, ""); + len = strlen(data); + result = htp_chomp((unsigned char *) data, &len); + EXPECT_EQ(0, result); + EXPECT_EQ(0, len); +} + +TEST(UtilTest, Space) { + EXPECT_EQ(0, htp_is_space('a')); + EXPECT_EQ(1, htp_is_space(' ')); + EXPECT_EQ(1, htp_is_space('\f')); + EXPECT_EQ(1, htp_is_space('\n')); + EXPECT_EQ(1, htp_is_space('\r')); + EXPECT_EQ(1, htp_is_space('\t')); + EXPECT_EQ(1, htp_is_space('\v')); +} + +TEST(UtilTest, Method) { + bstr *method = bstr_dup_c("GET"); + + EXPECT_EQ(HTP_M_GET, htp_convert_method_to_number(method)); + + bstr_free(method); +} + +TEST(UtilTest, IsLineEmpty) { + char data[100]; + strcpy(data, "arfarf"); + EXPECT_EQ(0, htp_is_line_empty((unsigned char*) data, 6)); + + strcpy(data, "\r\n"); + EXPECT_EQ(1, htp_is_line_empty((unsigned char*) data, 2)); + strcpy(data, "\r"); + EXPECT_EQ(1, htp_is_line_empty((unsigned char*) data, 1)); + EXPECT_EQ(0, htp_is_line_empty((unsigned char*) data, 0)); + +} + +TEST(UtilTest, IsLineWhitespace) { + char data[100]; + strcpy(data, "arfarf"); + EXPECT_EQ(0, htp_is_line_whitespace((unsigned char*) data, 6)); + + strcpy(data, "\r\n"); + EXPECT_EQ(1, htp_is_line_whitespace((unsigned char*) data, 2)); + strcpy(data, "\r"); + EXPECT_EQ(1, htp_is_line_whitespace((unsigned char*) data, 1)); + EXPECT_EQ(1, htp_is_line_whitespace((unsigned char*) data, 0)); +} + +TEST(UtilTest, ParsePositiveIntegerWhitespace) { + EXPECT_EQ(123, htp_parse_positive_integer_whitespace( + (unsigned char*) "123 ", 6, 10)); + EXPECT_EQ(123, htp_parse_positive_integer_whitespace( + (unsigned char*) " 123", 6, 10)); + EXPECT_EQ(123, htp_parse_positive_integer_whitespace( + (unsigned char*) " 123 ", 9, 10)); + EXPECT_EQ(-1, htp_parse_positive_integer_whitespace( + (unsigned char*) "a123", 4, 10)); + EXPECT_EQ(-1001, htp_parse_positive_integer_whitespace( + (unsigned char*) " \t", 4, 10)); + EXPECT_EQ(-1002, htp_parse_positive_integer_whitespace( + (unsigned char*) "123b ", 5, 10)); + + EXPECT_EQ(-1, htp_parse_positive_integer_whitespace( + (unsigned char*) " a123 ", 9, 10)); + EXPECT_EQ(-1002, htp_parse_positive_integer_whitespace( + (unsigned char*) " 123b ", 9, 10)); + + EXPECT_EQ(0x123, htp_parse_positive_integer_whitespace( + (unsigned char*) " 123 ", 9, 16)); +} + +TEST(UtilTest, ParseContentLength) { + bstr *str = bstr_dup_c("134"); + + EXPECT_EQ(134, htp_parse_content_length(str, NULL)); + + bstr_free(str); +} + +TEST(UtilTest, ParseChunkedLength) { + EXPECT_EQ(0x12a5, htp_parse_chunked_length((unsigned char*) "12a5", 4, NULL)); +} + +TEST(UtilTest, IsLineFolded) { + EXPECT_EQ(-1, htp_connp_is_line_folded((unsigned char*) "", 0)); + EXPECT_EQ(1, htp_connp_is_line_folded((unsigned char*) "\tline", 5)); + EXPECT_EQ(1, htp_connp_is_line_folded((unsigned char*) " line", 5)); + EXPECT_EQ(0, htp_connp_is_line_folded((unsigned char*) "line ", 5)); +} + +static void free_htp_uri_t(htp_uri_t **urip) { + htp_uri_t *uri = *urip; + + if (uri == NULL) { + return; + } + bstr_free(uri->scheme); + bstr_free(uri->username); + bstr_free(uri->password); + bstr_free(uri->hostname); + bstr_free(uri->port); + bstr_free(uri->path); + bstr_free(uri->query); + bstr_free(uri->fragment); + + free(uri); + *urip = NULL; +} + +struct uri_expected { + const char *scheme; + const char *username; + const char *password; + const char *hostname; + const char *port; + const char *path; + const char *query; + const char *fragment; +}; + +struct uri_test { + const char *uri; + uri_expected expected; +}; + +bool bstr_equal_c(const bstr *b, const char *c) { + if ((c == NULL) || (b == NULL)) { + return (c == NULL) && (b == NULL); + } else { + return (0 == bstr_cmp_c(b, c)); + } +} + +void append_message(std::ostream & o, + const char *label, const char *expected, bstr *actual) { + o << label << " missmatch: "; + if (expected != NULL) { + o << "'" << expected << "'"; + } else { + o << "<NULL>"; + } + o << " != "; + if (actual != NULL) { + o << "'"; + o.write((const char *) bstr_ptr(actual), bstr_len(actual)); + o << "'"; + } else { + o << "<NULL>"; + } + o << std::endl; +} + +static ::testing::AssertionResult UriIsExpected(const char *expected_var, + const char *actual_var, + const uri_expected &expected, + const htp_uri_t *actual) { + std::stringstream msg; + bool equal = true; + + if (!bstr_equal_c(actual->scheme, expected.scheme)) { + equal = false; + append_message(msg, "scheme", expected.scheme, actual->scheme); + } + + if (!bstr_equal_c(actual->username, expected.username)) { + equal = false; + append_message(msg, "username", expected.username, actual->username); + } + + if (!bstr_equal_c(actual->password, expected.password)) { + equal = false; + append_message(msg, "password", expected.password, actual->password); + } + + if (!bstr_equal_c(actual->hostname, expected.hostname)) { + equal = false; + append_message(msg, "hostname", expected.hostname, actual->hostname); + } + + if (!bstr_equal_c(actual->port, expected.port)) { + equal = false; + append_message(msg, "port", expected.port, actual->port); + } + + if (!bstr_equal_c(actual->path, expected.path)) { + equal = false; + append_message(msg, "path", expected.path, actual->path); + } + + if (!bstr_equal_c(actual->query, expected.query)) { + equal = false; + append_message(msg, "query", expected.query, actual->query); + } + + if (!bstr_equal_c(actual->fragment, expected.fragment)) { + equal = false; + append_message(msg, "fragment", expected.fragment, actual->fragment); + } + + if (equal) { + return ::testing::AssertionSuccess(); + } else { + return ::testing::AssertionFailure() << msg.str(); + } +} + +struct uri_test uri_tests[] = { + {"http://user:pass@www.example.com:1234/path1/path2?a=b&c=d#frag", + {"http", "user", "pass", "www.example.com", "1234", "/path1/path2", "a=b&c=d", "frag"}}, + {"http://host.com/path", + {"http", NULL, NULL, "host.com", NULL, "/path", NULL, NULL}}, + {"http://", + {"http", NULL, NULL, NULL, NULL, "//", NULL, NULL}}, + {"/path", + {NULL, NULL, NULL, NULL, NULL, "/path", NULL, NULL}}, + {"://", + {"", NULL, NULL, NULL, NULL, "//", NULL, NULL}}, + {"", + {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}}, + {"http://user@host.com", + {"http", "user", NULL, "host.com", NULL, "", NULL, NULL}}, + {NULL, + { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }} +}; + +TEST(UtilTest, HtpParseUri) { + bstr *input = NULL; + htp_uri_t *uri = NULL; + uri_test *test; + + input = bstr_dup_c(""); + EXPECT_EQ(HTP_OK, htp_parse_uri(input, &uri)); + bstr_free(input); + free_htp_uri_t(&uri); + + test = uri_tests; + while (test->uri != NULL) { + input = bstr_dup_c(test->uri); + EXPECT_EQ(HTP_OK, htp_parse_uri(input, &uri)); + EXPECT_PRED_FORMAT2(UriIsExpected, test->expected, uri) + << "Failed URI = " << test->uri << std::endl; + + bstr_free(input); + free_htp_uri_t(&uri); + ++test; + } +} + +TEST(UtilTest, ParseHostPort1) { + bstr *i = bstr_dup_c("www.example.com"); + bstr *host; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(bstr_cmp(i, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort2) { + bstr *i = bstr_dup_c(" www.example.com "); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort3) { + bstr *i = bstr_dup_c(" www.example.com:8001 "); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(8001, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort4) { + bstr *i = bstr_dup_c(" www.example.com : 8001 "); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(8001, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort5) { + bstr *i = bstr_dup_c("www.example.com."); + bstr *e = bstr_dup_c("www.example.com."); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort6) { + bstr *i = bstr_dup_c("www.example.com.:8001"); + bstr *e = bstr_dup_c("www.example.com."); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(8001, port); + ASSERT_EQ(0, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort7) { + bstr *i = bstr_dup_c("www.example.com:"); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort8) { + bstr *i = bstr_dup_c("www.example.com:ff"); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort9) { + bstr *i = bstr_dup_c("www.example.com:0"); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort10) { + bstr *i = bstr_dup_c("www.example.com:65536"); + bstr *e = bstr_dup_c("www.example.com"); + bstr *host = NULL; + int port; + int flag = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &flag)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, flag); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort11) { + bstr *i = bstr_dup_c("[::1]:8080"); + bstr *e = bstr_dup_c("[::1]"); + bstr *host = NULL; + int port; + int invalid = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &invalid)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(8080, port); + ASSERT_EQ(0, invalid); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort12) { + bstr *i = bstr_dup_c("[::1]:"); + bstr *e = bstr_dup_c("[::1]"); + bstr *host = NULL; + int port; + int invalid = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &invalid)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, invalid); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort13) { + bstr *i = bstr_dup_c("[::1]x"); + bstr *e = bstr_dup_c("[::1]"); + bstr *host = NULL; + int port; + int invalid = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &invalid)); + + ASSERT_TRUE(host != NULL); + ASSERT_TRUE(bstr_cmp(e, host) == 0); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, invalid); + + bstr_free(host); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseHostPort14) { + bstr *i = bstr_dup_c("[::1"); + bstr *host = NULL; + int port; + int invalid = 0; + + ASSERT_EQ(HTP_OK, htp_parse_hostport(i, &host, NULL, &port, &invalid)); + + ASSERT_TRUE(host == NULL); + ASSERT_EQ(-1, port); + ASSERT_EQ(1, invalid); + + bstr_free(host); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType1) { + bstr *i = bstr_dup_c("multipart/form-data"); + bstr *e = bstr_dup_c("multipart/form-data"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType2) { + bstr *i = bstr_dup_c("multipart/form-data;boundary=X"); + bstr *e = bstr_dup_c("multipart/form-data"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType3) { + bstr *i = bstr_dup_c("multipart/form-data boundary=X"); + bstr *e = bstr_dup_c("multipart/form-data"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType4) { + bstr *i = bstr_dup_c("multipart/form-data,boundary=X"); + bstr *e = bstr_dup_c("multipart/form-data"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType5) { + bstr *i = bstr_dup_c("multipart/FoRm-data"); + bstr *e = bstr_dup_c("multipart/form-data"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ParseContentType6) { + bstr *i = bstr_dup_c("multipart/form-data\t boundary=X"); + bstr *e = bstr_dup_c("multipart/form-data\t"); + bstr *ct = NULL; + + ASSERT_EQ(HTP_OK, htp_parse_ct_header(i, &ct)); + + ASSERT_TRUE(ct != NULL); + ASSERT_TRUE(bstr_cmp(e, ct) == 0); + + bstr_free(ct); + bstr_free(e); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname1) { + bstr *i = bstr_dup_c("www.example.com"); + ASSERT_EQ(1, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname2) { + bstr *i = bstr_dup_c(".www.example.com"); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname3) { + bstr *i = bstr_dup_c("www..example.com"); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname4) { + bstr *i = bstr_dup_c("www.example.com.."); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname5) { + bstr *i = bstr_dup_c("www example com"); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname6) { + bstr *i = bstr_dup_c(""); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname7) { + // Label over 63 characters. + bstr *i = bstr_dup_c("www.exampleexampleexampleexampleexampleexampleexampleexampleexampleexample.com"); + ASSERT_EQ(0, htp_validate_hostname(i)); + bstr_free(i); +} + +TEST(UtilTest, ValidateHostname8) { + bstr *i = bstr_dup_c("www.ExAmplE-1984.com"); + ASSERT_EQ(1, htp_validate_hostname(i)); + bstr_free(i); +} + +class DecodingTest : public testing::Test { +protected: + + virtual void SetUp() { + testing::Test::SetUp(); + + cfg = htp_config_create(); + connp = htp_connp_create(cfg); + htp_connp_open(connp, "127.0.0.1", 32768, "127.0.0.1", 80, NULL); + tx = htp_connp_tx_create(connp); + } + + virtual void TearDown() { + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + + testing::Test::TearDown(); + } + + htp_connp_t *connp; + + htp_cfg_t *cfg; + + htp_tx_t *tx; +}; + +TEST_F(DecodingTest, DecodeUrlencodedInplace1_Identity) { + bstr *i = bstr_dup_c("/dest"); + bstr *e = bstr_dup_c("/dest"); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace2_Urlencoded) { + bstr *i = bstr_dup_c("/%64est"); + bstr *e = bstr_dup_c("/dest"); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace3_UrlencodedInvalidPreserve) { + bstr *i = bstr_dup_c("/%xxest"); + bstr *e = bstr_dup_c("/%xxest"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace4_UrlencodedInvalidRemove) { + bstr *i = bstr_dup_c("/%xxest"); + bstr *e = bstr_dup_c("/xxest"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace5_UrlencodedInvalidDecode) { + bstr *i = bstr_dup_c("/%}9est"); + bstr *e = bstr_dup_c("/iest"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace6_UrlencodedInvalidNotEnoughBytes) { + bstr *i = bstr_dup_c("/%a"); + bstr *e = bstr_dup_c("/%a"); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace7_UrlencodedInvalidNotEnoughBytes) { + bstr *i = bstr_dup_c("/%"); + bstr *e = bstr_dup_c("/%"); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace8_Uencoded) { + bstr *i = bstr_dup_c("/%u0064"); + bstr *e = bstr_dup_c("/d"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace9_UencodedDoNotDecode) { + bstr *i = bstr_dup_c("/%u0064"); + bstr *e = bstr_dup_c("/%u0064"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 0); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace10_UencodedInvalidNotEnoughBytes) { + bstr *i = bstr_dup_c("/%u006"); + bstr *e = bstr_dup_c("/%u006"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace11_UencodedInvalidPreserve) { + bstr *i = bstr_dup_c("/%u006"); + bstr *e = bstr_dup_c("/%u006"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace12_UencodedInvalidRemove) { + bstr *i = bstr_dup_c("/%uXXXX"); + bstr *e = bstr_dup_c("/uXXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace13_UencodedInvalidDecode) { + bstr *i = bstr_dup_c("/%u00}9"); + bstr *e = bstr_dup_c("/i"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace14_UencodedInvalidPreserve) { + bstr *i = bstr_dup_c("/%u00"); + bstr *e = bstr_dup_c("/%u00"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace15_UencodedInvalidPreserve) { + bstr *i = bstr_dup_c("/%u0"); + bstr *e = bstr_dup_c("/%u0"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace16_UencodedInvalidPreserve) { + bstr *i = bstr_dup_c("/%u"); + bstr *e = bstr_dup_c("/%u"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace17_UrlencodedNul) { + bstr *i = bstr_dup_c("/%00"); + bstr *e = bstr_dup_mem("/\0", 2); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace18_UrlencodedNulTerminates) { + bstr *i = bstr_dup_c("/%00ABC"); + bstr *e = bstr_dup_c("/"); + htp_config_set_nul_encoded_terminates(cfg, HTP_DECODER_DEFAULTS, 1); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace19_RawNulTerminates) { + bstr *i = bstr_dup_mem("/\0ABC", 5); + bstr *e = bstr_dup_c("/"); + htp_config_set_nul_raw_terminates(cfg, HTP_DECODER_DEFAULTS, 1); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodeUrlencodedInplace20_UencodedBestFit) { + bstr *i = bstr_dup_c("/%u0107"); + bstr *e = bstr_dup_c("/c"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_tx_urldecode_params_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace1_UrlencodedInvalidNotEnoughBytes) { + bstr *i = bstr_dup_c("/%a"); + bstr *e = bstr_dup_c("/%a"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace2_UencodedInvalidNotEnoughBytes) { + bstr *i = bstr_dup_c("/%uX"); + bstr *e = bstr_dup_c("/%uX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace3_UencodedValid) { + bstr *i = bstr_dup_c("/%u0107"); + bstr *e = bstr_dup_c("/c"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace4_UencodedInvalidNotHexDigits_Remove) { + bstr *i = bstr_dup_c("/%uXXXX"); + bstr *e = bstr_dup_c("/uXXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace5_UencodedInvalidNotHexDigits_Preserve) { + bstr *i = bstr_dup_c("/%uXXXX"); + bstr *e = bstr_dup_c("/%uXXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace6_UencodedInvalidNotHexDigits_Process) { + bstr *i = bstr_dup_c("/%u00}9"); + bstr *e = bstr_dup_c("/i"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace7_UencodedNul) { + bstr *i = bstr_dup_c("/%u0000"); + bstr *e = bstr_dup_mem("/\0", 2); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_ENCODED_NUL); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace8_UencodedNotEnough_Remove) { + bstr *i = bstr_dup_c("/%uXXX"); + bstr *e = bstr_dup_c("/uXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace9_UencodedNotEnough_Preserve) { + bstr *i = bstr_dup_c("/%uXXX"); + bstr *e = bstr_dup_c("/%uXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace10_UrlencodedNul) { + bstr *i = bstr_dup_c("/%00123"); + bstr *e = bstr_dup_mem("/\000123", 5); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_ENCODED_NUL); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace11_UrlencodedNul_Terminates) { + bstr *i = bstr_dup_c("/%00123"); + bstr *e = bstr_dup_mem("/", 1); + htp_config_set_nul_encoded_terminates(cfg, HTP_DECODER_DEFAULTS, 1); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_ENCODED_NUL); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace12_EncodedSlash) { + bstr *i = bstr_dup_c("/one%2ftwo"); + bstr *e = bstr_dup_c("/one%2ftwo"); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_DEFAULTS, 0); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_ENCODED_SEPARATOR); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace13_EncodedSlash_Decode) { + bstr *i = bstr_dup_c("/one%2ftwo"); + bstr *e = bstr_dup_c("/one/two"); + htp_config_set_path_separators_decode(cfg, HTP_DECODER_DEFAULTS, 1); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_ENCODED_SEPARATOR); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace14_Urlencoded_Invalid_Preserve) { + bstr *i = bstr_dup_c("/%HH"); + bstr *e = bstr_dup_c("/%HH"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace15_Urlencoded_Invalid_Remove) { + bstr *i = bstr_dup_c("/%HH"); + bstr *e = bstr_dup_c("/HH"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace16_Urlencoded_Invalid_Process) { + bstr *i = bstr_dup_c("/%}9"); + bstr *e = bstr_dup_c("/i"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace17_Urlencoded_NotEnough_Remove) { + bstr *i = bstr_dup_c("/%H"); + bstr *e = bstr_dup_c("/H"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_REMOVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace18_Urlencoded_NotEnough_Preserve) { + bstr *i = bstr_dup_c("/%H"); + bstr *e = bstr_dup_c("/%H"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace19_Urlencoded_NotEnough_Process) { + bstr *i = bstr_dup_c("/%H"); + bstr *e = bstr_dup_c("/%H"); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_DEFAULTS, HTP_URL_DECODE_PROCESS_INVALID); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + ASSERT_TRUE(tx->flags & HTP_PATH_INVALID_ENCODING); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace20_RawNul1) { + bstr *i = bstr_dup_mem("/\000123", 5); + bstr *e = bstr_dup_c("/"); + htp_config_set_nul_raw_terminates(cfg, HTP_DECODER_DEFAULTS, 1); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace21_RawNul1) { + bstr *i = bstr_dup_mem("/\000123", 5); + bstr *e = bstr_dup_mem("/\000123", 5); + htp_config_set_nul_raw_terminates(cfg, HTP_DECODER_DEFAULTS, 0); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace22_ConvertBackslash1) { + bstr *i = bstr_dup_c("/one\\two"); + bstr *e = bstr_dup_c("/one/two"); + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_DEFAULTS, 1); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, DecodePathInplace23_ConvertBackslash2) { + bstr *i = bstr_dup_c("/one\\two"); + bstr *e = bstr_dup_c("/one\\two"); + htp_config_set_backslash_convert_slashes(cfg, HTP_DECODER_DEFAULTS, 0); + htp_decode_path_inplace(tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +TEST_F(DecodingTest, InvalidUtf8) { + bstr *i = bstr_dup_c("\xf1."); + bstr *e = bstr_dup_c("?."); + htp_config_set_utf8_convert_bestfit(cfg, HTP_DECODER_URL_PATH, 1); + htp_utf8_decode_path_inplace(cfg, tx, i); + ASSERT_TRUE(bstr_cmp(i, e) == 0); + bstr_free(e); + bstr_free(i); +} + +class UrlencodedParser : public testing::Test { +protected: + + virtual void SetUp() { + cfg = htp_config_create(); + connp = htp_connp_create(cfg); + htp_connp_open(connp, "127.0.0.1", 32768, "127.0.0.1", 80, NULL); + tx = htp_connp_tx_create(connp); + urlenp = htp_urlenp_create(tx); + } + + virtual void TearDown() { + htp_urlenp_destroy(urlenp); + htp_connp_destroy_all(connp); + htp_config_destroy(cfg); + } + + htp_connp_t *connp; + + htp_cfg_t *cfg; + + htp_tx_t *tx; + + htp_urlenp_t *urlenp; +}; + +TEST_F(UrlencodedParser, Empty) { + htp_urlenp_parse_complete(urlenp, "", 0); + + ASSERT_EQ(0, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, EmptyKey1) { + htp_urlenp_parse_complete(urlenp, "&", 1); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "", 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, EmptyKey2) { + htp_urlenp_parse_complete(urlenp, "=&", 2); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "", 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, EmptyKey3) { + htp_urlenp_parse_complete(urlenp, "=1&", 3); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "", 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "1")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, EmptyKeyAndValue) { + htp_urlenp_parse_complete(urlenp, "=", 1); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "", 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, OnePairEmptyValue) { + htp_urlenp_parse_complete(urlenp, "p=", 2); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, OnePair) { + htp_urlenp_parse_complete(urlenp, "p=1", 3); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "1")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, TwoPairs) { + htp_urlenp_parse_complete(urlenp, "p=1&q=2", 7); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, bstr_cmp_c(p, "1")); + + bstr *q = (bstr *) htp_table_get_mem(urlenp->params, "q", 1); + ASSERT_TRUE(q != NULL); + ASSERT_EQ(0, bstr_cmp_c(q, "2")); + + ASSERT_EQ(2, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, KeyNoValue1) { + htp_urlenp_parse_complete(urlenp, "p", 1); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, KeyNoValue2) { + htp_urlenp_parse_complete(urlenp, "p&", 2); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, KeyNoValue3) { + htp_urlenp_parse_complete(urlenp, "p&q", 3); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + bstr *q = (bstr *) htp_table_get_mem(urlenp->params, "q", 1); + ASSERT_TRUE(q != NULL); + ASSERT_EQ(0, bstr_cmp_c(q, "")); + + ASSERT_EQ(2, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, KeyNoValue4) { + htp_urlenp_parse_complete(urlenp, "p&q=2", 5); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + bstr *q = (bstr *) htp_table_get_mem(urlenp->params, "q", 1); + ASSERT_TRUE(q != NULL); + ASSERT_EQ(0, bstr_cmp_c(q, "2")); + + ASSERT_EQ(2, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial1) { + htp_urlenp_parse_partial(urlenp, "p", 1); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial2) { + htp_urlenp_parse_partial(urlenp, "p", 1); + htp_urlenp_parse_partial(urlenp, "x", 1); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "px", 2); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial3) { + htp_urlenp_parse_partial(urlenp, "p", 1); + htp_urlenp_parse_partial(urlenp, "x&", 2); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "px", 2); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial4) { + htp_urlenp_parse_partial(urlenp, "p", 1); + htp_urlenp_parse_partial(urlenp, "=", 1); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial5) { + htp_urlenp_parse_partial(urlenp, "p", 1); + htp_urlenp_parse_partial(urlenp, "", 0); + htp_urlenp_parse_partial(urlenp, "", 0); + htp_urlenp_parse_partial(urlenp, "", 0); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "p", 1); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "")); + + ASSERT_EQ(1, htp_table_size(urlenp->params)); +} + +TEST_F(UrlencodedParser, Partial6) { + htp_urlenp_parse_partial(urlenp, "px", 2); + htp_urlenp_parse_partial(urlenp, "n", 1); + htp_urlenp_parse_partial(urlenp, "", 0); + htp_urlenp_parse_partial(urlenp, "=", 1); + htp_urlenp_parse_partial(urlenp, "1", 1); + htp_urlenp_parse_partial(urlenp, "2", 1); + htp_urlenp_parse_partial(urlenp, "&", 1); + htp_urlenp_parse_partial(urlenp, "qz", 2); + htp_urlenp_parse_partial(urlenp, "n", 1); + htp_urlenp_parse_partial(urlenp, "", 0); + htp_urlenp_parse_partial(urlenp, "=", 1); + htp_urlenp_parse_partial(urlenp, "2", 1); + htp_urlenp_parse_partial(urlenp, "3", 1); + htp_urlenp_parse_partial(urlenp, "&", 1); + htp_urlenp_finalize(urlenp); + + bstr *p = (bstr *) htp_table_get_mem(urlenp->params, "pxn", 3); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(p, "12")); + + bstr *q = (bstr *) htp_table_get_mem(urlenp->params, "qzn", 3); + ASSERT_TRUE(p != NULL); + + ASSERT_EQ(0, bstr_cmp_c(q, "23")); + + ASSERT_EQ(2, htp_table_size(urlenp->params)); +} + +TEST(List, Misc) { + htp_list_t *l = htp_list_create(16); + + htp_list_push(l, (void *) "1"); + htp_list_push(l, (void *) "2"); + htp_list_push(l, (void *) "3"); + + ASSERT_EQ(3, htp_list_size(l)); + + char *p = (char *) htp_list_pop(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("3", p)); + + ASSERT_EQ(2, htp_list_size(l)); + + p = (char *) htp_list_shift(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("1", p)); + + ASSERT_EQ(1, htp_list_size(l)); + + p = (char *) htp_list_shift(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("2", p)); + + p = (char *) htp_list_shift(l); + ASSERT_TRUE(p == NULL); + + p = (char *) htp_list_pop(l); + ASSERT_TRUE(p == NULL); + + htp_list_destroy(l); +} + +TEST(List, Misc2) { + htp_list_t *l = htp_list_create(1); + + htp_list_push(l, (void *) "1"); + + char *p = (char *) htp_list_shift(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("1", p)); + + htp_list_push(l, (void *) "2"); + + p = (char *) htp_list_shift(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("2", p)); + + ASSERT_EQ(0, htp_list_size(l)); + + htp_list_destroy(l); +} + +TEST(List, Misc3) { + htp_list_t *l = htp_list_create(2); + + htp_list_push(l, (void *) "1"); + htp_list_push(l, (void *) "2"); + + char *p = (char *) htp_list_shift(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("1", p)); + + htp_list_push(l, (void *) "3"); + + p = (char *) htp_list_get(l, 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("3", p)); + + ASSERT_EQ(2, htp_list_size(l)); + + htp_list_replace(l, 1, (void *) "4"); + + p = (char *) htp_list_pop(l); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("4", p)); + + htp_list_destroy(l); +} + +TEST(List, Expand1) { + htp_list_t *l = htp_list_create(2); + + htp_list_push(l, (void *) "1"); + htp_list_push(l, (void *) "2"); + + ASSERT_EQ(2, htp_list_size(l)); + + htp_list_push(l, (void *) "3"); + + ASSERT_EQ(3, htp_list_size(l)); + + char *p = (char *) htp_list_get(l, 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("1", p)); + + p = (char *) htp_list_get(l, 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("2", p)); + + p = (char *) htp_list_get(l, 2); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("3", p)); + + htp_list_destroy(l); +} + +TEST(List, Expand2) { + htp_list_t *l = htp_list_create(2); + + htp_list_push(l, (void *) "1"); + htp_list_push(l, (void *) "2"); + + ASSERT_EQ(2, htp_list_size(l)); + + htp_list_shift(l); + + ASSERT_EQ(1, htp_list_size(l)); + + htp_list_push(l, (void *) "3"); + htp_list_push(l, (void *) "4"); + + ASSERT_EQ(3, htp_list_size(l)); + + char *p = (char *) htp_list_get(l, 0); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("2", p)); + + p = (char *) htp_list_get(l, 1); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("3", p)); + + p = (char *) htp_list_get(l, 2); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("4", p)); + + htp_list_destroy(l); +} + +TEST(Table, Misc) { + htp_table_t *t = htp_table_create(2); + + bstr *pkey = bstr_dup_c("p"); + bstr *qkey = bstr_dup_c("q"); + + htp_table_addk(t, pkey, "1"); + htp_table_addk(t, qkey, "2"); + + char *p = (char *) htp_table_get_mem(t, "z", 1); + ASSERT_TRUE(p == NULL); + + p = (char *) htp_table_get(t, pkey); + ASSERT_TRUE(p != NULL); + ASSERT_EQ(0, strcmp("1", p)); + + htp_table_clear_ex(t); + + bstr_free(qkey); + bstr_free(pkey); + + htp_table_destroy(t); +} + +TEST(Util, ExtractQuotedString) { + bstr *s; + size_t end_offset; + + htp_status_t rc = htp_extract_quoted_string_as_bstr((unsigned char *) "\"test\"", 6, &s, &end_offset); + ASSERT_EQ(HTP_OK, rc); + ASSERT_TRUE(s != NULL); + ASSERT_EQ(0, bstr_cmp_c(s, "test")); + ASSERT_EQ(5, end_offset); + bstr_free(s); + + rc = htp_extract_quoted_string_as_bstr((unsigned char *) "\"te\\\"st\"", 8, &s, &end_offset); + ASSERT_EQ(HTP_OK, rc); + ASSERT_TRUE(s != NULL); + ASSERT_EQ(0, bstr_cmp_c(s, "te\"st")); + ASSERT_EQ(7, end_offset); + bstr_free(s); +} + +TEST(Util, NormalizeUriPath) { + bstr *s = NULL; + + s = bstr_dup_c("/a/b/c/./../../g"); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "/a/g")); + bstr_free(s); + + s = bstr_dup_c("mid/content=5/../6"); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "mid/6")); + bstr_free(s); + + s = bstr_dup_c("./one"); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "one")); + bstr_free(s); + + s = bstr_dup_c("../one"); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "one")); + bstr_free(s); + + s = bstr_dup_c("."); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "")); + bstr_free(s); + + s = bstr_dup_c(".."); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "")); + bstr_free(s); + + s = bstr_dup_c("one/."); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "one")); + bstr_free(s); + + s = bstr_dup_c("one/.."); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "")); + bstr_free(s); + + s = bstr_dup_c("one/../"); + htp_normalize_uri_path_inplace(s); + ASSERT_EQ(0, bstr_cmp_c(s, "")); + bstr_free(s); +} + +TEST_F(UrlencodedParser, UrlDecode1) { + bstr *s = NULL; + uint64_t flags; + + s = bstr_dup_c("/one/tw%u006f/three/%u123"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URLENCODED, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URLENCODED, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_urldecode_inplace(cfg, HTP_DECODER_URLENCODED, s, &flags); + ASSERT_EQ(0, bstr_cmp_c(s, "/one/two/three/%u123")); + bstr_free(s); + + s = bstr_dup_c("/one/tw%u006f/three/%uXXXX"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URLENCODED, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URLENCODED, HTP_URL_DECODE_PRESERVE_PERCENT); + htp_urldecode_inplace(cfg, HTP_DECODER_URLENCODED, s, &flags); + ASSERT_EQ(0, bstr_cmp_c(s, "/one/two/three/%uXXXX")); + bstr_free(s); + + s = bstr_dup_c("/one/tw%u006f/three/%u123"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URLENCODED, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URLENCODED, HTP_URL_DECODE_REMOVE_PERCENT); + htp_urldecode_inplace(cfg, HTP_DECODER_URLENCODED, s, &flags); + ASSERT_EQ(0, bstr_cmp_c(s, "/one/two/three/u123")); + bstr_free(s); + + s = bstr_dup_c("/one/tw%u006f/three/%3"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URLENCODED, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URLENCODED, HTP_URL_DECODE_REMOVE_PERCENT); + htp_urldecode_inplace(cfg, HTP_DECODER_URLENCODED, s, &flags); + ASSERT_EQ(0, bstr_cmp_c(s, "/one/two/three/3")); + bstr_free(s); + + s = bstr_dup_c("/one/tw%u006f/three/%3"); + htp_config_set_u_encoding_decode(cfg, HTP_DECODER_URLENCODED, 1); + htp_config_set_url_encoding_invalid_handling(cfg, HTP_DECODER_URLENCODED, HTP_URL_DECODE_PROCESS_INVALID); + htp_urldecode_inplace(cfg, HTP_DECODER_URLENCODED, s, &flags); + ASSERT_EQ(0, bstr_cmp_c(s, "/one/two/three/%3")); + bstr_free(s); +} + +TEST(UtilTest, HeaderHasToken) { + char data[100]; + + // Basic + strcpy(data, "chunked"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + + // Negative + strcpy(data, "notchunked"); + EXPECT_EQ(HTP_ERROR, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunkednot"); + EXPECT_EQ(HTP_ERROR, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunk,ed"); + EXPECT_EQ(HTP_ERROR, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunk ed"); + EXPECT_EQ(HTP_ERROR, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + + // Positive + strcpy(data, " notchunked , chunked , yetanother"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunked,yetanother"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "not,chunked"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunk,chunked"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, " chunked"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunked "); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, "chunked,"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); + strcpy(data, ",chunked"); + EXPECT_EQ(HTP_OK, htp_header_has_token((unsigned char*) data, strlen(data), (unsigned char *)"chunked")); +} diff --git a/test/valgrind b/test/valgrind new file mode 100755 index 0000000..7d4373d --- /dev/null +++ b/test/valgrind @@ -0,0 +1,9 @@ +#!/bin/sh +# --gen-suppressions=all \ +exec valgrind \ +--suppressions=valgrind.supp \ +--leak-check=full \ +--show-reachable=yes \ +--dsymutil=yes \ +"$@" + diff --git a/test/valgrind.supp b/test/valgrind.supp new file mode 100644 index 0000000..7015d65 --- /dev/null +++ b/test/valgrind.supp @@ -0,0 +1,155 @@ +{ + gtest_1 + Memcheck:Leak + fun:malloc + fun:__smakebuf + fun:__swsetup + fun:__sfvwrite + fun:fwrite + fun:_ZSt15__ostream_writeIcSt11char_traitsIcEEvRSt13basic_ostreamIT_T0_EPKS3_l + fun:_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l + fun:main +} +{ + gtest_2 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_3 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_4 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_5 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_6 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_7 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_Znam + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv +} +{ + gtest_8 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv + fun:main +} +{ + gtest_9 + Memcheck:Leak + fun:malloc + fun:_Znwm + fun:_ZN7testing4TestC2Ev + fun:_ZN7testing8internal15TestFactoryImplI26ConnectionParsing_Get_TestE10CreateTestEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_15TestFactoryBaseEPNS_4TestEEET0_PT_MS6_FS5_vEPKc + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv + fun:_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing8UnitTest3RunEv + fun:main +} +{ + fprintf + Memcheck:Leak + fun:malloc + fun:__smakebuf + fun:__swsetup + fun:__vfprintf + fun:vfprintf_l + fun:fprintf + fun:fprint_raw_data_ex + fun:_ZN27ConnectionParsing_Util_Test8TestBodyEv + fun:_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc + fun:_ZN7testing4Test3RunEv + fun:_ZN7testing8TestInfo3RunEv + fun:_ZN7testing8TestCase3RunEv +} |