summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.github/workflows/builds.yml90
-rw-r--r--.github/workflows/cifuzz.yml26
-rw-r--r--.gitignore67
-rw-r--r--AUTHORS6
-rw-r--r--COPYING1
-rw-r--r--ChangeLog473
-rw-r--r--LICENSE30
-rw-r--r--Makefile.am37
-rw-r--r--NEWS0
-rw-r--r--NOTICE0
-rw-r--r--README70
-rw-r--r--VERSION2
-rw-r--r--appveyor.yml18
-rwxr-xr-xautogen.sh7
-rw-r--r--config.rpath0
-rw-r--r--configure.ac275
-rw-r--r--docs/COMPATIBILITY_CHANGES79
-rw-r--r--docs/Makefile.am18
-rw-r--r--docs/QUICK_START106
-rw-r--r--docs/doxygen.conf1356
-rw-r--r--examples/mod_libhtp/mod_libhtp.c161
-rw-r--r--extras/htptest.c569
-rw-r--r--extras/ruby/HTP.c1008
-rw-r--r--extras/ruby/README58
-rw-r--r--extras/ruby/example.rb116
-rw-r--r--extras/ruby/extconf.rb6
-rw-r--r--extras/ruby/htp.gemspec12
-rw-r--r--extras/ruby/htp_ruby.rb247
-rwxr-xr-xget-version.sh32
-rw-r--r--htp.pc.in12
-rw-r--r--htp/Makefile.am35
-rw-r--r--htp/bstr.c638
-rw-r--r--htp/bstr.h678
-rw-r--r--htp/bstr_builder.c121
-rw-r--r--htp/bstr_builder.h136
-rw-r--r--htp/htp.h678
-rw-r--r--htp/htp_base64.c196
-rw-r--r--htp/htp_base64.h74
-rw-r--r--htp/htp_config.c954
-rw-r--r--htp/htp_config.h719
-rw-r--r--htp/htp_config_auto.h51
-rw-r--r--htp/htp_config_private.h373
-rw-r--r--htp/htp_connection.c168
-rw-r--r--htp/htp_connection_parser.c260
-rw-r--r--htp/htp_connection_parser.h218
-rw-r--r--htp/htp_connection_parser_private.h275
-rw-r--r--htp/htp_connection_private.h121
-rw-r--r--htp/htp_content_handlers.c299
-rw-r--r--htp/htp_cookies.c119
-rw-r--r--htp/htp_core.h353
-rw-r--r--htp/htp_decompressors.c490
-rw-r--r--htp/htp_decompressors.h94
-rw-r--r--htp/htp_hooks.c160
-rw-r--r--htp/htp_hooks.h122
-rw-r--r--htp/htp_list.c360
-rw-r--r--htp/htp_list.h227
-rw-r--r--htp/htp_list_private.h73
-rw-r--r--htp/htp_multipart.c1615
-rw-r--r--htp/htp_multipart.h345
-rw-r--r--htp/htp_multipart_private.h203
-rw-r--r--htp/htp_parsers.c214
-rw-r--r--htp/htp_php.c116
-rw-r--r--htp/htp_private.h269
-rw-r--r--htp/htp_request.c1173
-rw-r--r--htp/htp_request_apache_2_2.c64
-rw-r--r--htp/htp_request_generic.c462
-rw-r--r--htp/htp_request_parsers.c149
-rw-r--r--htp/htp_response.c1436
-rw-r--r--htp/htp_response_generic.c334
-rw-r--r--htp/htp_table.c250
-rw-r--r--htp/htp_table.h184
-rw-r--r--htp/htp_table_private.h78
-rw-r--r--htp/htp_transaction.c1558
-rw-r--r--htp/htp_transaction.h529
-rw-r--r--htp/htp_transcoder.c211
-rw-r--r--htp/htp_urlencoded.c332
-rw-r--r--htp/htp_urlencoded.h111
-rw-r--r--htp/htp_utf8_decoder.c118
-rw-r--r--htp/htp_utf8_decoder.h85
-rw-r--r--htp/htp_util.c2602
-rw-r--r--htp/htp_version.h.in53
-rw-r--r--htp/lzma/7zTypes.h375
-rw-r--r--htp/lzma/Compiler.h33
-rw-r--r--htp/lzma/LzFind.c1127
-rw-r--r--htp/lzma/LzFind.h121
-rw-r--r--htp/lzma/LzHash.h57
-rw-r--r--htp/lzma/LzmaDec.c1223
-rw-r--r--htp/lzma/LzmaDec.h234
-rw-r--r--htp/lzma/Makefile.am16
-rw-r--r--htp/lzma/Precomp.h10
-rw-r--r--htp/strlcat.c76
-rw-r--r--htp/strlcpy.c72
-rw-r--r--m4/iconv.m4214
-rw-r--r--m4/lib-ld.m4110
-rw-r--r--m4/lib-link.m4774
-rw-r--r--m4/lib-prefix.m4224
-rw-r--r--test/Makefile.am36
-rw-r--r--test/files/00-adhoc.t14
-rw-r--r--test/files/01-get.t14
-rw-r--r--test/files/02-header-test-apache2.tbin0 -> 394 bytes
-rw-r--r--test/files/03-post-urlencoded.t34
-rw-r--r--test/files/04-post-urlencoded-chunked.t26
-rw-r--r--test/files/05-expect.t39
-rw-r--r--test/files/06-uri-normal.t9
-rw-r--r--test/files/07-pipelined-connection.t15
-rw-r--r--test/files/08-not-pipelined-connection.t18
-rw-r--r--test/files/09-multi-packet-request-head.t14
-rw-r--r--test/files/10-host-in-headers.t34
-rw-r--r--test/files/100-auth-bearer.t5
-rw-r--r--test/files/100-response-body-data.t6
-rw-r--r--test/files/11-response-stream-closure.t13
-rw-r--r--test/files/12-connect-request.t21
-rw-r--r--test/files/13-compressed-response-gzip-ct.tbin0 -> 1462 bytes
-rw-r--r--test/files/14-compressed-response-gzip-chunked.tbin0 -> 29051 bytes
-rw-r--r--test/files/15-connect-complete.tbin0 -> 8601 bytes
-rw-r--r--test/files/16-connect-extra.t32
-rw-r--r--test/files/17-multipart-1.t41
-rw-r--r--test/files/18-compressed-response-deflate.tbin0 -> 1721 bytes
-rw-r--r--test/files/19-urlencoded-test.t15
-rw-r--r--test/files/20-ambiguous-host.t58
-rw-r--r--test/files/21-http09.t11
-rw-r--r--test/files/22-http_1_1-host_missing14
-rw-r--r--test/files/22-php-param-processing.t14
-rw-r--r--test/files/23-http09-multiple.t12
-rw-r--r--test/files/24-http09-explicit.t13
-rw-r--r--test/files/25-small-chunks.t41
-rw-r--r--test/files/26-request-headers-raw.t41
-rw-r--r--test/files/27-request-trailer-raw.t26
-rw-r--r--test/files/28-response-headers-raw.t33
-rw-r--r--test/files/29-response-trailer-raw.t33
-rw-r--r--test/files/30-get-ipv6.t15
-rw-r--r--test/files/31-get-request-line-nul.tbin0 -> 209 bytes
-rw-r--r--test/files/32-invalid-hostname.t15
-rw-r--r--test/files/33-invalid-hostname.t15
-rw-r--r--test/files/34-invalid-hostname.t15
-rw-r--r--test/files/35-early-response.t18
-rw-r--r--test/files/36-invalid-request-1-invalid-c-l.t17
-rw-r--r--test/files/37-invalid-request-2-t-e-and-c-l.t28
-rw-r--r--test/files/38-invalid-request-3-invalid-t-e.t27
-rw-r--r--test/files/39-auto-destroy-crash.t34
-rw-r--r--test/files/40-auth-basic.t5
-rw-r--r--test/files/41-auth-digest.t8
-rw-r--r--test/files/42-unknown-method_only.t3
-rw-r--r--test/files/43-invalid-protocol.t3
-rw-r--r--test/files/44-auth-basic-invalid.t5
-rw-r--r--test/files/45-auth-digest-unquoted-username.t8
-rw-r--r--test/files/46-auth-digest-invalid-username.t8
-rw-r--r--test/files/47-auth-unrecognized.t5
-rw-r--r--test/files/48-invalid-response-headers-1.t17
-rw-r--r--test/files/49-invalid-response-headers-2.t15
-rw-r--r--test/files/50-util.t14
-rw-r--r--test/files/51-get-ipv6-invalid.t15
-rw-r--r--test/files/52-invalid-path.t15
-rw-r--r--test/files/53-path-utf8-none.t15
-rw-r--r--test/files/54-path-utf8-valid.t15
-rw-r--r--test/files/55-path-utf8-overlong-2.t15
-rw-r--r--test/files/56-path-utf8-overlong-3.t15
-rw-r--r--test/files/57-path-utf8-overlong-4.t15
-rw-r--r--test/files/58-path-utf8-invalid.t15
-rw-r--r--test/files/59-path-utf8-fullwidth.t15
-rw-r--r--test/files/60-request-cookies.t16
-rw-r--r--test/files/61-empty-line-between-requests.t19
-rw-r--r--test/files/62-post-no-body.t34
-rw-r--r--test/files/63-post-chunked-invalid-1.t26
-rw-r--r--test/files/64-post-chunked-invalid-2.t26
-rw-r--r--test/files/65-post-chunked-invalid-3.t26
-rw-r--r--test/files/66-post-chunked-split-chunk.t28
-rw-r--r--test/files/67-long-request-line.t16
-rw-r--r--test/files/68-invalid-request-header.tbin0 -> 267 bytes
-rw-r--r--test/files/69-long-response-header.t16
-rw-r--r--test/files/70-response-invalid-chunk-length.t18
-rw-r--r--test/files/71-response-split-chunk.t20
-rw-r--r--test/files/72-response-split-body.t16
-rw-r--r--test/files/73-response-te-and-cl.t19
-rw-r--r--test/files/74-response-multiple-cl.t14
-rw-r--r--test/files/75-response-invalid-cl.t13
-rw-r--r--test/files/76-response-no-body.t34
-rw-r--r--test/files/77-response-folded-headers.t35
-rw-r--r--test/files/78-response-no-status-headers.t8
-rw-r--r--test/files/79-connect-invalid-hostport.t32
-rw-r--r--test/files/80-hostname-invalid-1.t15
-rw-r--r--test/files/81-hostname-invalid-2.t15
-rw-r--r--test/files/82-put.t16
-rw-r--r--test/files/83-auth-digest-invalid-username-2.t5
-rw-r--r--test/files/84-response-no-status-headers-2.t7
-rw-r--r--test/files/85-zero-byte-request-timeout.t16
-rw-r--r--test/files/86-partial-request-timeout.t18
-rw-r--r--test/files/87-issue-55-incorrect-host-ambiguous-warning.t8
-rw-r--r--test/files/88-response-multiple-cl-mismatch.t14
-rw-r--r--test/files/89-get-whitespace.t14
-rw-r--r--test/files/90-request-uri-too-large.t17
-rw-r--r--test/files/91-request-unexpected-body.t16
-rw-r--r--test/files/92-http_0_9-method_only.t3
-rw-r--r--test/files/93-compressed-response-deflateasgzip.tbin0 -> 1718 bytes
-rw-r--r--test/files/94-compressed-response-multiple.tbin0 -> 311 bytes
-rw-r--r--test/files/95-compressed-response-gzipasdeflate.tbin0 -> 1465 bytes
-rw-r--r--test/files/96-compressed-response-lzma.tbin0 -> 336 bytes
-rw-r--r--test/files/97-requests-cut.t9
-rw-r--r--test/files/98-responses-cut.t26
-rw-r--r--test/files/99-expect-100.t27
-rw-r--r--test/files/anchor.empty0
-rwxr-xr-xtest/files/generate-gzip-tests.php322
-rw-r--r--test/files/gztest-01-minimal.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-02-fname.gzbin0 -> 66 bytes
-rw-r--r--test/files/gztest-03-fcomment.gzbin0 -> 65 bytes
-rw-r--r--test/files/gztest-04-fhcrc.gzbin0 -> 59 bytes
-rw-r--r--test/files/gztest-05-fextra.gzbin0 -> 64 bytes
-rw-r--r--test/files/gztest-06-ftext.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-07-freserved1.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-08-freserved2.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-09-freserved3.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-10-multipart.gzbin0 -> 140 bytes
-rw-r--r--test/files/gztest-11-invalid-method.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-12-invalid-crc32.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-13-invalid-isize.gzbin0 -> 79 bytes
-rw-r--r--test/files/gztest-14-invalid-xfl.gzbin0 -> 57 bytes
-rw-r--r--test/files/gztest-15-invalid-fhcrc.gzbin0 -> 59 bytes
-rw-r--r--test/fuzz/fuzz_diff.c440
-rw-r--r--test/fuzz/fuzz_htp.c257
-rw-r--r--test/fuzz/fuzz_htp.h26
-rw-r--r--test/fuzz/onefile.c52
-rw-r--r--test/gtest/gtest-all.cc9118
-rw-r--r--test/gtest/gtest.h19537
-rw-r--r--test/gtest/gtest_main.cc39
-rw-r--r--test/main.c802
-rw-r--r--test/pcaptohtp.py17
-rw-r--r--test/test-tcpick.c351
-rw-r--r--test/test.c444
-rw-r--r--test/test.h83
-rw-r--r--test/test_bench.cpp78
-rw-r--r--test/test_bstr.cpp598
-rw-r--r--test/test_gunzip.cpp270
-rw-r--r--test/test_hybrid.cpp873
-rw-r--r--test/test_main.cpp2148
-rw-r--r--test/test_multipart.cpp1940
-rw-r--r--test/test_utils.cpp1839
-rwxr-xr-xtest/valgrind9
-rw-r--r--test/valgrind.supp155
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
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8b456c2
--- /dev/null
+++ b/AUTHORS
@@ -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>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..3585e74
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fc6ef81
--- /dev/null
+++ b/LICENSE
@@ -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 '{}' ';'
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NOTICE
diff --git a/README b/README
new file mode 100644
index 0000000..90bce81
--- /dev/null
+++ b/README
@@ -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.
+
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..a42c9e0
--- /dev/null
+++ b/VERSION
@@ -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, &timestamp_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(&copy, 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
new file mode 100644
index 0000000..a7cb787
--- /dev/null
+++ b/test/files/02-header-test-apache2.t
Binary files differ
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
new file mode 100644
index 0000000..d5a2e31
--- /dev/null
+++ b/test/files/13-compressed-response-gzip-ct.t
Binary files differ
diff --git a/test/files/14-compressed-response-gzip-chunked.t b/test/files/14-compressed-response-gzip-chunked.t
new file mode 100644
index 0000000..bae8a2d
--- /dev/null
+++ b/test/files/14-compressed-response-gzip-chunked.t
Binary files differ
diff --git a/test/files/15-connect-complete.t b/test/files/15-connect-complete.t
new file mode 100644
index 0000000..071d064
--- /dev/null
+++ b/test/files/15-connect-complete.t
Binary files differ
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
new file mode 100644
index 0000000..b70940e
--- /dev/null
+++ b/test/files/18-compressed-response-deflate.t
Binary files differ
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
new file mode 100644
index 0000000..3de2eb4
--- /dev/null
+++ b/test/files/31-get-request-line-nul.t
Binary files differ
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
new file mode 100644
index 0000000..4e6d688
--- /dev/null
+++ b/test/files/68-invalid-request-header.t
Binary files differ
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&#32;&#35;2&#46;9efcd4d9&#46;1380708056&#46;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&#32;&#35;2&#46;9efcd4d9&#46;1380708056&#46;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
new file mode 100644
index 0000000..e6c2eb5
--- /dev/null
+++ b/test/files/93-compressed-response-deflateasgzip.t
Binary files differ
diff --git a/test/files/94-compressed-response-multiple.t b/test/files/94-compressed-response-multiple.t
new file mode 100644
index 0000000..4d0fdf7
--- /dev/null
+++ b/test/files/94-compressed-response-multiple.t
Binary files differ
diff --git a/test/files/95-compressed-response-gzipasdeflate.t b/test/files/95-compressed-response-gzipasdeflate.t
new file mode 100644
index 0000000..8076f83
--- /dev/null
+++ b/test/files/95-compressed-response-gzipasdeflate.t
Binary files differ
diff --git a/test/files/96-compressed-response-lzma.t b/test/files/96-compressed-response-lzma.t
new file mode 100644
index 0000000..a5ea306
--- /dev/null
+++ b/test/files/96-compressed-response-lzma.t
Binary files differ
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
new file mode 100644
index 0000000..e82fcde
--- /dev/null
+++ b/test/files/gztest-01-minimal.gz
Binary files differ
diff --git a/test/files/gztest-02-fname.gz b/test/files/gztest-02-fname.gz
new file mode 100644
index 0000000..bb38b70
--- /dev/null
+++ b/test/files/gztest-02-fname.gz
Binary files differ
diff --git a/test/files/gztest-03-fcomment.gz b/test/files/gztest-03-fcomment.gz
new file mode 100644
index 0000000..fe55135
--- /dev/null
+++ b/test/files/gztest-03-fcomment.gz
Binary files differ
diff --git a/test/files/gztest-04-fhcrc.gz b/test/files/gztest-04-fhcrc.gz
new file mode 100644
index 0000000..cd0ce6b
--- /dev/null
+++ b/test/files/gztest-04-fhcrc.gz
Binary files differ
diff --git a/test/files/gztest-05-fextra.gz b/test/files/gztest-05-fextra.gz
new file mode 100644
index 0000000..72290b0
--- /dev/null
+++ b/test/files/gztest-05-fextra.gz
Binary files differ
diff --git a/test/files/gztest-06-ftext.gz b/test/files/gztest-06-ftext.gz
new file mode 100644
index 0000000..9d9aecc
--- /dev/null
+++ b/test/files/gztest-06-ftext.gz
Binary files differ
diff --git a/test/files/gztest-07-freserved1.gz b/test/files/gztest-07-freserved1.gz
new file mode 100644
index 0000000..bd365b5
--- /dev/null
+++ b/test/files/gztest-07-freserved1.gz
Binary files differ
diff --git a/test/files/gztest-08-freserved2.gz b/test/files/gztest-08-freserved2.gz
new file mode 100644
index 0000000..e240ec1
--- /dev/null
+++ b/test/files/gztest-08-freserved2.gz
Binary files differ
diff --git a/test/files/gztest-09-freserved3.gz b/test/files/gztest-09-freserved3.gz
new file mode 100644
index 0000000..4071cdc
--- /dev/null
+++ b/test/files/gztest-09-freserved3.gz
Binary files differ
diff --git a/test/files/gztest-10-multipart.gz b/test/files/gztest-10-multipart.gz
new file mode 100644
index 0000000..a2c0cd5
--- /dev/null
+++ b/test/files/gztest-10-multipart.gz
Binary files differ
diff --git a/test/files/gztest-11-invalid-method.gz b/test/files/gztest-11-invalid-method.gz
new file mode 100644
index 0000000..9c13768
--- /dev/null
+++ b/test/files/gztest-11-invalid-method.gz
Binary files differ
diff --git a/test/files/gztest-12-invalid-crc32.gz b/test/files/gztest-12-invalid-crc32.gz
new file mode 100644
index 0000000..1832ef8
--- /dev/null
+++ b/test/files/gztest-12-invalid-crc32.gz
Binary files differ
diff --git a/test/files/gztest-13-invalid-isize.gz b/test/files/gztest-13-invalid-isize.gz
new file mode 100644
index 0000000..55263bc
--- /dev/null
+++ b/test/files/gztest-13-invalid-isize.gz
Binary files differ
diff --git a/test/files/gztest-14-invalid-xfl.gz b/test/files/gztest-14-invalid-xfl.gz
new file mode 100644
index 0000000..a844957
--- /dev/null
+++ b/test/files/gztest-14-invalid-xfl.gz
Binary files differ
diff --git a/test/files/gztest-15-invalid-fhcrc.gz b/test/files/gztest-15-invalid-fhcrc.gz
new file mode 100644
index 0000000..b6fa5dd
--- /dev/null
+++ b/test/files/gztest-15-invalid-fhcrc.gz
Binary files differ
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(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_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(\
+ &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ALL_THREADS, &gtest_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(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter:: \
+ INTERCEPT_ONLY_CURRENT_THREAD, &gtest_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(\
+ &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+ (substr));\
+ {\
+ ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+ ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS,\
+ &gtest_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 << "&lt;";
+ break;
+ case '>':
+ m << "&gt;";
+ break;
+ case '&':
+ m << "&amp;";
+ break;
+ case '\'':
+ if (is_attribute)
+ m << "&apos;";
+ else
+ m << '\'';
+ break;
+ case '"':
+ if (is_attribute)
+ m << "&quot;";
+ 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 << "]]>]]&gt;<![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,
+ &GTEST_FLAG(also_run_disabled_tests)) ||
+ ParseBoolFlag(arg, kBreakOnFailureFlag,
+ &GTEST_FLAG(break_on_failure)) ||
+ ParseBoolFlag(arg, kCatchExceptionsFlag,
+ &GTEST_FLAG(catch_exceptions)) ||
+ ParseStringFlag(arg, kColorFlag, &GTEST_FLAG(color)) ||
+ ParseStringFlag(arg, kDeathTestStyleFlag,
+ &GTEST_FLAG(death_test_style)) ||
+ ParseBoolFlag(arg, kDeathTestUseFork,
+ &GTEST_FLAG(death_test_use_fork)) ||
+ ParseStringFlag(arg, kFilterFlag, &GTEST_FLAG(filter)) ||
+ ParseStringFlag(arg, kInternalRunDeathTestFlag,
+ &GTEST_FLAG(internal_run_death_test)) ||
+ ParseBoolFlag(arg, kListTestsFlag, &GTEST_FLAG(list_tests)) ||
+ ParseStringFlag(arg, kOutputFlag, &GTEST_FLAG(output)) ||
+ ParseBoolFlag(arg, kPrintTimeFlag, &GTEST_FLAG(print_time)) ||
+ ParseInt32Flag(arg, kRandomSeedFlag, &GTEST_FLAG(random_seed)) ||
+ ParseInt32Flag(arg, kRepeatFlag, &GTEST_FLAG(repeat)) ||
+ ParseBoolFlag(arg, kShuffleFlag, &GTEST_FLAG(shuffle)) ||
+ ParseInt32Flag(arg, kStackTraceDepthFlag,
+ &GTEST_FLAG(stack_trace_depth)) ||
+ ParseStringFlag(arg, kStreamResultToFlag,
+ &GTEST_FLAG(stream_result_to)) ||
+ ParseBoolFlag(arg, kThrowOnFailureFlag,
+ &GTEST_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, &gtest_regex, \
+ __FILE__, __LINE__, &gtest_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(&parameter_);
+ 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 &current_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 &current_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 &current_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 &current_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 &current_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 &current_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 &current_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 &current_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 &current_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, \
+ &gtest_##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
+}