summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore13
-rw-r--r--.travis.yml3
-rw-r--r--ACKNOWLEDGEMENTS13
-rw-r--r--REQUIREMENTS14
-rw-r--r--configure.ac28
-rw-r--r--m4/libcurl.m4272
-rw-r--r--plugins/Makefile.am9
-rw-r--r--plugins/check_curl.c2256
-rw-r--r--plugins/picohttpparser/Makefile.am3
-rw-r--r--plugins/picohttpparser/picohttpparser.c620
-rw-r--r--plugins/picohttpparser/picohttpparser.h89
-rw-r--r--plugins/sslutils.c33
-rw-r--r--plugins/t/check_curl.t204
-rw-r--r--plugins/t/check_http.t68
-rwxr-xr-xplugins/tests/check_curl.t455
-rwxr-xr-xplugins/tests/check_http.t7
-rw-r--r--plugins/uriparser/Makefile.am11
-rw-r--r--plugins/uriparser/Uri.h777
-rw-r--r--plugins/uriparser/UriBase.h197
-rw-r--r--plugins/uriparser/UriCommon.c567
-rw-r--r--plugins/uriparser/UriCommon.h104
-rw-r--r--plugins/uriparser/UriCompare.c168
-rw-r--r--plugins/uriparser/UriDefsAnsi.h82
-rw-r--r--plugins/uriparser/UriDefsConfig.h101
-rw-r--r--plugins/uriparser/UriDefsUnicode.h82
-rw-r--r--plugins/uriparser/UriEscape.c453
-rw-r--r--plugins/uriparser/UriFile.c226
-rw-r--r--plugins/uriparser/UriIp4.c329
-rw-r--r--plugins/uriparser/UriIp4.h92
-rw-r--r--plugins/uriparser/UriIp4Base.c96
-rw-r--r--plugins/uriparser/UriIp4Base.h59
-rw-r--r--plugins/uriparser/UriNormalize.c728
-rw-r--r--plugins/uriparser/UriNormalizeBase.c119
-rw-r--r--plugins/uriparser/UriNormalizeBase.h53
-rw-r--r--plugins/uriparser/UriParse.c2241
-rw-r--r--plugins/uriparser/UriParseBase.c90
-rw-r--r--plugins/uriparser/UriParseBase.h55
-rw-r--r--plugins/uriparser/UriQuery.c460
-rw-r--r--plugins/uriparser/UriRecompose.c577
-rw-r--r--plugins/uriparser/UriResolve.c316
-rw-r--r--plugins/uriparser/UriShorten.c320
-rw-r--r--po/de.po6
42 files changed, 12343 insertions, 53 deletions
diff --git a/.gitignore b/.gitignore
index 3093c6ea..1c8cbdec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,6 +142,7 @@ NP-VERSION-FILE
142/plugins/check_by_ssh 142/plugins/check_by_ssh
143/plugins/check_clamd 143/plugins/check_clamd
144/plugins/check_cluster 144/plugins/check_cluster
145/plugins/check_curl
145/plugins/check_dbi 146/plugins/check_dbi
146/plugins/check_dig 147/plugins/check_dig
147/plugins/check_disk 148/plugins/check_disk
@@ -202,6 +203,18 @@ NP-VERSION-FILE
202/plugins/stamp-h* 203/plugins/stamp-h*
203/plugins/urlize 204/plugins/urlize
204 205
206# /plugins/picohttpparser
207/plugins/picohttpparser/Makefile
208/plugins/picohttpparser/Makefile.in
209/plugins/picohttpparser/.deps
210/plugins/picohttpparser/libpicohttpparser.a
211
212# /plugins/uriparser
213/plugins/uriparser/Makefile
214/plugins/uriparser/Makefile.in
215/plugins/uriparser/.deps
216/plugins/uriparser/liburiparser.a
217
205# /plugins/t/ 218# /plugins/t/
206/plugins/t/*.tmp 219/plugins/t/*.tmp
207 220
diff --git a/.travis.yml b/.travis.yml
index 617c4154..e725004f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -55,6 +55,7 @@ install:
55 - sudo apt-get install -qq --no-install-recommends autoconf automake 55 - sudo apt-get install -qq --no-install-recommends autoconf automake
56 - sudo apt-get install -qq --no-install-recommends faketime 56 - sudo apt-get install -qq --no-install-recommends faketime
57 - sudo apt-get install -qq --no-install-recommends libmonitoring-plugin-perl 57 - sudo apt-get install -qq --no-install-recommends libmonitoring-plugin-perl
58 - sudo apt-get install -qq --no-install-recommends libcurl4-openssl-dev
58 # Trusty related dependencies (not yet provided) 59 # Trusty related dependencies (not yet provided)
59 - test "$(dpkg -l | grep -E "mysql-(client|server)-[0-9].[0-9]" | grep -c ^ii)" -gt 0 || sudo apt-get install -qq --no-install-recommends mariadb-client mariadb-server 60 - test "$(dpkg -l | grep -E "mysql-(client|server)-[0-9].[0-9]" | grep -c ^ii)" -gt 0 || sudo apt-get install -qq --no-install-recommends mariadb-client mariadb-server
60 # enable ssl apache 61 # enable ssl apache
@@ -69,7 +70,7 @@ before_script:
69 # Detect LDAP configuration (seems volatile on trusty env) 70 # Detect LDAP configuration (seems volatile on trusty env)
70 - sed -e 's/cn=admin,dc=nodomain/'$(sudo /usr/sbin/slapcat|grep ^dn:|grep cn=|awk '{print $2}')'/' -i plugins/t/NPTest.cache.travis 71 - sed -e 's/cn=admin,dc=nodomain/'$(sudo /usr/sbin/slapcat|grep ^dn:|grep cn=|awk '{print $2}')'/' -i plugins/t/NPTest.cache.travis
71 - tools/setup 72 - tools/setup
72 - ./configure --enable-libtap 73 - ./configure --enable-libtap --enable-check-curl
73 - make 74 - make
74 - export NPTEST_CACHE="$(pwd)/plugins/t/NPTest.cache.travis" 75 - export NPTEST_CACHE="$(pwd)/plugins/t/NPTest.cache.travis"
75 - ssh-keygen -t dsa -N "" -f ~/.ssh/id_dsa 76 - ssh-keygen -t dsa -N "" -f ~/.ssh/id_dsa
diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS
index 50c714c3..06e84e25 100644
--- a/ACKNOWLEDGEMENTS
+++ b/ACKNOWLEDGEMENTS
@@ -31,3 +31,16 @@ Gnulib team
31Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc 31Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc
32http://www.gnu.org/software/gnulib/ 32http://www.gnu.org/software/gnulib/
33Use of lib files that originally were used from coreutils 33Use of lib files that originally were used from coreutils
34
35Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
36 Shigeo Mitsunari
37picohttpparser
38https://github.com/h2o/picohttpparser
39Use of the library for HTTP header parsing in check_curl.
40
41Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
42Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
43All rights reserved.
44uriparser - RFC 3986 URI parsing library
45http://uriparser.sourceforge.net/
46Use of the library for URL parsing in check_curl.
diff --git a/REQUIREMENTS b/REQUIREMENTS
index ac7b5935..39e04cdc 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -11,6 +11,20 @@ check_ldaps, check_http --ssl, check_tcp --ssl, check_smtp --starttls
11 - Requires openssl or gnutls libraries for SSL connections 11 - Requires openssl or gnutls libraries for SSL connections
12 http://www.openssl.org, http://www.gnu.org/software/gnutls 12 http://www.openssl.org, http://www.gnu.org/software/gnutls
13 13
14check_curl:
15 - Requires libcurl 7.15.2 or later
16 http://www.haxx.se
17 - --ssl requires OpenSSL for certificate checks, otherwise
18 libcurl must be quite new to support CURLINFO_CRETINFO with
19 GnuTLS and NSS libraries:
20 - 7.42.0 or newer for GnuTLS
21 - 7.34.0 or newer for NSS
22 GnuTLS is known to create problems on some distributions with
23 self-signed certificate chains
24 http://www.openssl.org, http://www.gnu.org/software/gnutls,
25 http://www.mozilla.org/projects/security/pki/nss/,
26 other SSL implementations are currently not supported
27
14check_fping: 28check_fping:
15 - Requires the fping utility distributed with SATAN. Either 29 - Requires the fping utility distributed with SATAN. Either
16 download and install SATAN or grab the fping program from 30 download and install SATAN or grab the fping program from
diff --git a/configure.ac b/configure.ac
index bf129956..2291c51b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,6 +385,32 @@ if test "$ac_cv_header_wtsapi32_h" = "yes"; then
385 AC_SUBST(WTSAPI32LIBS) 385 AC_SUBST(WTSAPI32LIBS)
386fi 386fi
387 387
388AC_ARG_ENABLE(check-curl,
389 AC_HELP_STRING([--enable-check-curl],
390 [Enables compilation of check_curl (default: no)]),
391 [enable_check_curl=$enableval],
392 [enable_check_curl=no])
393if test "$enable_check_curl" = "yes" ; then
394 dnl Check for cURL library
395 LIBCURL_CHECK_CONFIG(yes, 7.15.2, [
396 EXTRAS="$EXTRAS check_curl\$(EXEEXT)"
397 LIBCURLINCLUDE="$LIBCURL_CPPFLAGS"
398 LIBCURLLIBS="$LIBCURL"
399 LIBCURLCFLAGS="$LIBCURL_CPPFLAGS"
400 AC_SUBST(LIBCURLINCLUDE)
401 AC_SUBST(LIBCURLLIBS)
402 AC_SUBST(LIBCURLCFLAGS)
403 AC_SUBST(PICOHTTPPARSER_DIR, picohttpparser)
404 AC_SUBST(URIPARSER_DIR, uriparser)
405 ], [
406 AC_MSG_WARN([Skipping curl plugin])
407 AC_MSG_WARN([install libcurl libs to compile this plugin (see REQUIREMENTS).])
408 ])
409fi
410AM_CONDITIONAL([WITH_CHECK_CURL], [test "$enable_check_curl" = "yes"])
411AM_COND_IF([WITH_CHECK_CURL],
412 [AC_CONFIG_FILES([plugins/picohttpparser/Makefile plugins/uriparser/Makefile])])
413
388dnl Fallback to who(1) if the system doesn't provide an utmpx(5) interface 414dnl Fallback to who(1) if the system doesn't provide an utmpx(5) interface
389if test "$ac_cv_header_utmpx_h" = "no" -a "$ac_cv_header_wtsapi32_h" = "no" 415if test "$ac_cv_header_utmpx_h" = "no" -a "$ac_cv_header_wtsapi32_h" = "no"
390then 416then
@@ -1882,4 +1908,6 @@ ACX_FEATURE([enable],[perl-modules])
1882ACX_FEATURE([with],[cgiurl]) 1908ACX_FEATURE([with],[cgiurl])
1883ACX_FEATURE([with],[trusted-path]) 1909ACX_FEATURE([with],[trusted-path])
1884ACX_FEATURE([enable],[libtap]) 1910ACX_FEATURE([enable],[libtap])
1911ACX_FEATURE([enable],[check-curl])
1912ACX_FEATURE([with],[libcurl])
1885 1913
diff --git a/m4/libcurl.m4 b/m4/libcurl.m4
new file mode 100644
index 00000000..53d694d0
--- /dev/null
+++ b/m4/libcurl.m4
@@ -0,0 +1,272 @@
1#***************************************************************************
2# _ _ ____ _
3# Project ___| | | | _ \| |
4# / __| | | | |_) | |
5# | (__| |_| | _ <| |___
6# \___|\___/|_| \_\_____|
7#
8# Copyright (C) 2006, David Shaw <dshaw@jabberwocky.com>
9#
10# This software is licensed as described in the file COPYING, which
11# you should have received as part of this distribution. The terms
12# are also available at https://curl.haxx.se/docs/copyright.html.
13#
14# You may opt to use, copy, modify, merge, publish, distribute and/or sell
15# copies of the Software, and permit persons to whom the Software is
16# furnished to do so, under the terms of the COPYING file.
17#
18# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19# KIND, either express or implied.
20#
21###########################################################################
22# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION],
23# [ACTION-IF-YES], [ACTION-IF-NO])
24# ----------------------------------------------------------
25# David Shaw <dshaw@jabberwocky.com> May-09-2006
26#
27# Checks for libcurl. DEFAULT-ACTION is the string yes or no to
28# specify whether to default to --with-libcurl or --without-libcurl.
29# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the
30# minimum version of libcurl to accept. Pass the version as a regular
31# version number like 7.10.1. If not supplied, any version is
32# accepted. ACTION-IF-YES is a list of shell commands to run if
33# libcurl was successfully found and passed the various tests.
34# ACTION-IF-NO is a list of shell commands that are run otherwise.
35# Note that using --without-libcurl does run ACTION-IF-NO.
36#
37# This macro #defines HAVE_LIBCURL if a working libcurl setup is
38# found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary
39# values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are
40# the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy
41# where yyy are the various protocols supported by libcurl. Both xxx
42# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of
43# the macro for the complete list of possible defines. Shell
44# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also
45# defined to 'yes' for those features and protocols that were found.
46# Note that xxx and yyy keep the same capitalization as in the
47# curl-config list (e.g. it's "HTTP" and not "http").
48#
49# Users may override the detected values by doing something like:
50# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure
51#
52# For the sake of sanity, this macro assumes that any libcurl that is
53# found is after version 7.7.2, the first version that included the
54# curl-config script. Note that it is very important for people
55# packaging binary versions of libcurl to include this script!
56# Without curl-config, we can only guess what protocols are available,
57# or use curl_version_info to figure it out at runtime.
58
59AC_DEFUN([LIBCURL_CHECK_CONFIG],
60[
61 AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL])
62 AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4])
63 AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6])
64 AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz])
65 AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS])
66 AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN])
67 AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI])
68 AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM])
69
70 AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP])
71 AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS])
72 AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP])
73 AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS])
74 AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE])
75 AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET])
76 AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP])
77 AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT])
78 AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP])
79 AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP])
80 AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3])
81 AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP])
82 AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP])
83
84 AC_ARG_WITH(libcurl,
85 AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]),
86 [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])])
87
88 if test "$_libcurl_with" != "no" ; then
89
90 AC_PROG_AWK
91
92 _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'"
93
94 _libcurl_try_link=yes
95
96 if test -d "$_libcurl_with" ; then
97 LIBCURL_CPPFLAGS="-I$withval/include"
98 _libcurl_ldflags="-L$withval/lib"
99 AC_PATH_PROG([_libcurl_config],[curl-config],[],
100 ["$withval/bin"])
101 else
102 AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
103 fi
104
105 if test x$_libcurl_config != "x" ; then
106 AC_CACHE_CHECK([for the version of libcurl],
107 [libcurl_cv_lib_curl_version],
108 [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`])
109
110 _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
111 _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse`
112
113 if test $_libcurl_wanted -gt 0 ; then
114 AC_CACHE_CHECK([for libcurl >= version $2],
115 [libcurl_cv_lib_version_ok],
116 [
117 if test $_libcurl_version -ge $_libcurl_wanted ; then
118 libcurl_cv_lib_version_ok=yes
119 else
120 libcurl_cv_lib_version_ok=no
121 fi
122 ])
123 fi
124
125 if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
126 if test x"$LIBCURL_CPPFLAGS" = "x" ; then
127 LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
128 fi
129 if test x"$LIBCURL" = "x" ; then
130 LIBCURL=`$_libcurl_config --libs`
131
132 # This is so silly, but Apple actually has a bug in their
133 # curl-config script. Fixed in Tiger, but there are still
134 # lots of Panther installs around.
135 case "${host}" in
136 powerpc-apple-darwin7*)
137 LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
138 ;;
139 esac
140 fi
141
142 # All curl-config scripts support --feature
143 _libcurl_features=`$_libcurl_config --feature`
144
145 # Is it modern enough to have --protocols? (7.12.4)
146 if test $_libcurl_version -ge 461828 ; then
147 _libcurl_protocols=`$_libcurl_config --protocols`
148 fi
149 else
150 _libcurl_try_link=no
151 fi
152
153 unset _libcurl_wanted
154 fi
155
156 if test $_libcurl_try_link = yes ; then
157
158 # we didn't find curl-config, so let's see if the user-supplied
159 # link line (or failing that, "-lcurl") is enough.
160 LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
161
162 AC_CACHE_CHECK([whether libcurl is usable],
163 [libcurl_cv_lib_curl_usable],
164 [
165 _libcurl_save_cppflags=$CPPFLAGS
166 CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
167 _libcurl_save_libs=$LIBS
168 LIBS="$LIBCURL $LIBS"
169
170 AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
171/* Try and use a few common options to force a failure if we are
172 missing symbols or can't link. */
173int x;
174curl_easy_setopt(NULL,CURLOPT_URL,NULL);
175x=CURL_ERROR_SIZE;
176x=CURLOPT_WRITEFUNCTION;
177x=CURLOPT_WRITEDATA;
178x=CURLOPT_ERRORBUFFER;
179x=CURLOPT_STDERR;
180x=CURLOPT_VERBOSE;
181if (x) {;}
182]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
183
184 CPPFLAGS=$_libcurl_save_cppflags
185 LIBS=$_libcurl_save_libs
186 unset _libcurl_save_cppflags
187 unset _libcurl_save_libs
188 ])
189
190 if test $libcurl_cv_lib_curl_usable = yes ; then
191
192 # Does curl_free() exist in this version of libcurl?
193 # If not, fake it with free()
194
195 _libcurl_save_cppflags=$CPPFLAGS
196 CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
197 _libcurl_save_libs=$LIBS
198 LIBS="$LIBS $LIBCURL"
199
200 AC_CHECK_FUNC(curl_free,,
201 AC_DEFINE(curl_free,free,
202 [Define curl_free() as free() if our version of curl lacks curl_free.]))
203
204 CPPFLAGS=$_libcurl_save_cppflags
205 LIBS=$_libcurl_save_libs
206 unset _libcurl_save_cppflags
207 unset _libcurl_save_libs
208
209 AC_DEFINE(HAVE_LIBCURL,1,
210 [Define to 1 if you have a functional curl library.])
211 AC_SUBST(LIBCURL_CPPFLAGS)
212 AC_SUBST(LIBCURL)
213
214 for _libcurl_feature in $_libcurl_features ; do
215 AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
216 eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
217 done
218
219 if test "x$_libcurl_protocols" = "x" ; then
220
221 # We don't have --protocols, so just assume that all
222 # protocols are available
223 _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
224
225 if test x$libcurl_feature_SSL = xyes ; then
226 _libcurl_protocols="$_libcurl_protocols HTTPS"
227
228 # FTPS wasn't standards-compliant until version
229 # 7.11.0 (0x070b00 == 461568)
230 if test $_libcurl_version -ge 461568; then
231 _libcurl_protocols="$_libcurl_protocols FTPS"
232 fi
233 fi
234
235 # RTSP, IMAP, POP3 and SMTP were added in
236 # 7.20.0 (0x071400 == 463872)
237 if test $_libcurl_version -ge 463872; then
238 _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
239 fi
240 fi
241
242 for _libcurl_protocol in $_libcurl_protocols ; do
243 AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1])
244 eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes
245 done
246 else
247 unset LIBCURL
248 unset LIBCURL_CPPFLAGS
249 fi
250 fi
251
252 unset _libcurl_try_link
253 unset _libcurl_version_parse
254 unset _libcurl_config
255 unset _libcurl_feature
256 unset _libcurl_features
257 unset _libcurl_protocol
258 unset _libcurl_protocols
259 unset _libcurl_version
260 unset _libcurl_ldflags
261 fi
262
263 if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then
264 # This is the IF-NO path
265 ifelse([$4],,:,[$4])
266 else
267 # This is the IF-YES path
268 ifelse([$3],,:,[$3])
269 fi
270
271 unset _libcurl_with
272])dnl
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0ddf9bd1..34a7da8b 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -38,7 +38,9 @@ check_tcp_programs = check_ftp check_imap check_nntp check_pop \
38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \ 38EXTRA_PROGRAMS = check_mysql check_radius check_pgsql check_snmp check_hpjd \
39 check_swap check_fping check_ldap check_game check_dig \ 39 check_swap check_fping check_ldap check_game check_dig \
40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \ 40 check_nagios check_by_ssh check_dns check_nt check_ide_smart \
41 check_procs check_mysql_query check_apt check_dbi 41 check_procs check_mysql_query check_apt check_dbi check_curl
42
43SUBDIRS = @PICOHTTPPARSER_DIR@ @URIPARSER_DIR@
42 44
43EXTRA_DIST = t tests 45EXTRA_DIST = t tests
44 46
@@ -69,6 +71,9 @@ test-debug:
69 71
70check_apt_LDADD = $(BASEOBJS) 72check_apt_LDADD = $(BASEOBJS)
71check_cluster_LDADD = $(BASEOBJS) 73check_cluster_LDADD = $(BASEOBJS)
74check_curl_CFLAGS = $(AM_CFLAGS) $(LIBCURLCFLAGS) -Ipicohttpparser -Iuriparser
75check_curl_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURLINCLUDE) -Ipicohttpparser -Iuriparser
76check_curl_LDADD = $(NETLIBS) $(LIBCURLLIBS) $(SSLOBJS) picohttpparser/libpicohttpparser.a uriparser/liburiparser.a
72check_dbi_LDADD = $(NETLIBS) $(DBILIBS) 77check_dbi_LDADD = $(NETLIBS) $(DBILIBS)
73check_dig_LDADD = $(NETLIBS) 78check_dig_LDADD = $(NETLIBS)
74check_disk_LDADD = $(BASEOBJS) 79check_disk_LDADD = $(BASEOBJS)
@@ -89,7 +94,7 @@ check_mysql_query_CFLAGS = $(AM_CFLAGS) $(MYSQLCFLAGS)
89check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE) 94check_mysql_query_CPPFLAGS = $(AM_CPPFLAGS) $(MYSQLINCLUDE)
90check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS) 95check_mysql_query_LDADD = $(NETLIBS) $(MYSQLLIBS)
91check_nagios_LDADD = $(BASEOBJS) 96check_nagios_LDADD = $(BASEOBJS)
92check_nt_LDADD = $(NETLIBS) 97check_nt_LDADD = $(NETLIBS)
93check_ntp_LDADD = $(NETLIBS) $(MATHLIBS) 98check_ntp_LDADD = $(NETLIBS) $(MATHLIBS)
94check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS) 99check_ntp_peer_LDADD = $(NETLIBS) $(MATHLIBS)
95check_nwstat_LDADD = $(NETLIBS) 100check_nwstat_LDADD = $(NETLIBS)
diff --git a/plugins/check_curl.c b/plugins/check_curl.c
new file mode 100644
index 00000000..2f583052
--- /dev/null
+++ b/plugins/check_curl.c
@@ -0,0 +1,2256 @@
1/*****************************************************************************
2*
3* Monitoring check_curl plugin
4*
5* License: GPL
6* Copyright (c) 1999-2017 Monitoring Plugins Development Team
7*
8* Description:
9*
10* This file contains the check_curl plugin
11*
12* This plugin tests the HTTP service on the specified host. It can test
13* normal (http) and secure (https) servers, follow redirects, search for
14* strings and regular expressions, check connection times, and report on
15* certificate expiration times.
16*
17* This plugin uses functions from the curl library, see
18* http://curl.haxx.se
19*
20* This program is free software: you can redistribute it and/or modify
21* it under the terms of the GNU General Public License as published by
22* the Free Software Foundation, either version 3 of the License, or
23* (at your option) any later version.
24*
25* This program is distributed in the hope that it will be useful,
26* but WITHOUT ANY WARRANTY; without even the implied warranty of
27* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28* GNU General Public License for more details.
29*
30* You should have received a copy of the GNU General Public License
31* along with this program. If not, see <http://www.gnu.org/licenses/>.
32*
33*
34*****************************************************************************/
35const char *progname = "check_curl";
36
37const char *copyright = "2006-2017";
38const char *email = "devel@monitoring-plugins.org";
39
40#include <ctype.h>
41
42#include "common.h"
43#include "utils.h"
44
45#ifndef LIBCURL_PROTOCOL_HTTP
46#error libcurl compiled without HTTP support, compiling check_curl plugin does not makes a lot of sense
47#endif
48
49#include "curl/curl.h"
50#include "curl/easy.h"
51
52#include "picohttpparser.h"
53
54#include "uriparser/Uri.h"
55
56#include <arpa/inet.h>
57
58#define MAKE_LIBCURL_VERSION(major, minor, patch) ((major)*0x10000 + (minor)*0x100 + (patch))
59
60#define DEFAULT_BUFFER_SIZE 2048
61#define DEFAULT_SERVER_URL "/"
62#define HTTP_EXPECT "HTTP/1."
63#define DEFAULT_MAX_REDIRS 15
64#define INET_ADDR_MAX_SIZE INET6_ADDRSTRLEN
65enum {
66 MAX_IPV4_HOSTLENGTH = 255,
67 HTTP_PORT = 80,
68 HTTPS_PORT = 443,
69 MAX_PORT = 65535
70};
71
72enum {
73 STICKY_NONE = 0,
74 STICKY_HOST = 1,
75 STICKY_PORT = 2
76};
77
78enum {
79 FOLLOW_HTTP_CURL = 0,
80 FOLLOW_LIBCURL = 1
81};
82
83/* for buffers for header and body */
84typedef struct {
85 char *buf;
86 size_t buflen;
87 size_t bufsize;
88} curlhelp_write_curlbuf;
89
90/* for buffering the data sent in PUT */
91typedef struct {
92 char *buf;
93 size_t buflen;
94 off_t pos;
95} curlhelp_read_curlbuf;
96
97/* for parsing the HTTP status line */
98typedef struct {
99 int http_major; /* major version of the protocol, always 1 (HTTP/0.9
100 * never reached the big internet most likely) */
101 int http_minor; /* minor version of the protocol, usually 0 or 1 */
102 int http_code; /* HTTP return code as in RFC 2145 */
103 int http_subcode; /* Microsoft IIS extension, HTTP subcodes, see
104 * http://support.microsoft.com/kb/318380/en-us */
105 const char *msg; /* the human readable message */
106 char *first_line; /* a copy of the first line */
107} curlhelp_statusline;
108
109/* to know the underlying SSL library used by libcurl */
110typedef enum curlhelp_ssl_library {
111 CURLHELP_SSL_LIBRARY_UNKNOWN,
112 CURLHELP_SSL_LIBRARY_OPENSSL,
113 CURLHELP_SSL_LIBRARY_LIBRESSL,
114 CURLHELP_SSL_LIBRARY_GNUTLS,
115 CURLHELP_SSL_LIBRARY_NSS
116} curlhelp_ssl_library;
117
118enum {
119 REGS = 2,
120 MAX_RE_SIZE = 256
121};
122#include "regex.h"
123regex_t preg;
124regmatch_t pmatch[REGS];
125char regexp[MAX_RE_SIZE];
126int cflags = REG_NOSUB | REG_EXTENDED | REG_NEWLINE;
127int errcode;
128int invert_regex = 0;
129
130char *server_address;
131char *host_name;
132char *server_url = DEFAULT_SERVER_URL;
133char server_ip[DEFAULT_BUFFER_SIZE];
134struct curl_slist *server_ips = NULL;
135unsigned short server_port = HTTP_PORT;
136int virtual_port = 0;
137int host_name_length;
138char output_header_search[30] = "";
139char output_string_search[30] = "";
140char *warning_thresholds = NULL;
141char *critical_thresholds = NULL;
142int days_till_exp_warn, days_till_exp_crit;
143thresholds *thlds;
144char user_agent[DEFAULT_BUFFER_SIZE];
145int verbose = 0;
146int show_extended_perfdata = FALSE;
147int min_page_len = 0;
148int max_page_len = 0;
149int redir_depth = 0;
150int max_depth = DEFAULT_MAX_REDIRS;
151char *http_method = NULL;
152char *http_post_data = NULL;
153char *http_content_type = NULL;
154CURL *curl;
155struct curl_slist *header_list = NULL;
156curlhelp_write_curlbuf body_buf;
157curlhelp_write_curlbuf header_buf;
158curlhelp_statusline status_line;
159curlhelp_read_curlbuf put_buf;
160char http_header[DEFAULT_BUFFER_SIZE];
161long code;
162long socket_timeout = DEFAULT_SOCKET_TIMEOUT;
163double total_time;
164double time_connect;
165double time_appconnect;
166double time_headers;
167double time_firstbyte;
168char errbuf[CURL_ERROR_SIZE+1];
169CURLcode res;
170char url[DEFAULT_BUFFER_SIZE];
171char msg[DEFAULT_BUFFER_SIZE];
172char perfstring[DEFAULT_BUFFER_SIZE];
173char header_expect[MAX_INPUT_BUFFER] = "";
174char string_expect[MAX_INPUT_BUFFER] = "";
175char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
176int server_expect_yn = 0;
177char user_auth[MAX_INPUT_BUFFER] = "";
178char proxy_auth[MAX_INPUT_BUFFER] = "";
179char **http_opt_headers;
180int http_opt_headers_count = 0;
181int display_html = FALSE;
182int onredirect = STATE_OK;
183int followmethod = FOLLOW_HTTP_CURL;
184int followsticky = STICKY_NONE;
185int use_ssl = FALSE;
186int use_sni = TRUE;
187int check_cert = FALSE;
188typedef union {
189 struct curl_slist* to_info;
190 struct curl_certinfo* to_certinfo;
191} cert_ptr_union;
192cert_ptr_union cert_ptr;
193int ssl_version = CURL_SSLVERSION_DEFAULT;
194char *client_cert = NULL;
195char *client_privkey = NULL;
196char *ca_cert = NULL;
197int is_openssl_callback = FALSE;
198#if defined(HAVE_SSL) && defined(USE_OPENSSL)
199X509 *cert = NULL;
200#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
201int no_body = FALSE;
202int maximum_age = -1;
203int address_family = AF_UNSPEC;
204curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
205
206int process_arguments (int, char**);
207void handle_curl_option_return_code (CURLcode res, const char* option);
208int check_http (void);
209void redir (curlhelp_write_curlbuf*);
210void print_help (void);
211void print_usage (void);
212void print_curl_version (void);
213int curlhelp_initwritebuffer (curlhelp_write_curlbuf*);
214int curlhelp_buffer_write_callback (void*, size_t , size_t , void*);
215void curlhelp_freewritebuffer (curlhelp_write_curlbuf*);
216int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t);
217int curlhelp_buffer_read_callback (void *, size_t , size_t , void *);
218void curlhelp_freereadbuffer (curlhelp_read_curlbuf *);
219curlhelp_ssl_library curlhelp_get_ssl_library (CURL*);
220const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library);
221int net_noopenssl_check_certificate (cert_ptr_union*, int, int);
222
223int curlhelp_parse_statusline (const char*, curlhelp_statusline *);
224void curlhelp_free_statusline (curlhelp_statusline *);
225char *perfd_time_ssl (double microsec);
226char *get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header);
227int check_document_dates (const curlhelp_write_curlbuf *, char (*msg)[DEFAULT_BUFFER_SIZE]);
228int get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf);
229
230#if defined(HAVE_SSL) && defined(USE_OPENSSL)
231int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit);
232#endif /* defined(HAVE_SSL) && defined(USE_OPENSSL) */
233
234void remove_newlines (char *);
235void test_file (char *);
236
237int
238main (int argc, char **argv)
239{
240 int result = STATE_UNKNOWN;
241
242 setlocale (LC_ALL, "");
243 bindtextdomain (PACKAGE, LOCALEDIR);
244 textdomain (PACKAGE);
245
246 /* Parse extra opts if any */
247 argv = np_extra_opts (&argc, argv, progname);
248
249 /* set defaults */
250 snprintf( user_agent, DEFAULT_BUFFER_SIZE, "%s/v%s (monitoring-plugins %s)",
251 progname, NP_VERSION, VERSION);
252
253 /* parse arguments */
254 if (process_arguments (argc, argv) == ERROR)
255 usage4 (_("Could not parse arguments"));
256
257 if (display_html == TRUE)
258 printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">",
259 use_ssl ? "https" : "http", host_name ? host_name : server_address,
260 server_port, server_url);
261
262 result = check_http ();
263 return result;
264}
265
266#ifdef HAVE_SSL
267#ifdef USE_OPENSSL
268
269int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
270{
271 /* TODO: we get all certificates of the chain, so which ones
272 * should we test?
273 * TODO: is the last certificate always the server certificate?
274 */
275 cert = X509_STORE_CTX_get_current_cert(x509_ctx);
276 return 1;
277}
278
279CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm)
280{
281 SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);
282
283 return CURLE_OK;
284}
285
286#endif /* USE_OPENSSL */
287#endif /* HAVE_SSL */
288
289/* Checks if the server 'reply' is one of the expected 'statuscodes' */
290static int
291expected_statuscode (const char *reply, const char *statuscodes)
292{
293 char *expected, *code;
294 int result = 0;
295
296 if ((expected = strdup (statuscodes)) == NULL)
297 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Memory allocation error\n"));
298
299 for (code = strtok (expected, ","); code != NULL; code = strtok (NULL, ","))
300 if (strstr (reply, code) != NULL) {
301 result = 1;
302 break;
303 }
304
305 free (expected);
306 return result;
307}
308
309void
310handle_curl_option_return_code (CURLcode res, const char* option)
311{
312 if (res != CURLE_OK) {
313 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Error while setting cURL option '%s': cURL returned %d - %s"),
314 option, res, curl_easy_strerror(res));
315 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
316 }
317}
318
319int
320check_http (void)
321{
322 int result = STATE_OK;
323 int page_len = 0;
324 int i;
325 char *force_host_header = NULL;
326
327 /* initialize curl */
328 if (curl_global_init (CURL_GLOBAL_DEFAULT) != CURLE_OK)
329 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_global_init failed\n");
330
331 if ((curl = curl_easy_init()) == NULL)
332 die (STATE_UNKNOWN, "HTTP UNKNOWN - curl_easy_init failed\n");
333
334 if (verbose >= 1)
335 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_VERBOSE, TRUE), "CURLOPT_VERBOSE");
336
337 /* print everything on stdout like check_http would do */
338 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_STDERR, stdout), "CURLOPT_STDERR");
339
340 /* initialize buffer for body of the answer */
341 if (curlhelp_initwritebuffer(&body_buf) < 0)
342 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for body\n");
343 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_WRITEFUNCTION");
344 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *)&body_buf), "CURLOPT_WRITEDATA");
345
346 /* initialize buffer for header of the answer */
347 if (curlhelp_initwritebuffer( &header_buf ) < 0)
348 die (STATE_UNKNOWN, "HTTP CRITICAL - out of memory allocating buffer for header\n" );
349 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, (curl_write_callback)curlhelp_buffer_write_callback), "CURLOPT_HEADERFUNCTION");
350 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_WRITEHEADER, (void *)&header_buf), "CURLOPT_WRITEHEADER");
351
352 /* set the error buffer */
353 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, errbuf), "CURLOPT_ERRORBUFFER");
354
355 /* set timeouts */
356 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, socket_timeout), "CURLOPT_CONNECTTIMEOUT");
357 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_TIMEOUT, socket_timeout), "CURLOPT_TIMEOUT");
358
359 /* compose URL: must be the host_name, only if not given take the IP address. */
360 snprintf (url, DEFAULT_BUFFER_SIZE, "%s://%s%s", use_ssl ? "https" : "http",
361 host_name ? host_name : server_address, server_url);
362 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, url), "CURLOPT_URL");
363
364 /* cURL does certificate checking with this host_name (and not the virtual host?
365 * So we force CURLOPT_RESOLVE to make sure the resolver pickes the right IP
366 * for this hostname. */
367#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 3)
368 if (host_name && strcmp (host_name, server_address)) {
369 snprintf (server_ip, DEFAULT_BUFFER_SIZE, "%s:%d:%s", host_name, server_port, server_address);
370 server_ips = curl_slist_append (server_ips, server_ip);
371 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_RESOLVE, server_ips), "CURLOPT_RESOLVE");
372 }
373#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 21, 3) */
374
375 /* extract proxy information for legacy proxy https requests */
376 if (!strcmp(http_method, "CONNECT") || strstr(server_url, "http") == server_url) {
377 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXY, server_address), "CURLOPT_PROXY");
378 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYPORT, (long)server_port), "CURLOPT_PROXYPORT");
379 if (verbose>=2)
380 printf ("* curl CURLOPT_PROXY: %s:%d\n", server_address, server_port);
381 http_method = "GET";
382 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_URL, server_url), "CURLOPT_URL");
383 virtual_port = use_ssl ? HTTPS_PORT : HTTP_PORT;
384 } else {
385 /* set port */
386 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PORT, server_port), "CURLOPT_PORT");
387 }
388
389 /* set HTTP method */
390 if (http_method) {
391 if (!strcmp(http_method, "POST"))
392 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POST, 1), "CURLOPT_POST");
393 else if (!strcmp(http_method, "PUT"))
394 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_UPLOAD, 1), "CURLOPT_UPLOAD");
395 else
396 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, http_method), "CURLOPT_CUSTOMREQUEST");
397 }
398
399 /* set hostname (virtual hosts) */
400 if(host_name != NULL) {
401 if((virtual_port != HTTP_PORT && !use_ssl) || (virtual_port != HTTPS_PORT && use_ssl)) {
402 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s:%d", host_name, virtual_port);
403 } else {
404 snprintf(http_header, DEFAULT_BUFFER_SIZE, "Host: %s", host_name);
405 }
406 header_list = curl_slist_append (header_list, http_header);
407 }
408
409 /* always close connection, be nice to servers */
410 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Connection: close");
411 header_list = curl_slist_append (header_list, http_header);
412
413 /* check if Host header is explicitly set in options */
414 if (http_opt_headers_count) {
415 for (i = 0; i < http_opt_headers_count ; i++) {
416 if (strncmp(http_opt_headers[i], "Host:", 5) == 0) {
417 force_host_header = http_opt_headers[i];
418 }
419 }
420 }
421
422 /* attach additional headers supplied by the user */
423 /* optionally send any other header tag */
424 if (http_opt_headers_count) {
425 for (i = 0; i < http_opt_headers_count ; i++) {
426 if (force_host_header != http_opt_headers[i]) {
427 header_list = curl_slist_append (header_list, http_opt_headers[i]);
428 }
429 }
430 /* This cannot be free'd here because a redirection will then try to access this and segfault */
431 /* Covered in a testcase in tests/check_http.t */
432 /* free(http_opt_headers); */
433 }
434
435 /* set HTTP headers */
436 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER");
437
438#ifdef LIBCURL_FEATURE_SSL
439
440 /* set SSL version, warn about unsecure or unsupported versions */
441 if (use_ssl) {
442 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION");
443 }
444
445 /* client certificate and key to present to server (SSL) */
446 if (client_cert)
447 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLCERT, client_cert), "CURLOPT_SSLCERT");
448 if (client_privkey)
449 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLKEY, client_privkey), "CURLOPT_SSLKEY");
450 if (ca_cert) {
451 /* per default if we have a CA verify both the peer and the
452 * hostname in the certificate, can be switched off later */
453 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO");
454 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER");
455 handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST");
456 } else {
457 /* backward-compatible behaviour, be tolerant in checks
458 * TODO: depending on more options have aspects we want
459 * to be less tolerant about ssl verfications
460 */
461 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER");
462 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST");
463 }
464
465 /* detect SSL library used by libcurl */
466 ssl_library = curlhelp_get_ssl_library (curl);
467
468 /* try hard to get a stack of certificates to verify against */
469 if (check_cert)
470#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1)
471 /* inform curl to report back certificates */
472 switch (ssl_library) {
473 case CURLHELP_SSL_LIBRARY_OPENSSL:
474 case CURLHELP_SSL_LIBRARY_LIBRESSL:
475 /* set callback to extract certificate with OpenSSL context function (works with
476 * OpenSSL-style libraries only!) */
477#ifdef USE_OPENSSL
478 /* libcurl and monitoring plugins built with OpenSSL, good */
479 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
480 is_openssl_callback = TRUE;
481#else /* USE_OPENSSL */
482#endif /* USE_OPENSSL */
483 /* libcurl is built with OpenSSL, monitoring plugins, so falling
484 * back to manually extracting certificate information */
485 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
486 break;
487
488 case CURLHELP_SSL_LIBRARY_NSS:
489#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
490 /* NSS: support for CERTINFO is implemented since 7.34.0 */
491 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
492#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
493 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
494#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
495 break;
496
497 case CURLHELP_SSL_LIBRARY_GNUTLS:
498#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0)
499 /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */
500 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO");
501#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
502 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library));
503#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */
504 break;
505
506 case CURLHELP_SSL_LIBRARY_UNKNOWN:
507 default:
508 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library));
509 break;
510 }
511#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
512 /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */
513 if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL)
514 handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION");
515 else
516 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl too old and has no CURLOPT_CERTINFO)\n");
517#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */
518
519#endif /* LIBCURL_FEATURE_SSL */
520
521 /* set default or user-given user agent identification */
522 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT");
523
524 /* proxy-authentication */
525 if (strcmp(proxy_auth, ""))
526 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, proxy_auth), "CURLOPT_PROXYUSERPWD");
527
528 /* authentication */
529 if (strcmp(user_auth, ""))
530 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERPWD, user_auth), "CURLOPT_USERPWD");
531
532 /* TODO: parameter auth method, bitfield of following methods:
533 * CURLAUTH_BASIC (default)
534 * CURLAUTH_DIGEST
535 * CURLAUTH_DIGEST_IE
536 * CURLAUTH_NEGOTIATE
537 * CURLAUTH_NTLM
538 * CURLAUTH_NTLM_WB
539 *
540 * convenience tokens for typical sets of methods:
541 * CURLAUTH_ANYSAFE: most secure, without BASIC
542 * or CURLAUTH_ANY: most secure, even BASIC if necessary
543 *
544 * handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_DIGEST ), "CURLOPT_HTTPAUTH");
545 */
546
547 /* handle redirections */
548 if (onredirect == STATE_DEPENDENT) {
549 if( followmethod == FOLLOW_LIBCURL ) {
550 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1), "CURLOPT_FOLLOWLOCATION");
551
552 /* default -1 is infinite, not good, could lead to zombie plugins!
553 Setting it to one bigger than maximal limit to handle errors nicely below
554 */
555 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_MAXREDIRS, max_depth+1), "CURLOPT_MAXREDIRS");
556
557 /* for now allow only http and https (we are a http(s) check plugin in the end) */
558#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4)
559 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS), "CURLOPT_REDIRECT_PROTOCOLS");
560#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 4) */
561
562 /* TODO: handle the following aspects of redirection, make them
563 * command line options too later:
564 CURLOPT_POSTREDIR: method switch
565 CURLINFO_REDIRECT_URL: custom redirect option
566 CURLOPT_REDIRECT_PROTOCOLS: allow people to step outside safe protocols
567 CURLINFO_REDIRECT_COUNT: get the number of redirects, print it, maybe a range option here is nice like for expected page size?
568 */
569 } else {
570 /* old style redirection is handled below */
571 }
572 }
573
574 /* no-body */
575 if (no_body)
576 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_NOBODY, 1), "CURLOPT_NOBODY");
577
578 /* IPv4 or IPv6 forced DNS resolution */
579 if (address_family == AF_UNSPEC)
580 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)");
581 else if (address_family == AF_INET)
582 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)");
583#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
584 else if (address_family == AF_INET6)
585 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)");
586#endif
587
588 /* either send http POST data (any data, not only POST)*/
589 if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) {
590 /* set content of payload for POST and PUT */
591 if (http_content_type) {
592 snprintf (http_header, DEFAULT_BUFFER_SIZE, "Content-Type: %s", http_content_type);
593 header_list = curl_slist_append (header_list, http_header);
594 }
595 /* NULL indicates "HTTP Continue" in libcurl, provide an empty string
596 * in case of no POST/PUT data */
597 if (!http_post_data)
598 http_post_data = "";
599 if (!strcmp(http_method, "POST")) {
600 /* POST method, set payload with CURLOPT_POSTFIELDS */
601 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_POSTFIELDS, http_post_data), "CURLOPT_POSTFIELDS");
602 } else if (!strcmp(http_method, "PUT")) {
603 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READFUNCTION, (curl_read_callback)curlhelp_buffer_read_callback), "CURLOPT_READFUNCTION");
604 curlhelp_initreadbuffer (&put_buf, http_post_data, strlen (http_post_data));
605 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_READDATA, (void *)&put_buf), "CURLOPT_READDATA");
606 handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_INFILESIZE, (curl_off_t)strlen (http_post_data)), "CURLOPT_INFILESIZE");
607 }
608 }
609
610 /* do the request */
611 res = curl_easy_perform(curl);
612
613 if (verbose>=2 && http_post_data)
614 printf ("**** REQUEST CONTENT ****\n%s\n", http_post_data);
615
616 /* free header and server IP resolve lists, we don't need it anymore */
617 curl_slist_free_all (header_list); header_list = NULL;
618 curl_slist_free_all (server_ips); server_ips = NULL;
619
620 /* Curl errors, result in critical Nagios state */
621 if (res != CURLE_OK) {
622 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: cURL returned %d - %s"),
623 server_port, res, curl_easy_strerror(res));
624 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
625 }
626
627 /* certificate checks */
628#ifdef LIBCURL_FEATURE_SSL
629 if (use_ssl == TRUE) {
630 if (check_cert == TRUE) {
631 if (is_openssl_callback) {
632#ifdef USE_OPENSSL
633 /* check certificate with OpenSSL functions, curl has been built against OpenSSL
634 * and we actually have OpenSSL in the monitoring tools
635 */
636 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
637 return result;
638#else /* USE_OPENSSL */
639 die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n");
640#endif /* USE_OPENSSL */
641 } else {
642 int i;
643 struct curl_slist *slist;
644
645 cert_ptr.to_info = NULL;
646 res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info);
647 if (!res && cert_ptr.to_info) {
648#ifdef USE_OPENSSL
649 /* We have no OpenSSL in libcurl, but we can use OpenSSL for X509 cert parsing
650 * We only check the first certificate and assume it's the one of the server
651 */
652 const char* raw_cert = NULL;
653 for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) {
654 for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) {
655 if (verbose >= 2)
656 printf ("%d ** %s\n", i, slist->data);
657 if (strncmp (slist->data, "Cert:", 5) == 0) {
658 raw_cert = &slist->data[5];
659 goto GOT_FIRST_CERT;
660 }
661 }
662 }
663GOT_FIRST_CERT:
664 if (!raw_cert) {
665 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates from CERTINFO information - certificate data was empty"));
666 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
667 }
668 BIO* cert_BIO = BIO_new (BIO_s_mem());
669 BIO_write (cert_BIO, raw_cert, strlen(raw_cert));
670 cert = PEM_read_bio_X509 (cert_BIO, NULL, NULL, NULL);
671 if (!cert) {
672 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot read certificate from CERTINFO information - BIO error"));
673 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
674 }
675 BIO_free (cert_BIO);
676 result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit);
677 return result;
678#else /* USE_OPENSSL */
679 /* We assume we don't have OpenSSL and np_net_ssl_check_certificate at our disposal,
680 * so we use the libcurl CURLINFO data
681 */
682 result = net_noopenssl_check_certificate(&cert_ptr, days_till_exp_warn, days_till_exp_crit);
683 return result;
684#endif /* USE_OPENSSL */
685 } else {
686 snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"),
687 res, curl_easy_strerror(res));
688 die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg);
689 }
690 }
691 }
692 }
693#endif /* LIBCURL_FEATURE_SSL */
694
695 /* we got the data and we executed the request in a given time, so we can append
696 * performance data to the answer always
697 */
698 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &total_time), "CURLINFO_TOTAL_TIME");
699 if(show_extended_perfdata) {
700 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &time_connect), "CURLINFO_CONNECT_TIME");
701 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &time_appconnect), "CURLINFO_APPCONNECT_TIME");
702 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &time_headers), "CURLINFO_PRETRANSFER_TIME");
703 handle_curl_option_return_code (curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &time_firstbyte), "CURLINFO_STARTTRANSFER_TIME");
704 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;; size=%dB;;; time_connect=%.6gs;;;; %s time_headers=%.6gs;;;; time_firstbyte=%.6gs;;;; time_transfer=%.6gs;;;;",
705 total_time,
706 warning_thresholds != NULL ? (double)thlds->warning->end : 0.0,
707 critical_thresholds != NULL ? (double)thlds->critical->end : 0.0,
708 (int)body_buf.buflen,
709 time_connect,
710 use_ssl == TRUE ? perfd_time_ssl(time_appconnect-time_connect) : "",
711 (time_headers - time_appconnect),
712 (time_firstbyte - time_headers),
713 (total_time-time_firstbyte)
714 );
715 } else {
716 snprintf(perfstring, DEFAULT_BUFFER_SIZE, "time=%.6gs;%.6g;%.6g;; size=%dB;;;",
717 total_time,
718 warning_thresholds != NULL ? (double)thlds->warning->end : 0.0,
719 critical_thresholds != NULL ? (double)thlds->critical->end : 0.0,
720 (int)body_buf.buflen);
721 }
722
723 /* return a CRITICAL status if we couldn't read any data */
724 if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0)
725 die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n"));
726
727 /* get status line of answer, check sanity of HTTP code */
728 if (curlhelp_parse_statusline (header_buf.buf, &status_line) < 0) {
729 snprintf (msg, DEFAULT_BUFFER_SIZE, "Unparseable status line in %.3g seconds response time|%s\n",
730 total_time, perfstring);
731 die (STATE_CRITICAL, "HTTP CRITICAL HTTP/1.x %ld unknown - %s", code, msg);
732 }
733
734 /* get result code from cURL */
735 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &code), "CURLINFO_RESPONSE_CODE");
736 if (verbose>=2)
737 printf ("* curl CURLINFO_RESPONSE_CODE is %ld\n", code);
738
739 /* print status line, header, body if verbose */
740 if (verbose >= 2) {
741 printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header_buf.buf,
742 (no_body ? " [[ skipped ]]" : body_buf.buf));
743 }
744
745 /* make sure the status line matches the response we are looking for */
746 if (!expected_statuscode(status_line.first_line, server_expect)) {
747 /* TODO: fix first_line being cut off */
748 if (server_port == HTTP_PORT)
749 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host: %s\n"), status_line.first_line);
750 else
751 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Invalid HTTP response received from host on port %d: %s\n"), server_port, status_line.first_line);
752 die (STATE_CRITICAL, "HTTP CRITICAL - %s", msg);
753 }
754
755 /* TODO: implement -d header tests */
756 if( server_expect_yn ) {
757 snprintf(msg, DEFAULT_BUFFER_SIZE, _("Status line output matched \"%s\" - "), server_expect);
758 if (verbose)
759 printf ("%s\n",msg);
760 result = STATE_OK;
761 }
762 else {
763 /* illegal return codes result in a critical state */
764 if (code >= 600 || code < 100) {
765 die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%d, %.40s)\n"), status_line.http_code, status_line.msg);
766 /* server errors result in a critical state */
767 } else if (code >= 500) {
768 result = STATE_CRITICAL;
769 /* client errors result in a warning state */
770 } else if (code >= 400) {
771 result = STATE_WARNING;
772 /* check redirected page if specified */
773 } else if (code >= 300) {
774 if (onredirect == STATE_DEPENDENT) {
775 if( followmethod == FOLLOW_LIBCURL ) {
776 code = status_line.http_code;
777 } else {
778 /* old check_http style redirection, if we come
779 * back here, we are in the same status as with
780 * the libcurl method
781 */
782 redir (&header_buf);
783 }
784 } else {
785 /* this is a specific code in the command line to
786 * be returned when a redirection is encoutered
787 */
788 }
789 result = max_state_alt (onredirect, result);
790 /* all other codes are considered ok */
791 } else {
792 result = STATE_OK;
793 }
794 }
795
796 /* libcurl redirection internally, handle error states here */
797 if( followmethod == FOLLOW_LIBCURL ) {
798 handle_curl_option_return_code (curl_easy_getinfo (curl, CURLINFO_REDIRECT_COUNT, &redir_depth), "CURLINFO_REDIRECT_COUNT");
799 if (verbose >= 2)
800 printf(_("* curl LIBINFO_REDIRECT_COUNT is %d\n"), redir_depth);
801 if (redir_depth > max_depth) {
802 snprintf (msg, DEFAULT_BUFFER_SIZE, "maximum redirection depth %d exceeded in libcurl",
803 max_depth);
804 die (STATE_WARNING, "HTTP WARNING - %s", msg);
805 }
806 }
807
808 /* check status codes, set exit status accordingly */
809 if( status_line.http_code != code ) {
810 die (STATE_CRITICAL, _("HTTP CRITICAL HTTP/%d.%d %d %s - different HTTP codes (cUrl has %ld)\n"),
811 status_line.http_major, status_line.http_minor,
812 status_line.http_code, status_line.msg, code);
813 }
814
815 if (maximum_age >= 0) {
816 result = max_state_alt(check_document_dates(&header_buf, &msg), result);
817 }
818
819 /* Page and Header content checks go here */
820
821 if (strlen (header_expect)) {
822 if (!strstr (header_buf.buf, header_expect)) {
823 strncpy(&output_header_search[0],header_expect,sizeof(output_header_search));
824 if(output_header_search[sizeof(output_header_search)-1]!='\0') {
825 bcopy("...",&output_header_search[sizeof(output_header_search)-4],4);
826 }
827 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%sheader '%s' not found on '%s://%s:%d%s', "), msg, output_header_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
828 result = STATE_CRITICAL;
829 }
830 }
831
832 if (strlen (string_expect)) {
833 if (!strstr (body_buf.buf, string_expect)) {
834 strncpy(&output_string_search[0],string_expect,sizeof(output_string_search));
835 if(output_string_search[sizeof(output_string_search)-1]!='\0') {
836 bcopy("...",&output_string_search[sizeof(output_string_search)-4],4);
837 }
838 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%sstring '%s' not found on '%s://%s:%d%s', "), msg, output_string_search, use_ssl ? "https" : "http", host_name ? host_name : server_address, server_port, server_url);
839 result = STATE_CRITICAL;
840 }
841 }
842
843 if (strlen (regexp)) {
844 errcode = regexec (&preg, body_buf.buf, REGS, pmatch, 0);
845 if ((errcode == 0 && invert_regex == 0) || (errcode == REG_NOMATCH && invert_regex == 1)) {
846 /* OK - No-op to avoid changing the logic around it */
847 result = max_state_alt(STATE_OK, result);
848 }
849 else if ((errcode == REG_NOMATCH && invert_regex == 0) || (errcode == 0 && invert_regex == 1)) {
850 if (invert_regex == 0)
851 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern not found, "), msg);
852 else
853 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spattern found, "), msg);
854 result = STATE_CRITICAL;
855 }
856 else {
857 /* FIXME: Shouldn't that be UNKNOWN? */
858 regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
859 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%sExecute Error: %s, "), msg, errbuf);
860 result = STATE_CRITICAL;
861 }
862 }
863
864 /* make sure the page is of an appropriate size
865 * TODO: as far I can tell check_http gets the full size of header and
866 * if -N is not given header+body. Does this make sense?
867 *
868 * TODO: check_http.c had a get_length function, the question is really
869 * here what to use? the raw data size of the header_buf, the value of
870 * Content-Length, both and warn if they differ? Should the length be
871 * header+body or only body?
872 *
873 * One possible policy:
874 * - use header_buf.buflen (warning, if it mismatches to the Content-Length value
875 * - if -N (nobody) is given, use Content-Length only and hope the server set
876 * the value correcly
877 */
878 page_len = get_content_length(&header_buf, &body_buf);
879 if ((max_page_len > 0) && (page_len > max_page_len)) {
880 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too large, "), msg, page_len);
881 result = max_state_alt(STATE_WARNING, result);
882 } else if ((min_page_len > 0) && (page_len < min_page_len)) {
883 snprintf (msg, DEFAULT_BUFFER_SIZE, _("%spage size %d too small, "), msg, page_len);
884 result = max_state_alt(STATE_WARNING, result);
885 }
886
887 /* -w, -c: check warning and critical level */
888 result = max_state_alt(get_status(total_time, thlds), result);
889
890 /* Cut-off trailing characters */
891 if(msg[strlen(msg)-2] == ',')
892 msg[strlen(msg)-2] = '\0';
893 else
894 msg[strlen(msg)-3] = '\0';
895
896 /* TODO: separate _() msg and status code: die (result, "HTTP %s: %s\n", state_text(result), msg); */
897 die (result, "HTTP %s: HTTP/%d.%d %d %s%s%s - %d bytes in %.3f second response time %s|%s\n",
898 state_text(result), status_line.http_major, status_line.http_minor,
899 status_line.http_code, status_line.msg,
900 strlen(msg) > 0 ? " - " : "",
901 msg, page_len, total_time,
902 (display_html ? "</A>" : ""),
903 perfstring);
904
905 /* proper cleanup after die? */
906 curlhelp_free_statusline(&status_line);
907 curl_easy_cleanup (curl);
908 curl_global_cleanup ();
909 curlhelp_freewritebuffer (&body_buf);
910 curlhelp_freewritebuffer (&header_buf);
911 if (!strcmp (http_method, "PUT")) {
912 curlhelp_freereadbuffer (&put_buf);
913 }
914
915 return result;
916}
917
918int
919uri_strcmp (const UriTextRangeA range, const char* s)
920{
921 if (!range.first) return -1;
922 if (range.afterLast - range.first < strlen (s)) return -1;
923 return strncmp (s, range.first, min( range.afterLast - range.first, strlen (s)));
924}
925
926char*
927uri_string (const UriTextRangeA range, char* buf, size_t buflen)
928{
929 if (!range.first) return "(null)";
930 strncpy (buf, range.first, max (buflen, range.afterLast - range.first));
931 buf[max (buflen, range.afterLast - range.first)] = '\0';
932 buf[range.afterLast - range.first] = '\0';
933 return buf;
934}
935
936void
937redir (curlhelp_write_curlbuf* header_buf)
938{
939 char *location = NULL;
940 curlhelp_statusline status_line;
941 struct phr_header headers[255];
942 size_t nof_headers = 255;
943 size_t msglen;
944 char buf[DEFAULT_BUFFER_SIZE];
945 char ipstr[INET_ADDR_MAX_SIZE];
946 int new_port;
947 char *new_host;
948 char *new_url;
949
950 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
951 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
952 headers, &nof_headers, 0);
953
954 location = get_header_value (headers, nof_headers, "location");
955
956 if (verbose >= 2)
957 printf(_("* Seen redirect location %s\n"), location);
958
959 if (++redir_depth > max_depth)
960 die (STATE_WARNING,
961 _("HTTP WARNING - maximum redirection depth %d exceeded - %s%s\n"),
962 max_depth, location, (display_html ? "</A>" : ""));
963
964 UriParserStateA state;
965 UriUriA uri;
966 state.uri = &uri;
967 if (uriParseUriA (&state, location) != URI_SUCCESS) {
968 if (state.errorCode == URI_ERROR_SYNTAX) {
969 die (STATE_UNKNOWN,
970 _("HTTP UNKNOWN - Could not parse redirect location '%s'%s\n"),
971 location, (display_html ? "</A>" : ""));
972 } else if (state.errorCode == URI_ERROR_MALLOC) {
973 die (STATE_UNKNOWN, _("HTTP UNKNOWN - Could not allocate URL\n"));
974 }
975 }
976
977 if (verbose >= 2) {
978 printf (_("** scheme: %s\n"),
979 uri_string (uri.scheme, buf, DEFAULT_BUFFER_SIZE));
980 printf (_("** host: %s\n"),
981 uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
982 printf (_("** port: %s\n"),
983 uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
984 if (uri.hostData.ip4) {
985 inet_ntop (AF_INET, uri.hostData.ip4->data, ipstr, sizeof (ipstr));
986 printf (_("** IPv4: %s\n"), ipstr);
987 }
988 if (uri.hostData.ip6) {
989 inet_ntop (AF_INET, uri.hostData.ip6->data, ipstr, sizeof (ipstr));
990 printf (_("** IPv6: %s\n"), ipstr);
991 }
992 if (uri.pathHead) {
993 printf (_("** path: "));
994 const UriPathSegmentA* p = uri.pathHead;
995 for (; p; p = p->next) {
996 printf ("/%s", uri_string (p->text, buf, DEFAULT_BUFFER_SIZE));
997 }
998 puts ("");
999 }
1000 if (uri.query.first) {
1001 printf (_("** query: %s\n"),
1002 uri_string (uri.query, buf, DEFAULT_BUFFER_SIZE));
1003 }
1004 if (uri.fragment.first) {
1005 printf (_("** fragment: %s\n"),
1006 uri_string (uri.fragment, buf, DEFAULT_BUFFER_SIZE));
1007 }
1008 }
1009
1010 use_ssl = !uri_strcmp (uri.scheme, "https");
1011
1012 /* we do a sloppy test here only, because uriparser would have failed
1013 * above, if the port would be invalid, we just check for MAX_PORT
1014 */
1015 if (uri.portText.first) {
1016 new_port = atoi (uri_string (uri.portText, buf, DEFAULT_BUFFER_SIZE));
1017 } else {
1018 new_port = HTTP_PORT;
1019 if (use_ssl)
1020 new_port = HTTPS_PORT;
1021 }
1022 if (new_port > MAX_PORT)
1023 die (STATE_UNKNOWN,
1024 _("HTTP UNKNOWN - Redirection to port above %d - %s%s\n"),
1025 MAX_PORT, location, display_html ? "</A>" : "");
1026
1027 /* by RFC 7231 relative URLs in Location should be taken relative to
1028 * the original URL, so wy try to form a new absolute URL here
1029 */
1030 if (!uri.scheme.first && !uri.hostText.first) {
1031 /* TODO: implement */
1032 die (STATE_UNKNOWN, _("HTTP UNKNOWN - non-absolute location, not implemented yet!\n"));
1033 new_host = strdup (host_name ? host_name : server_address);
1034 } else {
1035 new_host = strdup (uri_string (uri.hostText, buf, DEFAULT_BUFFER_SIZE));
1036 }
1037
1038 /* compose new path */
1039 /* TODO: handle fragments and query part of URL */
1040 new_url = (char *)calloc( 1, DEFAULT_BUFFER_SIZE);
1041 if (uri.pathHead) {
1042 const UriPathSegmentA* p = uri.pathHead;
1043 for (; p; p = p->next) {
1044 strncat (new_url, "/", DEFAULT_BUFFER_SIZE);
1045 strncat (new_url, uri_string (p->text, buf, DEFAULT_BUFFER_SIZE), DEFAULT_BUFFER_SIZE);
1046 }
1047 }
1048
1049 if (server_port==new_port &&
1050 !strncmp(server_address, new_host, MAX_IPV4_HOSTLENGTH) &&
1051 (host_name && !strncmp(host_name, new_host, MAX_IPV4_HOSTLENGTH)) &&
1052 !strcmp(server_url, new_url))
1053 die (STATE_WARNING,
1054 _("HTTP WARNING - redirection creates an infinite loop - %s://%s:%d%s%s\n"),
1055 use_ssl ? "https" : "http", new_host, new_port, new_url, (display_html ? "</A>" : ""));
1056
1057 /* set new values for redirected request */
1058
1059 if (!(followsticky & STICKY_HOST)) {
1060 free (server_address);
1061 server_address = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1062 }
1063 if (!(followsticky & STICKY_PORT)) {
1064 server_port = (unsigned short)new_port;
1065 }
1066
1067 free (host_name);
1068 host_name = strndup (new_host, MAX_IPV4_HOSTLENGTH);
1069
1070 /* reset virtual port */
1071 virtual_port = server_port;
1072
1073 free(new_host);
1074 free (server_url);
1075 server_url = new_url;
1076
1077 uriFreeUriMembersA (&uri);
1078
1079 if (verbose)
1080 printf (_("Redirection to %s://%s:%d%s\n"), use_ssl ? "https" : "http",
1081 host_name ? host_name : server_address, server_port, server_url);
1082
1083 /* TODO: the hash component MUST be taken from the original URL and
1084 * attached to the URL in Location
1085 */
1086
1087 check_http ();
1088}
1089
1090#if 0
1091
1092int main(int argc, char *argv[]) {
1093
1094 for (; i < argc; i++) {
1095
1096 }
1097 printf("\n");
1098#endif
1099
1100/* check whether a file exists */
1101void
1102test_file (char *path)
1103{
1104 if (access(path, R_OK) == 0)
1105 return;
1106 usage2 (_("file does not exist or is not readable"), path);
1107}
1108
1109int
1110process_arguments (int argc, char **argv)
1111{
1112 char *p;
1113 int c = 1;
1114 char *temp;
1115
1116 enum {
1117 INVERT_REGEX = CHAR_MAX + 1,
1118 SNI_OPTION,
1119 CA_CERT_OPTION
1120 };
1121
1122 int option = 0;
1123 int got_plus = 0;
1124 static struct option longopts[] = {
1125 STD_LONG_OPTS,
1126 {"link", no_argument, 0, 'L'},
1127 {"nohtml", no_argument, 0, 'n'},
1128 {"ssl", optional_argument, 0, 'S'},
1129 {"sni", no_argument, 0, SNI_OPTION},
1130 {"post", required_argument, 0, 'P'},
1131 {"method", required_argument, 0, 'j'},
1132 {"IP-address", required_argument, 0, 'I'},
1133 {"url", required_argument, 0, 'u'},
1134 {"port", required_argument, 0, 'p'},
1135 {"authorization", required_argument, 0, 'a'},
1136 {"proxy-authorization", required_argument, 0, 'b'},
1137 {"header-string", required_argument, 0, 'd'},
1138 {"string", required_argument, 0, 's'},
1139 {"expect", required_argument, 0, 'e'},
1140 {"regex", required_argument, 0, 'r'},
1141 {"ereg", required_argument, 0, 'r'},
1142 {"eregi", required_argument, 0, 'R'},
1143 {"linespan", no_argument, 0, 'l'},
1144 {"onredirect", required_argument, 0, 'f'},
1145 {"certificate", required_argument, 0, 'C'},
1146 {"client-cert", required_argument, 0, 'J'},
1147 {"private-key", required_argument, 0, 'K'},
1148 {"ca-cert", required_argument, 0, CA_CERT_OPTION},
1149 {"useragent", required_argument, 0, 'A'},
1150 {"header", required_argument, 0, 'k'},
1151 {"no-body", no_argument, 0, 'N'},
1152 {"max-age", required_argument, 0, 'M'},
1153 {"content-type", required_argument, 0, 'T'},
1154 {"pagesize", required_argument, 0, 'm'},
1155 {"invert-regex", no_argument, NULL, INVERT_REGEX},
1156 {"use-ipv4", no_argument, 0, '4'},
1157 {"use-ipv6", no_argument, 0, '6'},
1158 {"extended-perfdata", no_argument, 0, 'E'},
1159 {0, 0, 0, 0}
1160 };
1161
1162 if (argc < 2)
1163 return ERROR;
1164
1165 /* support check_http compatible arguments */
1166 for (c = 1; c < argc; c++) {
1167 if (strcmp ("-to", argv[c]) == 0)
1168 strcpy (argv[c], "-t");
1169 if (strcmp ("-hn", argv[c]) == 0)
1170 strcpy (argv[c], "-H");
1171 if (strcmp ("-wt", argv[c]) == 0)
1172 strcpy (argv[c], "-w");
1173 if (strcmp ("-ct", argv[c]) == 0)
1174 strcpy (argv[c], "-c");
1175 if (strcmp ("-nohtml", argv[c]) == 0)
1176 strcpy (argv[c], "-n");
1177 }
1178
1179 while (1) {
1180 c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:j:T:I:a:b:d:e:p:s:R:r:u:f:C:J:K:nlLS::m:M:NE", longopts, &option);
1181 if (c == -1 || c == EOF || c == 1)
1182 break;
1183
1184 switch (c) {
1185 case 'h':
1186 print_help();
1187 exit(STATE_UNKNOWN);
1188 break;
1189 case 'V':
1190 print_revision(progname, NP_VERSION);
1191 print_curl_version();
1192 exit(STATE_UNKNOWN);
1193 break;
1194 case 'v':
1195 verbose++;
1196 break;
1197 case 't': /* timeout period */
1198 if (!is_intnonneg (optarg))
1199 usage2 (_("Timeout interval must be a positive integer"), optarg);
1200 else
1201 socket_timeout = (int)strtol (optarg, NULL, 10);
1202 break;
1203 case 'c': /* critical time threshold */
1204 critical_thresholds = optarg;
1205 break;
1206 case 'w': /* warning time threshold */
1207 warning_thresholds = optarg;
1208 break;
1209 case 'H': /* virtual host */
1210 host_name = strdup (optarg);
1211 if (host_name[0] == '[') {
1212 if ((p = strstr (host_name, "]:")) != NULL) { /* [IPv6]:port */
1213 virtual_port = atoi (p + 2);
1214 /* cut off the port */
1215 host_name_length = strlen (host_name) - strlen (p) - 1;
1216 free (host_name);
1217 host_name = strndup (optarg, host_name_length);
1218 }
1219 } else if ((p = strchr (host_name, ':')) != NULL
1220 && strchr (++p, ':') == NULL) { /* IPv4:port or host:port */
1221 virtual_port = atoi (p);
1222 /* cut off the port */
1223 host_name_length = strlen (host_name) - strlen (p) - 1;
1224 free (host_name);
1225 host_name = strndup (optarg, host_name_length);
1226 }
1227 break;
1228 case 'I': /* internet address */
1229 server_address = strdup (optarg);
1230 break;
1231 case 'u': /* URL path */
1232 server_url = strdup (optarg);
1233 break;
1234 case 'p': /* Server port */
1235 if (!is_intnonneg (optarg))
1236 usage2 (_("Invalid port number, expecting a non-negative number"), optarg);
1237 else {
1238 if( strtol(optarg, NULL, 10) > MAX_PORT)
1239 usage2 (_("Invalid port number, supplied port number is too big"), optarg);
1240 server_port = (unsigned short)strtol(optarg, NULL, 10);
1241 }
1242 break;
1243 case 'a': /* authorization info */
1244 strncpy (user_auth, optarg, MAX_INPUT_BUFFER - 1);
1245 user_auth[MAX_INPUT_BUFFER - 1] = 0;
1246 break;
1247 case 'b': /* proxy-authorization info */
1248 strncpy (proxy_auth, optarg, MAX_INPUT_BUFFER - 1);
1249 proxy_auth[MAX_INPUT_BUFFER - 1] = 0;
1250 break;
1251 case 'P': /* HTTP POST data in URL encoded format; ignored if settings already */
1252 if (! http_post_data)
1253 http_post_data = strdup (optarg);
1254 if (! http_method)
1255 http_method = strdup("POST");
1256 break;
1257 case 'j': /* Set HTTP method */
1258 if (http_method)
1259 free(http_method);
1260 http_method = strdup (optarg);
1261 break;
1262 case 'A': /* useragent */
1263 strncpy (user_agent, optarg, DEFAULT_BUFFER_SIZE);
1264 user_agent[DEFAULT_BUFFER_SIZE-1] = '\0';
1265 break;
1266 case 'k': /* Additional headers */
1267 if (http_opt_headers_count == 0)
1268 http_opt_headers = malloc (sizeof (char *) * (++http_opt_headers_count));
1269 else
1270 http_opt_headers = realloc (http_opt_headers, sizeof (char *) * (++http_opt_headers_count));
1271 http_opt_headers[http_opt_headers_count - 1] = optarg;
1272 break;
1273 case 'L': /* show html link */
1274 display_html = TRUE;
1275 break;
1276 case 'n': /* do not show html link */
1277 display_html = FALSE;
1278 break;
1279 case 'C': /* Check SSL cert validity */
1280#ifdef LIBCURL_FEATURE_SSL
1281 if ((temp=strchr(optarg,','))!=NULL) {
1282 *temp='\0';
1283 if (!is_intnonneg (optarg))
1284 usage2 (_("Invalid certificate expiration period"), optarg);
1285 days_till_exp_warn = atoi(optarg);
1286 *temp=',';
1287 temp++;
1288 if (!is_intnonneg (temp))
1289 usage2 (_("Invalid certificate expiration period"), temp);
1290 days_till_exp_crit = atoi (temp);
1291 }
1292 else {
1293 days_till_exp_crit=0;
1294 if (!is_intnonneg (optarg))
1295 usage2 (_("Invalid certificate expiration period"), optarg);
1296 days_till_exp_warn = atoi (optarg);
1297 }
1298 check_cert = TRUE;
1299 goto enable_ssl;
1300#endif
1301 case 'J': /* use client certificate */
1302#ifdef LIBCURL_FEATURE_SSL
1303 test_file(optarg);
1304 client_cert = optarg;
1305 goto enable_ssl;
1306#endif
1307 case 'K': /* use client private key */
1308#ifdef LIBCURL_FEATURE_SSL
1309 test_file(optarg);
1310 client_privkey = optarg;
1311 goto enable_ssl;
1312#endif
1313#ifdef LIBCURL_FEATURE_SSL
1314 case CA_CERT_OPTION: /* use CA chain file */
1315 test_file(optarg);
1316 ca_cert = optarg;
1317 goto enable_ssl;
1318#endif
1319 case 'S': /* use SSL */
1320#ifdef LIBCURL_FEATURE_SSL
1321 enable_ssl:
1322 use_ssl = TRUE;
1323 /* ssl_version initialized to CURL_SSLVERSION_TLSv1_0 as a default.
1324 * Only set if it's non-zero. This helps when we include multiple
1325 * parameters, like -S and -C combinations */
1326 ssl_version = CURL_SSLVERSION_TLSv1_0;
1327 if (c=='S' && optarg != NULL) {
1328 char *plus_ptr = strchr(optarg, '+');
1329 if (plus_ptr) {
1330 got_plus = 1;
1331 *plus_ptr = '\0';
1332 }
1333
1334 if (optarg[0] == '2')
1335 ssl_version = CURL_SSLVERSION_SSLv2;
1336 else if (optarg[0] == '3')
1337 ssl_version = CURL_SSLVERSION_SSLv3;
1338 else if (!strcmp (optarg, "1") || !strcmp (optarg, "1.0"))
1339#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1340 ssl_version = CURL_SSLVERSION_TLSv1_0;
1341#else
1342 ssl_version = CURL_SSLVERSION_DEFAULT;
1343#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1344 else if (!strcmp (optarg, "1.1"))
1345#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1346 ssl_version = CURL_SSLVERSION_TLSv1_1;
1347#else
1348 ssl_version = CURL_SSLVERSION_DEFAULT;
1349#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1350 else if (!strcmp (optarg, "1.2"))
1351#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0)
1352 ssl_version = CURL_SSLVERSION_TLSv1_2;
1353#else
1354 ssl_version = CURL_SSLVERSION_DEFAULT;
1355#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */
1356 else if (!strcmp (optarg, "1.3"))
1357#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0)
1358 ssl_version = CURL_SSLVERSION_TLSv1_3;
1359#else
1360 ssl_version = CURL_SSLVERSION_DEFAULT;
1361#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 52, 0) */
1362 else
1363 usage4 (_("Invalid option - Valid SSL/TLS versions: 2, 3, 1, 1.1, 1.2 (with optional '+' suffix)"));
1364 }
1365#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0)
1366 if (got_plus) {
1367 switch (ssl_version) {
1368 case CURL_SSLVERSION_TLSv1_3:
1369 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1370 break;
1371 case CURL_SSLVERSION_TLSv1_2:
1372 case CURL_SSLVERSION_TLSv1_1:
1373 case CURL_SSLVERSION_TLSv1_0:
1374 ssl_version |= CURL_SSLVERSION_MAX_DEFAULT;
1375 break;
1376 }
1377 } else {
1378 switch (ssl_version) {
1379 case CURL_SSLVERSION_TLSv1_3:
1380 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_3;
1381 break;
1382 case CURL_SSLVERSION_TLSv1_2:
1383 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_2;
1384 break;
1385 case CURL_SSLVERSION_TLSv1_1:
1386 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_1;
1387 break;
1388 case CURL_SSLVERSION_TLSv1_0:
1389 ssl_version |= CURL_SSLVERSION_MAX_TLSv1_0;
1390 break;
1391 }
1392 }
1393#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 54, 0) */
1394 if (verbose >= 2)
1395 printf(_("* Set SSL/TLS version to %d\n"), ssl_version);
1396 if (server_port == HTTP_PORT)
1397 server_port = HTTPS_PORT;
1398 break;
1399#else /* LIBCURL_FEATURE_SSL */
1400 /* -C -J and -K fall through to here without SSL */
1401 usage4 (_("Invalid option - SSL is not available"));
1402 break;
1403 case SNI_OPTION: /* --sni is parsed, but ignored, the default is TRUE with libcurl */
1404 use_sni = TRUE;
1405 break;
1406#endif /* LIBCURL_FEATURE_SSL */
1407 case 'f': /* onredirect */
1408 if (!strcmp (optarg, "ok"))
1409 onredirect = STATE_OK;
1410 else if (!strcmp (optarg, "warning"))
1411 onredirect = STATE_WARNING;
1412 else if (!strcmp (optarg, "critical"))
1413 onredirect = STATE_CRITICAL;
1414 else if (!strcmp (optarg, "unknown"))
1415 onredirect = STATE_UNKNOWN;
1416 else if (!strcmp (optarg, "follow"))
1417 onredirect = STATE_DEPENDENT;
1418 else if (!strcmp (optarg, "stickyport"))
1419 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST|STICKY_PORT;
1420 else if (!strcmp (optarg, "sticky"))
1421 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_HOST;
1422 else if (!strcmp (optarg, "follow"))
1423 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_HTTP_CURL, followsticky = STICKY_NONE;
1424 else if (!strcmp (optarg, "curl"))
1425 onredirect = STATE_DEPENDENT, followmethod = FOLLOW_LIBCURL;
1426 else usage2 (_("Invalid onredirect option"), optarg);
1427 if (verbose >= 2)
1428 printf(_("* Following redirects set to %s\n"), state_text(onredirect));
1429 break;
1430 case 'd': /* string or substring */
1431 strncpy (header_expect, optarg, MAX_INPUT_BUFFER - 1);
1432 header_expect[MAX_INPUT_BUFFER - 1] = 0;
1433 break;
1434 case 's': /* string or substring */
1435 strncpy (string_expect, optarg, MAX_INPUT_BUFFER - 1);
1436 string_expect[MAX_INPUT_BUFFER - 1] = 0;
1437 break;
1438 case 'e': /* string or substring */
1439 strncpy (server_expect, optarg, MAX_INPUT_BUFFER - 1);
1440 server_expect[MAX_INPUT_BUFFER - 1] = 0;
1441 server_expect_yn = 1;
1442 break;
1443 case 'T': /* Content-type */
1444 http_content_type = strdup (optarg);
1445 break;
1446 case 'l': /* linespan */
1447 cflags &= ~REG_NEWLINE;
1448 break;
1449 case 'R': /* regex */
1450 cflags |= REG_ICASE;
1451 case 'r': /* regex */
1452 strncpy (regexp, optarg, MAX_RE_SIZE - 1);
1453 regexp[MAX_RE_SIZE - 1] = 0;
1454 errcode = regcomp (&preg, regexp, cflags);
1455 if (errcode != 0) {
1456 (void) regerror (errcode, &preg, errbuf, MAX_INPUT_BUFFER);
1457 printf (_("Could Not Compile Regular Expression: %s"), errbuf);
1458 return ERROR;
1459 }
1460 break;
1461 case INVERT_REGEX:
1462 invert_regex = 1;
1463 break;
1464 case '4':
1465 address_family = AF_INET;
1466 break;
1467 case '6':
1468#if defined (USE_IPV6) && defined (LIBCURL_FEATURE_IPV6)
1469 address_family = AF_INET6;
1470#else
1471 usage4 (_("IPv6 support not available"));
1472#endif
1473 break;
1474 case 'm': /* min_page_length */
1475 {
1476 char *tmp;
1477 if (strchr(optarg, ':') != (char *)NULL) {
1478 /* range, so get two values, min:max */
1479 tmp = strtok(optarg, ":");
1480 if (tmp == NULL) {
1481 printf("Bad format: try \"-m min:max\"\n");
1482 exit (STATE_WARNING);
1483 } else
1484 min_page_len = atoi(tmp);
1485
1486 tmp = strtok(NULL, ":");
1487 if (tmp == NULL) {
1488 printf("Bad format: try \"-m min:max\"\n");
1489 exit (STATE_WARNING);
1490 } else
1491 max_page_len = atoi(tmp);
1492 } else
1493 min_page_len = atoi (optarg);
1494 break;
1495 }
1496 case 'N': /* no-body */
1497 no_body = TRUE;
1498 break;
1499 case 'M': /* max-age */
1500 {
1501 int L = strlen(optarg);
1502 if (L && optarg[L-1] == 'm')
1503 maximum_age = atoi (optarg) * 60;
1504 else if (L && optarg[L-1] == 'h')
1505 maximum_age = atoi (optarg) * 60 * 60;
1506 else if (L && optarg[L-1] == 'd')
1507 maximum_age = atoi (optarg) * 60 * 60 * 24;
1508 else if (L && (optarg[L-1] == 's' ||
1509 isdigit (optarg[L-1])))
1510 maximum_age = atoi (optarg);
1511 else {
1512 fprintf (stderr, "unparsable max-age: %s\n", optarg);
1513 exit (STATE_WARNING);
1514 }
1515 if (verbose >= 2)
1516 printf ("* Maximal age of document set to %d seconds\n", maximum_age);
1517 }
1518 break;
1519 case 'E': /* show extended perfdata */
1520 show_extended_perfdata = TRUE;
1521 break;
1522 case '?':
1523 /* print short usage statement if args not parsable */
1524 usage5 ();
1525 break;
1526 }
1527 }
1528
1529 c = optind;
1530
1531 if (server_address == NULL && c < argc)
1532 server_address = strdup (argv[c++]);
1533
1534 if (host_name == NULL && c < argc)
1535 host_name = strdup (argv[c++]);
1536
1537 if (server_address == NULL) {
1538 if (host_name == NULL)
1539 usage4 (_("You must specify a server address or host name"));
1540 else
1541 server_address = strdup (host_name);
1542 }
1543
1544 set_thresholds(&thlds, warning_thresholds, critical_thresholds);
1545
1546 if (critical_thresholds && thlds->critical->end>(double)socket_timeout)
1547 socket_timeout = (int)thlds->critical->end + 1;
1548 if (verbose >= 2)
1549 printf ("* Socket timeout set to %ld seconds\n", socket_timeout);
1550
1551 if (http_method == NULL)
1552 http_method = strdup ("GET");
1553
1554 if (client_cert && !client_privkey)
1555 usage4 (_("If you use a client certificate you must also specify a private key file"));
1556
1557 if (virtual_port == 0)
1558 virtual_port = server_port;
1559
1560 return TRUE;
1561}
1562
1563void
1564print_help (void)
1565{
1566 print_revision (progname, NP_VERSION);
1567
1568 printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
1569 printf (COPYRIGHT, copyright, email);
1570
1571 printf ("%s\n", _("This plugin tests the HTTP service on the specified host. It can test"));
1572 printf ("%s\n", _("normal (http) and secure (https) servers, follow redirects, search for"));
1573 printf ("%s\n", _("strings and regular expressions, check connection times, and report on"));
1574 printf ("%s\n", _("certificate expiration times."));
1575 printf ("\n");
1576 printf ("%s\n", _("It makes use of libcurl to do so. It tries to be as compatible to check_http"));
1577 printf ("%s\n", _("as possible."));
1578
1579 printf ("\n\n");
1580
1581 print_usage ();
1582
1583 printf (_("NOTE: One or both of -H and -I must be specified"));
1584
1585 printf ("\n");
1586
1587 printf (UT_HELP_VRSN);
1588 printf (UT_EXTRA_OPTS);
1589
1590 printf (" %s\n", "-H, --hostname=ADDRESS");
1591 printf (" %s\n", _("Host name argument for servers using host headers (virtual host)"));
1592 printf (" %s\n", _("Append a port to include it in the header (eg: example.com:5000)"));
1593 printf (" %s\n", "-I, --IP-address=ADDRESS");
1594 printf (" %s\n", _("IP address or name (use numeric address if possible to bypass DNS lookup)."));
1595 printf (" %s\n", "-p, --port=INTEGER");
1596 printf (" %s", _("Port number (default: "));
1597 printf ("%d)\n", HTTP_PORT);
1598
1599 printf (UT_IPv46);
1600
1601#ifdef LIBCURL_FEATURE_SSL
1602 printf (" %s\n", "-S, --ssl=VERSION[+]");
1603 printf (" %s\n", _("Connect via SSL. Port defaults to 443. VERSION is optional, and prevents"));
1604 printf (" %s\n", _("auto-negotiation (2 = SSLv2, 3 = SSLv3, 1 = TLSv1, 1.1 = TLSv1.1,"));
1605 printf (" %s\n", _("1.2 = TLSv1.2). With a '+' suffix, newer versions are also accepted."));
1606 printf (" %s\n", _("Note: SSLv2 and SSLv3 are deprecated and are usually disabled in libcurl"));
1607 printf (" %s\n", "--sni");
1608 printf (" %s\n", _("Enable SSL/TLS hostname extension support (SNI)"));
1609#if LIBCURL_VERSION_NUM >= 0x071801
1610 printf (" %s\n", _("Note: --sni is the default in libcurl as SSLv2 and SSLV3 are deprecated and"));
1611 printf (" %s\n", _(" SNI only really works since TLSv1.0"));
1612#else
1613 printf (" %s\n", _("Note: SNI is not supported in libcurl before 7.18.1"));
1614#endif
1615 printf (" %s\n", "-C, --certificate=INTEGER[,INTEGER]");
1616 printf (" %s\n", _("Minimum number of days a certificate has to be valid. Port defaults to 443"));
1617 printf (" %s\n", _("(when this option is used the URL is not checked.)"));
1618 printf (" %s\n", "-J, --client-cert=FILE");
1619 printf (" %s\n", _("Name of file that contains the client certificate (PEM format)"));
1620 printf (" %s\n", _("to be used in establishing the SSL session"));
1621 printf (" %s\n", "-K, --private-key=FILE");
1622 printf (" %s\n", _("Name of file containing the private key (PEM format)"));
1623 printf (" %s\n", _("matching the client certificate"));
1624 printf (" %s\n", "--ca-cert=FILE");
1625 printf (" %s\n", _("CA certificate file to verify peer against"));
1626#endif
1627
1628 printf (" %s\n", "-e, --expect=STRING");
1629 printf (" %s\n", _("Comma-delimited list of strings, at least one of them is expected in"));
1630 printf (" %s", _("the first (status) line of the server response (default: "));
1631 printf ("%s)\n", HTTP_EXPECT);
1632 printf (" %s\n", _("If specified skips all other status line logic (ex: 3xx, 4xx, 5xx processing)"));
1633 printf (" %s\n", "-d, --header-string=STRING");
1634 printf (" %s\n", _("String to expect in the response headers"));
1635 printf (" %s\n", "-s, --string=STRING");
1636 printf (" %s\n", _("String to expect in the content"));
1637 printf (" %s\n", "-u, --url=PATH");
1638 printf (" %s\n", _("URL to GET or POST (default: /)"));
1639 printf (" %s\n", "-P, --post=STRING");
1640 printf (" %s\n", _("URL encoded http POST data"));
1641 printf (" %s\n", "-j, --method=STRING (for example: HEAD, OPTIONS, TRACE, PUT, DELETE, CONNECT)");
1642 printf (" %s\n", _("Set HTTP method."));
1643 printf (" %s\n", "-N, --no-body");
1644 printf (" %s\n", _("Don't wait for document body: stop reading after headers."));
1645 printf (" %s\n", _("(Note that this still does an HTTP GET or POST, not a HEAD.)"));
1646 printf (" %s\n", "-M, --max-age=SECONDS");
1647 printf (" %s\n", _("Warn if document is more than SECONDS old. the number can also be of"));
1648 printf (" %s\n", _("the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days."));
1649 printf (" %s\n", "-T, --content-type=STRING");
1650 printf (" %s\n", _("specify Content-Type header media type when POSTing\n"));
1651 printf (" %s\n", "-l, --linespan");
1652 printf (" %s\n", _("Allow regex to span newlines (must precede -r or -R)"));
1653 printf (" %s\n", "-r, --regex, --ereg=STRING");
1654 printf (" %s\n", _("Search page for regex STRING"));
1655 printf (" %s\n", "-R, --eregi=STRING");
1656 printf (" %s\n", _("Search page for case-insensitive regex STRING"));
1657 printf (" %s\n", "--invert-regex");
1658 printf (" %s\n", _("Return CRITICAL if found, OK if not\n"));
1659 printf (" %s\n", "-a, --authorization=AUTH_PAIR");
1660 printf (" %s\n", _("Username:password on sites with basic authentication"));
1661 printf (" %s\n", "-b, --proxy-authorization=AUTH_PAIR");
1662 printf (" %s\n", _("Username:password on proxy-servers with basic authentication"));
1663 printf (" %s\n", "-A, --useragent=STRING");
1664 printf (" %s\n", _("String to be sent in http header as \"User Agent\""));
1665 printf (" %s\n", "-k, --header=STRING");
1666 printf (" %s\n", _("Any other tags to be sent in http header. Use multiple times for additional headers"));
1667 printf (" %s\n", "-E, --extended-perfdata");
1668 printf (" %s\n", _("Print additional performance data"));
1669 printf (" %s\n", "-L, --link");
1670 printf (" %s\n", _("Wrap output in HTML link (obsoleted by urlize)"));
1671 printf (" %s\n", "-f, --onredirect=<ok|warning|critical|follow|sticky|stickyport|curl>");
1672 printf (" %s\n", _("How to handle redirected pages. sticky is like follow but stick to the"));
1673 printf (" %s\n", _("specified IP address. stickyport also ensures port stays the same."));
1674 printf (" %s\n", _("follow uses the old redirection algorithm of check_http."));
1675 printf (" %s\n", _("curl uses CURL_FOLLOWLOCATION built into libcurl."));
1676 printf (" %s\n", "-m, --pagesize=INTEGER<:INTEGER>");
1677 printf (" %s\n", _("Minimum page size required (bytes) : Maximum page size required (bytes)"));
1678
1679 printf (UT_WARN_CRIT);
1680
1681 printf (UT_CONN_TIMEOUT, DEFAULT_SOCKET_TIMEOUT);
1682
1683 printf (UT_VERBOSE);
1684
1685 printf ("\n");
1686 printf ("%s\n", _("Notes:"));
1687 printf (" %s\n", _("This plugin will attempt to open an HTTP connection with the host."));
1688 printf (" %s\n", _("Successful connects return STATE_OK, refusals and timeouts return STATE_CRITICAL"));
1689 printf (" %s\n", _("other errors return STATE_UNKNOWN. Successful connects, but incorrect response"));
1690 printf (" %s\n", _("messages from the host result in STATE_WARNING return values. If you are"));
1691 printf (" %s\n", _("checking a virtual server that uses 'host headers' you must supply the FQDN"));
1692 printf (" %s\n", _("(fully qualified domain name) as the [host_name] argument."));
1693
1694#ifdef LIBCURL_FEATURE_SSL
1695 printf ("\n");
1696 printf (" %s\n", _("This plugin can also check whether an SSL enabled web server is able to"));
1697 printf (" %s\n", _("serve content (optionally within a specified time) or whether the X509 "));
1698 printf (" %s\n", _("certificate is still valid for the specified number of days."));
1699 printf ("\n");
1700 printf (" %s\n", _("Please note that this plugin does not check if the presented server"));
1701 printf (" %s\n", _("certificate matches the hostname of the server, or if the certificate"));
1702 printf (" %s\n", _("has a valid chain of trust to one of the locally installed CAs."));
1703 printf ("\n");
1704 printf ("%s\n", _("Examples:"));
1705 printf (" %s\n\n", "CHECK CONTENT: check_curl -w 5 -c 10 --ssl -H www.verisign.com");
1706 printf (" %s\n", _("When the 'www.verisign.com' server returns its content within 5 seconds,"));
1707 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1708 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1709 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1710 printf ("\n");
1711 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 14");
1712 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 14 days,"));
1713 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1714 printf (" %s\n", _("14 days, a STATE_WARNING is returned. A STATE_CRITICAL will be returned when"));
1715 printf (" %s\n\n", _("the certificate is expired."));
1716 printf ("\n");
1717 printf (" %s\n\n", "CHECK CERTIFICATE: check_curl -H www.verisign.com -C 30,14");
1718 printf (" %s\n", _("When the certificate of 'www.verisign.com' is valid for more than 30 days,"));
1719 printf (" %s\n", _("a STATE_OK is returned. When the certificate is still valid, but for less than"));
1720 printf (" %s\n", _("30 days, but more than 14 days, a STATE_WARNING is returned."));
1721 printf (" %s\n", _("A STATE_CRITICAL will be returned when certificate expires in less than 14 days"));
1722#endif
1723
1724 printf ("\n %s\n", "CHECK WEBSERVER CONTENT VIA PROXY:");
1725 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
1726 printf (" %s\n", _("http_proxy=http://192.168.100.35:3128 ./check_curl -H www.monitoring-plugins.org"));
1727 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
1728 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u http://www.monitoring-plugins.org/ -H www.monitoring-plugins.org"));
1729
1730#ifdef LIBCURL_FEATURE_SSL
1731 printf ("\n %s\n", "CHECK SSL WEBSERVER CONTENT VIA PROXY USING HTTP 1.1 CONNECT: ");
1732 printf (" %s\n", _("It is recommended to use an environment proxy like:"));
1733 printf (" %s\n", _("https_proxy=http://192.168.100.35:3128 ./check_curl -H www.verisign.com -S"));
1734 printf (" %s\n", _("legacy proxy requests in check_http style still work:"));
1735 printf (" %s\n", _("check_curl -I 192.168.100.35 -p 3128 -u https://www.verisign.com/ -S -j CONNECT -H www.verisign.com "));
1736 printf (" %s\n", _("all these options are needed: -I <proxy> -p <proxy-port> -u <check-url> -S(sl) -j CONNECT -H <webserver>"));
1737 printf (" %s\n", _("a STATE_OK will be returned. When the server returns its content but exceeds"));
1738 printf (" %s\n", _("the 5-second threshold, a STATE_WARNING will be returned. When an error occurs,"));
1739 printf (" %s\n", _("a STATE_CRITICAL will be returned."));
1740
1741#endif
1742
1743 printf (UT_SUPPORT);
1744
1745}
1746
1747
1748
1749void
1750print_usage (void)
1751{
1752 printf ("%s\n", _("Usage:"));
1753 printf (" %s -H <vhost> | -I <IP-address> [-u <uri>] [-p <port>]\n",progname);
1754 printf (" [-J <client certificate file>] [-K <private key>] [--ca-cert <CA certificate file>]\n");
1755 printf (" [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L] [-E] [-a auth]\n");
1756 printf (" [-b proxy_auth] [-f <ok|warning|critcal|follow|sticky|stickyport|curl>]\n");
1757 printf (" [-e <expect>] [-d string] [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n");
1758 printf (" [-P string] [-m <min_pg_size>:<max_pg_size>] [-4|-6] [-N] [-M <age>]\n");
1759 printf (" [-A string] [-k string] [-S <version>] [--sni] [-C <warn_age>[,<crit_age>]]\n");
1760 printf (" [-T <content-type>] [-j method]\n");
1761 printf ("\n");
1762 printf ("%s\n", _("WARNING: check_curl is experimental. Please use"));
1763 printf ("%s\n\n", _("check_http if you need a stable version."));
1764}
1765
1766void
1767print_curl_version (void)
1768{
1769 printf( "%s\n", curl_version());
1770}
1771
1772int
1773curlhelp_initwritebuffer (curlhelp_write_curlbuf *buf)
1774{
1775 buf->bufsize = DEFAULT_BUFFER_SIZE;
1776 buf->buflen = 0;
1777 buf->buf = (char *)malloc ((size_t)buf->bufsize);
1778 if (buf->buf == NULL) return -1;
1779 return 0;
1780}
1781
1782int
1783curlhelp_buffer_write_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1784{
1785 curlhelp_write_curlbuf *buf = (curlhelp_write_curlbuf *)stream;
1786
1787 while (buf->bufsize < buf->buflen + size * nmemb + 1) {
1788 buf->bufsize *= buf->bufsize * 2;
1789 buf->buf = (char *)realloc (buf->buf, buf->bufsize);
1790 if (buf->buf == NULL) return -1;
1791 }
1792
1793 memcpy (buf->buf + buf->buflen, buffer, size * nmemb);
1794 buf->buflen += size * nmemb;
1795 buf->buf[buf->buflen] = '\0';
1796
1797 return (int)(size * nmemb);
1798}
1799
1800int
1801curlhelp_buffer_read_callback (void *buffer, size_t size, size_t nmemb, void *stream)
1802{
1803 curlhelp_read_curlbuf *buf = (curlhelp_read_curlbuf *)stream;
1804
1805 size_t n = min (nmemb * size, buf->buflen - buf->pos);
1806
1807 memcpy (buffer, buf->buf + buf->pos, n);
1808 buf->pos += n;
1809
1810 return (int)n;
1811}
1812
1813void
1814curlhelp_freewritebuffer (curlhelp_write_curlbuf *buf)
1815{
1816 free (buf->buf);
1817 buf->buf = NULL;
1818}
1819
1820int
1821curlhelp_initreadbuffer (curlhelp_read_curlbuf *buf, const char *data, size_t datalen)
1822{
1823 buf->buflen = datalen;
1824 buf->buf = (char *)malloc ((size_t)buf->buflen);
1825 if (buf->buf == NULL) return -1;
1826 memcpy (buf->buf, data, datalen);
1827 buf->pos = 0;
1828 return 0;
1829}
1830
1831void
1832curlhelp_freereadbuffer (curlhelp_read_curlbuf *buf)
1833{
1834 free (buf->buf);
1835 buf->buf = NULL;
1836}
1837
1838/* TODO: where to put this, it's actually part of sstrings2 (logically)?
1839 */
1840const char*
1841strrstr2(const char *haystack, const char *needle)
1842{
1843 int counter;
1844 size_t len;
1845 const char *prev_pos;
1846 const char *pos;
1847
1848 if (haystack == NULL || needle == NULL)
1849 return NULL;
1850
1851 if (haystack[0] == '\0' || needle[0] == '\0')
1852 return NULL;
1853
1854 counter = 0;
1855 prev_pos = NULL;
1856 pos = haystack;
1857 len = strlen (needle);
1858 for (;;) {
1859 pos = strstr (pos, needle);
1860 if (pos == NULL) {
1861 if (counter == 0)
1862 return NULL;
1863 else
1864 return prev_pos;
1865 }
1866 counter++;
1867 prev_pos = pos;
1868 pos += len;
1869 if (*pos == '\0') return prev_pos;
1870 }
1871}
1872
1873int
1874curlhelp_parse_statusline (const char *buf, curlhelp_statusline *status_line)
1875{
1876 char *first_line_end;
1877 char *p;
1878 size_t first_line_len;
1879 char *pp;
1880 const char *start;
1881 char *first_line_buf;
1882
1883 /* find last start of a new header */
1884 start = strrstr2 (buf, "\r\nHTTP");
1885 if (start != NULL) {
1886 start += 2;
1887 buf = start;
1888 }
1889
1890 first_line_end = strstr(buf, "\r\n");
1891 if (first_line_end == NULL) return -1;
1892
1893 first_line_len = (size_t)(first_line_end - buf);
1894 status_line->first_line = (char *)malloc (first_line_len + 1);
1895 if (status_line->first_line == NULL) return -1;
1896 memcpy (status_line->first_line, buf, first_line_len);
1897 status_line->first_line[first_line_len] = '\0';
1898 first_line_buf = strdup( status_line->first_line );
1899
1900 /* protocol and version: "HTTP/x.x" SP */
1901
1902 p = strtok(first_line_buf, "/");
1903 if( p == NULL ) { free( first_line_buf ); return -1; }
1904 if( strcmp( p, "HTTP" ) != 0 ) { free( first_line_buf ); return -1; }
1905
1906 p = strtok( NULL, "." );
1907 if( p == NULL ) { free( first_line_buf ); return -1; }
1908 status_line->http_major = (int)strtol( p, &pp, 10 );
1909 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1910
1911 p = strtok( NULL, " " );
1912 if( p == NULL ) { free( first_line_buf ); return -1; }
1913 status_line->http_minor = (int)strtol( p, &pp, 10 );
1914 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1915
1916 /* status code: "404" or "404.1", then SP */
1917
1918 p = strtok( NULL, " ." );
1919 if( p == NULL ) { free( first_line_buf ); return -1; }
1920 if( strchr( p, '.' ) != NULL ) {
1921 char *ppp;
1922 ppp = strtok( p, "." );
1923 status_line->http_code = (int)strtol( ppp, &pp, 10 );
1924 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1925
1926 ppp = strtok( NULL, "" );
1927 status_line->http_subcode = (int)strtol( ppp, &pp, 10 );
1928 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1929 } else {
1930 status_line->http_code = (int)strtol( p, &pp, 10 );
1931 status_line->http_subcode = -1;
1932 if( *pp != '\0' ) { free( first_line_buf ); return -1; }
1933 }
1934
1935 /* Human readable message: "Not Found" CRLF */
1936
1937 free( first_line_buf );
1938 p = strtok( NULL, "" );
1939 if( p == NULL ) { free( status_line->first_line ); return -1; }
1940 status_line->msg = status_line->first_line + ( p - first_line_buf );
1941
1942 return 0;
1943}
1944
1945void
1946curlhelp_free_statusline (curlhelp_statusline *status_line)
1947{
1948 free (status_line->first_line);
1949}
1950
1951void
1952remove_newlines (char *s)
1953{
1954 char *p;
1955
1956 for (p = s; *p != '\0'; p++)
1957 if (*p == '\r' || *p == '\n')
1958 *p = ' ';
1959}
1960
1961char *
1962perfd_time_ssl (double elapsed_time_ssl)
1963{
1964 return fperfdata ("time_ssl", elapsed_time_ssl, "s", FALSE, 0, FALSE, 0, FALSE, 0, FALSE, 0);
1965}
1966
1967char *
1968get_header_value (const struct phr_header* headers, const size_t nof_headers, const char* header)
1969{
1970 int i;
1971 for( i = 0; i < nof_headers; i++ ) {
1972 if( strncasecmp( header, headers[i].name, max( headers[i].name_len, 4 ) ) == 0 ) {
1973 return strndup( headers[i].value, headers[i].value_len );
1974 }
1975 }
1976 return NULL;
1977}
1978
1979int
1980check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFAULT_BUFFER_SIZE])
1981{
1982 char *server_date = NULL;
1983 char *document_date = NULL;
1984 int date_result = STATE_OK;
1985 curlhelp_statusline status_line;
1986 struct phr_header headers[255];
1987 size_t nof_headers = 255;
1988 size_t msglen;
1989
1990 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
1991 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
1992 headers, &nof_headers, 0);
1993
1994 server_date = get_header_value (headers, nof_headers, "date");
1995 document_date = get_header_value (headers, nof_headers, "last-modified");
1996
1997 if (!server_date || !*server_date) {
1998 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date unknown, "), *msg);
1999 date_result = max_state_alt(STATE_UNKNOWN, date_result);
2000 } else if (!document_date || !*document_date) {
2001 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument modification date unknown, "), *msg);
2002 date_result = max_state_alt(STATE_CRITICAL, date_result);
2003 } else {
2004 time_t srv_data = curl_getdate (server_date, NULL);
2005 time_t doc_data = curl_getdate (document_date, NULL);
2006 if (verbose >= 2)
2007 printf ("* server date: '%s' (%d), doc_date: '%s' (%d)\n", server_date, (int)srv_data, document_date, (int)doc_data);
2008 if (srv_data <= 0) {
2009 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sServer date \"%100s\" unparsable, "), *msg, server_date);
2010 date_result = max_state_alt(STATE_CRITICAL, date_result);
2011 } else if (doc_data <= 0) {
2012 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument date \"%100s\" unparsable, "), *msg, document_date);
2013 date_result = max_state_alt(STATE_CRITICAL, date_result);
2014 } else if (doc_data > srv_data + 30) {
2015 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sDocument is %d seconds in the future, "), *msg, (int)doc_data - (int)srv_data);
2016 date_result = max_state_alt(STATE_CRITICAL, date_result);
2017 } else if (doc_data < srv_data - maximum_age) {
2018 int n = (srv_data - doc_data);
2019 if (n > (60 * 60 * 24 * 2)) {
2020 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %.1f days ago, "), *msg, ((float) n) / (60 * 60 * 24));
2021 date_result = max_state_alt(STATE_CRITICAL, date_result);
2022 } else {
2023 snprintf (*msg, DEFAULT_BUFFER_SIZE, _("%sLast modified %d:%02d:%02d ago, "), *msg, n / (60 * 60), (n / 60) % 60, n % 60);
2024 date_result = max_state_alt(STATE_CRITICAL, date_result);
2025 }
2026 }
2027 }
2028
2029 if (server_date) free (server_date);
2030 if (document_date) free (document_date);
2031
2032 return date_result;
2033}
2034
2035
2036int
2037get_content_length (const curlhelp_write_curlbuf* header_buf, const curlhelp_write_curlbuf* body_buf)
2038{
2039 const char *s;
2040 int content_length = 0;
2041 char *copy;
2042 struct phr_header headers[255];
2043 size_t nof_headers = 255;
2044 size_t msglen;
2045 char *content_length_s = NULL;
2046 curlhelp_statusline status_line;
2047
2048 int res = phr_parse_response (header_buf->buf, header_buf->buflen,
2049 &status_line.http_minor, &status_line.http_code, &status_line.msg, &msglen,
2050 headers, &nof_headers, 0);
2051
2052 content_length_s = get_header_value (headers, nof_headers, "content-length");
2053 if (!content_length_s) {
2054 return header_buf->buflen + body_buf->buflen;
2055 }
2056 content_length_s += strspn (content_length_s, " \t");
2057 content_length = atoi (content_length_s);
2058 if (content_length != body_buf->buflen) {
2059 /* TODO: should we warn if the actual and the reported body length don't match? */
2060 }
2061
2062 if (content_length_s) free (content_length_s);
2063
2064 return header_buf->buflen + body_buf->buflen;
2065}
2066
2067/* TODO: is there a better way in libcurl to check for the SSL library? */
2068curlhelp_ssl_library
2069curlhelp_get_ssl_library (CURL* curl)
2070{
2071 curl_version_info_data* version_data;
2072 char *ssl_version;
2073 char *library;
2074 curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN;
2075
2076 version_data = curl_version_info (CURLVERSION_NOW);
2077 if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2078
2079 ssl_version = strdup (version_data->ssl_version);
2080 if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2081
2082 library = strtok (ssl_version, "/");
2083 if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN;
2084
2085 if (strcmp (library, "OpenSSL") == 0)
2086 ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL;
2087 else if (strcmp (library, "LibreSSL") == 0)
2088 ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL;
2089 else if (strcmp (library, "GnuTLS") == 0)
2090 ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS;
2091 else if (strcmp (library, "NSS") == 0)
2092 ssl_library = CURLHELP_SSL_LIBRARY_NSS;
2093
2094 if (verbose >= 2)
2095 printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library);
2096
2097 free (ssl_version);
2098
2099 return ssl_library;
2100}
2101
2102const char*
2103curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library)
2104{
2105 switch (ssl_library) {
2106 case CURLHELP_SSL_LIBRARY_OPENSSL:
2107 return "OpenSSL";
2108 case CURLHELP_SSL_LIBRARY_LIBRESSL:
2109 return "LibreSSL";
2110 case CURLHELP_SSL_LIBRARY_GNUTLS:
2111 return "GnuTLS";
2112 case CURLHELP_SSL_LIBRARY_NSS:
2113 return "NSS";
2114 case CURLHELP_SSL_LIBRARY_UNKNOWN:
2115 default:
2116 return "unknown";
2117 }
2118}
2119
2120#ifdef LIBCURL_FEATURE_SSL
2121#ifndef USE_OPENSSL
2122time_t
2123parse_cert_date (const char *s)
2124{
2125 struct tm tm;
2126 time_t date;
2127
2128 if (!s) return -1;
2129
2130 strptime (s, "%Y-%m-%d %H:%M:%S GMT", &tm);
2131 date = mktime (&tm);
2132
2133 return date;
2134}
2135
2136/* TODO: this needs cleanup in the sslutils.c, maybe we the #else case to
2137 * OpenSSL could be this function
2138 */
2139int
2140net_noopenssl_check_certificate (cert_ptr_union* cert_ptr, int days_till_exp_warn, int days_till_exp_crit)
2141{
2142 int i;
2143 struct curl_slist* slist;
2144 int cname_found = 0;
2145 char* start_date_str = NULL;
2146 char* end_date_str = NULL;
2147 time_t start_date;
2148 time_t end_date;
2149 char *tz;
2150 float time_left;
2151 int days_left;
2152 int time_remaining;
2153 char timestamp[50] = "";
2154 int status = STATE_UNKNOWN;
2155
2156 if (verbose >= 2)
2157 printf ("**** REQUEST CERTIFICATES ****\n");
2158
2159 for (i = 0; i < cert_ptr->to_certinfo->num_of_certs; i++) {
2160 for (slist = cert_ptr->to_certinfo->certinfo[i]; slist; slist = slist->next) {
2161 /* find first common name in subject, TODO: check alternative subjects for
2162 * multi-host certificate, check wildcards
2163 */
2164 if (strncasecmp (slist->data, "Subject:", 8) == 0) {
2165 char* p = strstr (slist->data, "CN=");
2166 if (p != NULL) {
2167 if (strncmp (host_name, p+3, strlen (host_name)) == 0) {
2168 cname_found = 1;
2169 }
2170 }
2171 } else if (strncasecmp (slist->data, "Start Date:", 11) == 0) {
2172 start_date_str = &slist->data[11];
2173 } else if (strncasecmp (slist->data, "Expire Date:", 12) == 0) {
2174 end_date_str = &slist->data[12];
2175 } else if (strncasecmp (slist->data, "Cert:", 5) == 0) {
2176 goto HAVE_FIRST_CERT;
2177 }
2178 if (verbose >= 2)
2179 printf ("%d ** %s\n", i, slist->data);
2180 }
2181 }
2182HAVE_FIRST_CERT:
2183
2184 if (verbose >= 2)
2185 printf ("**** REQUEST CERTIFICATES ****\n");
2186
2187 if (!cname_found) {
2188 printf("%s\n",_("CRITICAL - Cannot retrieve certificate subject."));
2189 return STATE_CRITICAL;
2190 }
2191
2192 start_date = parse_cert_date (start_date_str);
2193 if (start_date <= 0) {
2194 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Start Date' in certificate: '%s'"),
2195 start_date_str);
2196 puts (msg);
2197 return STATE_WARNING;
2198 }
2199
2200 end_date = parse_cert_date (end_date_str);
2201 if (end_date <= 0) {
2202 snprintf (msg, DEFAULT_BUFFER_SIZE, _("WARNING - Unparsable 'Expire Date' in certificate: '%s'"),
2203 start_date_str);
2204 puts (msg);
2205 return STATE_WARNING;
2206 }
2207
2208 time_left = difftime (end_date, time(NULL));
2209 days_left = time_left / 86400;
2210 tz = getenv("TZ");
2211 setenv("TZ", "GMT", 1);
2212 tzset();
2213 strftime(timestamp, 50, "%c %z", localtime(&end_date));
2214 if (tz)
2215 setenv("TZ", tz, 1);
2216 else
2217 unsetenv("TZ");
2218 tzset();
2219
2220 if (days_left > 0 && days_left <= days_till_exp_warn) {
2221 printf (_("%s - Certificate '%s' expires in %d day(s) (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, days_left, timestamp);
2222 if (days_left > days_till_exp_crit)
2223 status = STATE_WARNING;
2224 else
2225 status = STATE_CRITICAL;
2226 } else if (days_left == 0 && time_left > 0) {
2227 if (time_left >= 3600)
2228 time_remaining = (int) time_left / 3600;
2229 else
2230 time_remaining = (int) time_left / 60;
2231
2232 printf (_("%s - Certificate '%s' expires in %u %s (%s)\n"),
2233 (days_left>days_till_exp_crit) ? "WARNING" : "CRITICAL", host_name, time_remaining,
2234 time_left >= 3600 ? "hours" : "minutes", timestamp);
2235
2236 if ( days_left > days_till_exp_crit)
2237 status = STATE_WARNING;
2238 else
2239 status = STATE_CRITICAL;
2240 } else if (time_left < 0) {
2241 printf(_("CRITICAL - Certificate '%s' expired on %s.\n"), host_name, timestamp);
2242 status=STATE_CRITICAL;
2243 } else if (days_left == 0) {
2244 printf (_("%s - Certificate '%s' just expired (%s).\n"), (days_left>days_till_exp_crit)?"WARNING":"CRITICAL", host_name, timestamp);
2245 if (days_left > days_till_exp_crit)
2246 status = STATE_WARNING;
2247 else
2248 status = STATE_CRITICAL;
2249 } else {
2250 printf(_("OK - Certificate '%s' will expire on %s.\n"), host_name, timestamp);
2251 status = STATE_OK;
2252 }
2253 return status;
2254}
2255#endif /* USE_OPENSSL */
2256#endif /* LIBCURL_FEATURE_SSL */
diff --git a/plugins/picohttpparser/Makefile.am b/plugins/picohttpparser/Makefile.am
new file mode 100644
index 00000000..5a610278
--- /dev/null
+++ b/plugins/picohttpparser/Makefile.am
@@ -0,0 +1,3 @@
1noinst_LIBRARIES = libpicohttpparser.a
2
3libpicohttpparser_a_SOURCES = picohttpparser.c
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
new file mode 100644
index 00000000..6a2d872d
--- /dev/null
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -0,0 +1,620 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#include <assert.h>
28#include <stddef.h>
29#include <string.h>
30#ifdef __SSE4_2__
31#ifdef _MSC_VER
32#include <nmmintrin.h>
33#else
34#include <x86intrin.h>
35#endif
36#endif
37#include "picohttpparser.h"
38
39/* $Id: a707070d11d499609f99d09f97535642cec910a8 $ */
40
41#if __GNUC__ >= 3
42#define likely(x) __builtin_expect(!!(x), 1)
43#define unlikely(x) __builtin_expect(!!(x), 0)
44#else
45#define likely(x) (x)
46#define unlikely(x) (x)
47#endif
48
49#ifdef _MSC_VER
50#define ALIGNED(n) _declspec(align(n))
51#else
52#define ALIGNED(n) __attribute__((aligned(n)))
53#endif
54
55#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
56
57#define CHECK_EOF() \
58 if (buf == buf_end) { \
59 *ret = -2; \
60 return NULL; \
61 }
62
63#define EXPECT_CHAR_NO_CHECK(ch) \
64 if (*buf++ != ch) { \
65 *ret = -1; \
66 return NULL; \
67 }
68
69#define EXPECT_CHAR(ch) \
70 CHECK_EOF(); \
71 EXPECT_CHAR_NO_CHECK(ch);
72
73#define ADVANCE_TOKEN(tok, toklen) \
74 do { \
75 const char *tok_start = buf; \
76 static const char ALIGNED(16) ranges2[] = "\000\040\177\177"; \
77 int found2; \
78 buf = findchar_fast(buf, buf_end, ranges2, sizeof(ranges2) - 1, &found2); \
79 if (!found2) { \
80 CHECK_EOF(); \
81 } \
82 while (1) { \
83 if (*buf == ' ') { \
84 break; \
85 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
86 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
87 *ret = -1; \
88 return NULL; \
89 } \
90 } \
91 ++buf; \
92 CHECK_EOF(); \
93 } \
94 tok = tok_start; \
95 toklen = buf - tok_start; \
96 } while (0)
97
98static const char *token_char_map = "\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"
99 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
100 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
101 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
102 "\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"
103 "\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"
104 "\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"
105 "\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";
106
107static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
108{
109 *found = 0;
110#if __SSE4_2__
111 if (likely(buf_end - buf >= 16)) {
112 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
113
114 size_t left = (buf_end - buf) & ~15;
115 do {
116 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
117 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
118 if (unlikely(r != 16)) {
119 buf += r;
120 *found = 1;
121 break;
122 }
123 buf += 16;
124 left -= 16;
125 } while (likely(left != 0));
126 }
127#else
128 /* suppress unused parameter warning */
129 (void)buf_end;
130 (void)ranges;
131 (void)ranges_size;
132#endif
133 return buf;
134}
135
136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
137{
138 const char *token_start = buf;
139
140#ifdef __SSE4_2__
141 static const char ranges1[] = "\0\010"
142 /* allow HT */
143 "\012\037"
144 /* allow SP and up to but not including DEL */
145 "\177\177"
146 /* allow chars w. MSB set */
147 ;
148 int found;
149 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
150 if (found)
151 goto FOUND_CTL;
152#else
153 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
154 while (likely(buf_end - buf >= 8)) {
155#define DOIT() \
156 do { \
157 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
158 goto NonPrintable; \
159 ++buf; \
160 } while (0)
161 DOIT();
162 DOIT();
163 DOIT();
164 DOIT();
165 DOIT();
166 DOIT();
167 DOIT();
168 DOIT();
169#undef DOIT
170 continue;
171 NonPrintable:
172 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
173 goto FOUND_CTL;
174 }
175 ++buf;
176 }
177#endif
178 for (;; ++buf) {
179 CHECK_EOF();
180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
182 goto FOUND_CTL;
183 }
184 }
185 }
186FOUND_CTL:
187 if (likely(*buf == '\015')) {
188 ++buf;
189 EXPECT_CHAR('\012');
190 *token_len = buf - 2 - token_start;
191 } else if (*buf == '\012') {
192 *token_len = buf - token_start;
193 ++buf;
194 } else {
195 *ret = -1;
196 return NULL;
197 }
198 *token = token_start;
199
200 return buf;
201}
202
203static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
204{
205 int ret_cnt = 0;
206 buf = last_len < 3 ? buf : buf + last_len - 3;
207
208 while (1) {
209 CHECK_EOF();
210 if (*buf == '\015') {
211 ++buf;
212 CHECK_EOF();
213 EXPECT_CHAR('\012');
214 ++ret_cnt;
215 } else if (*buf == '\012') {
216 ++buf;
217 ++ret_cnt;
218 } else {
219 ++buf;
220 ret_cnt = 0;
221 }
222 if (ret_cnt == 2) {
223 return buf;
224 }
225 }
226
227 *ret = -2;
228 return NULL;
229}
230
231#define PARSE_INT(valp_, mul_) \
232 if (*buf < '0' || '9' < *buf) { \
233 buf++; \
234 *ret = -1; \
235 return NULL; \
236 } \
237 *(valp_) = (mul_) * (*buf++ - '0');
238
239#define PARSE_INT_3(valp_) \
240 do { \
241 int res_ = 0; \
242 PARSE_INT(&res_, 100) \
243 *valp_ = res_; \
244 PARSE_INT(&res_, 10) \
245 *valp_ += res_; \
246 PARSE_INT(&res_, 1) \
247 *valp_ += res_; \
248 } while (0)
249
250/* returned pointer is always within [buf, buf_end), or null */
251static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
252{
253 /* we want at least [HTTP/1.<two chars>] to try to parse */
254 if (buf_end - buf < 9) {
255 *ret = -2;
256 return NULL;
257 }
258 EXPECT_CHAR_NO_CHECK('H');
259 EXPECT_CHAR_NO_CHECK('T');
260 EXPECT_CHAR_NO_CHECK('T');
261 EXPECT_CHAR_NO_CHECK('P');
262 EXPECT_CHAR_NO_CHECK('/');
263 EXPECT_CHAR_NO_CHECK('1');
264 EXPECT_CHAR_NO_CHECK('.');
265 PARSE_INT(minor_version, 1);
266 return buf;
267}
268
269static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
270 size_t max_headers, int *ret)
271{
272 for (;; ++*num_headers) {
273 CHECK_EOF();
274 if (*buf == '\015') {
275 ++buf;
276 EXPECT_CHAR('\012');
277 break;
278 } else if (*buf == '\012') {
279 ++buf;
280 break;
281 }
282 if (*num_headers == max_headers) {
283 *ret = -1;
284 return NULL;
285 }
286 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
287 /* parsing name, but do not discard SP before colon, see
288 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
289 headers[*num_headers].name = buf;
290 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
291 "\"\"" /* 0x22 */
292 "()" /* 0x28,0x29 */
293 ",," /* 0x2c */
294 "//" /* 0x2f */
295 ":@" /* 0x3a-0x40 */
296 "[]" /* 0x5b-0x5d */
297 "{\377"; /* 0x7b-0xff */
298 int found;
299 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
300 if (!found) {
301 CHECK_EOF();
302 }
303 while (1) {
304 if (*buf == ':') {
305 break;
306 } else if (!token_char_map[(unsigned char)*buf]) {
307 *ret = -1;
308 return NULL;
309 }
310 ++buf;
311 CHECK_EOF();
312 }
313 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
314 *ret = -1;
315 return NULL;
316 }
317 ++buf;
318 for (;; ++buf) {
319 CHECK_EOF();
320 if (!(*buf == ' ' || *buf == '\t')) {
321 break;
322 }
323 }
324 } else {
325 headers[*num_headers].name = NULL;
326 headers[*num_headers].name_len = 0;
327 }
328 if ((buf = get_token_to_eol(buf, buf_end, &headers[*num_headers].value, &headers[*num_headers].value_len, ret)) == NULL) {
329 return NULL;
330 }
331 }
332 return buf;
333}
334
335static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
336 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
337 size_t max_headers, int *ret)
338{
339 /* skip first empty line (some clients add CRLF after POST content) */
340 CHECK_EOF();
341 if (*buf == '\015') {
342 ++buf;
343 EXPECT_CHAR('\012');
344 } else if (*buf == '\012') {
345 ++buf;
346 }
347
348 /* parse request line */
349 ADVANCE_TOKEN(*method, *method_len);
350 ++buf;
351 ADVANCE_TOKEN(*path, *path_len);
352 ++buf;
353 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
354 return NULL;
355 }
356 if (*buf == '\015') {
357 ++buf;
358 EXPECT_CHAR('\012');
359 } else if (*buf == '\012') {
360 ++buf;
361 } else {
362 *ret = -1;
363 return NULL;
364 }
365
366 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
367}
368
369int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
370 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
371{
372 const char *buf = buf_start, *buf_end = buf_start + len;
373 size_t max_headers = *num_headers;
374 int r;
375
376 *method = NULL;
377 *method_len = 0;
378 *path = NULL;
379 *path_len = 0;
380 *minor_version = -1;
381 *num_headers = 0;
382
383 /* if last_len != 0, check if the request is complete (a fast countermeasure
384 againt slowloris */
385 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
386 return r;
387 }
388
389 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
390 &r)) == NULL) {
391 return r;
392 }
393
394 return (int)(buf - buf_start);
395}
396
397static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
398 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
399{
400 /* parse "HTTP/1.x" */
401 if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
402 return NULL;
403 }
404 /* skip space */
405 if (*buf++ != ' ') {
406 *ret = -1;
407 return NULL;
408 }
409 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
410 if (buf_end - buf < 4) {
411 *ret = -2;
412 return NULL;
413 }
414 PARSE_INT_3(status);
415
416 /* skip space */
417 if (*buf++ != ' ') {
418 *ret = -1;
419 return NULL;
420 }
421 /* get message */
422 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
423 return NULL;
424 }
425
426 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
427}
428
429int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
430 struct phr_header *headers, size_t *num_headers, size_t last_len)
431{
432 const char *buf = buf_start, *buf_end = buf + len;
433 size_t max_headers = *num_headers;
434 int r;
435
436 *minor_version = -1;
437 *status = 0;
438 *msg = NULL;
439 *msg_len = 0;
440 *num_headers = 0;
441
442 /* if last_len != 0, check if the response is complete (a fast countermeasure
443 against slowloris */
444 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
445 return r;
446 }
447
448 if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
449 return r;
450 }
451
452 return (int)(buf - buf_start);
453}
454
455int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
456{
457 const char *buf = buf_start, *buf_end = buf + len;
458 size_t max_headers = *num_headers;
459 int r;
460
461 *num_headers = 0;
462
463 /* if last_len != 0, check if the response is complete (a fast countermeasure
464 against slowloris */
465 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
466 return r;
467 }
468
469 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
470 return r;
471 }
472
473 return (int)(buf - buf_start);
474}
475
476enum {
477 CHUNKED_IN_CHUNK_SIZE,
478 CHUNKED_IN_CHUNK_EXT,
479 CHUNKED_IN_CHUNK_DATA,
480 CHUNKED_IN_CHUNK_CRLF,
481 CHUNKED_IN_TRAILERS_LINE_HEAD,
482 CHUNKED_IN_TRAILERS_LINE_MIDDLE
483};
484
485static int decode_hex(int ch)
486{
487 if ('0' <= ch && ch <= '9') {
488 return ch - '0';
489 } else if ('A' <= ch && ch <= 'F') {
490 return ch - 'A' + 0xa;
491 } else if ('a' <= ch && ch <= 'f') {
492 return ch - 'a' + 0xa;
493 } else {
494 return -1;
495 }
496}
497
498ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
499{
500 size_t dst = 0, src = 0, bufsz = *_bufsz;
501 ssize_t ret = -2; /* incomplete */
502
503 while (1) {
504 switch (decoder->_state) {
505 case CHUNKED_IN_CHUNK_SIZE:
506 for (;; ++src) {
507 int v;
508 if (src == bufsz)
509 goto Exit;
510 if ((v = decode_hex(buf[src])) == -1) {
511 if (decoder->_hex_count == 0) {
512 ret = -1;
513 goto Exit;
514 }
515 break;
516 }
517 if (decoder->_hex_count == sizeof(size_t) * 2) {
518 ret = -1;
519 goto Exit;
520 }
521 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
522 ++decoder->_hex_count;
523 }
524 decoder->_hex_count = 0;
525 decoder->_state = CHUNKED_IN_CHUNK_EXT;
526 /* fallthru */
527 case CHUNKED_IN_CHUNK_EXT:
528 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
529 for (;; ++src) {
530 if (src == bufsz)
531 goto Exit;
532 if (buf[src] == '\012')
533 break;
534 }
535 ++src;
536 if (decoder->bytes_left_in_chunk == 0) {
537 if (decoder->consume_trailer) {
538 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
539 break;
540 } else {
541 goto Complete;
542 }
543 }
544 decoder->_state = CHUNKED_IN_CHUNK_DATA;
545 /* fallthru */
546 case CHUNKED_IN_CHUNK_DATA: {
547 size_t avail = bufsz - src;
548 if (avail < decoder->bytes_left_in_chunk) {
549 if (dst != src)
550 memmove(buf + dst, buf + src, avail);
551 src += avail;
552 dst += avail;
553 decoder->bytes_left_in_chunk -= avail;
554 goto Exit;
555 }
556 if (dst != src)
557 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
558 src += decoder->bytes_left_in_chunk;
559 dst += decoder->bytes_left_in_chunk;
560 decoder->bytes_left_in_chunk = 0;
561 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
562 }
563 /* fallthru */
564 case CHUNKED_IN_CHUNK_CRLF:
565 for (;; ++src) {
566 if (src == bufsz)
567 goto Exit;
568 if (buf[src] != '\015')
569 break;
570 }
571 if (buf[src] != '\012') {
572 ret = -1;
573 goto Exit;
574 }
575 ++src;
576 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
577 break;
578 case CHUNKED_IN_TRAILERS_LINE_HEAD:
579 for (;; ++src) {
580 if (src == bufsz)
581 goto Exit;
582 if (buf[src] != '\015')
583 break;
584 }
585 if (buf[src++] == '\012')
586 goto Complete;
587 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
588 /* fallthru */
589 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
590 for (;; ++src) {
591 if (src == bufsz)
592 goto Exit;
593 if (buf[src] == '\012')
594 break;
595 }
596 ++src;
597 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
598 break;
599 default:
600 assert(!"decoder is corrupt");
601 }
602 }
603
604Complete:
605 ret = bufsz - src;
606Exit:
607 if (dst != src)
608 memmove(buf + dst, buf + src, bufsz - src);
609 *_bufsz = dst;
610 return ret;
611}
612
613int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
614{
615 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
616}
617
618#undef CHECK_EOF
619#undef EXPECT_CHAR
620#undef ADVANCE_TOKEN
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
new file mode 100644
index 00000000..a8fad71d
--- /dev/null
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -0,0 +1,89 @@
1/*
2 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3 * Shigeo Mitsunari
4 *
5 * The software is licensed under either the MIT License (below) or the Perl
6 * license.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to
10 * deal in the Software without restriction, including without limitation the
11 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 * sell copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27#ifndef picohttpparser_h
28#define picohttpparser_h
29
30#include <sys/types.h>
31
32#ifdef _MSC_VER
33#define ssize_t intptr_t
34#endif
35
36/* $Id: 67fd3ee74103ada60258d8a16e868f483abcca87 $ */
37
38#ifdef __cplusplus
39extern "C" {
40#endif
41
42/* contains name and value of a header (name == NULL if is a continuing line
43 * of a multiline header */
44struct phr_header {
45 const char *name;
46 size_t name_len;
47 const char *value;
48 size_t value_len;
49};
50
51/* returns number of bytes consumed if successful, -2 if request is partial,
52 * -1 if failed */
53int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
54 int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
55
56/* ditto */
57int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
58 struct phr_header *headers, size_t *num_headers, size_t last_len);
59
60/* ditto */
61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
62
63/* should be zero-filled before start */
64struct phr_chunked_decoder {
65 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
66 char consume_trailer; /* if trailing headers should be consumed */
67 char _hex_count;
68 char _state;
69};
70
71/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
72 * encoding headers. When the function returns without an error, bufsz is
73 * updated to the length of the decoded data available. Applications should
74 * repeatedly call the function while it returns -2 (incomplete) every time
75 * supplying newly arrived data. If the end of the chunked-encoded data is
76 * found, the function returns a non-negative number indicating the number of
77 * octets left undecoded at the tail of the supplied buffer. Returns -1 on
78 * error.
79 */
80ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
81
82/* returns if the chunked decoder is in middle of chunked data */
83int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
84
85#ifdef __cplusplus
86}
87#endif
88
89#endif
diff --git a/plugins/sslutils.c b/plugins/sslutils.c
index e38947e3..14f6579d 100644
--- a/plugins/sslutils.c
+++ b/plugins/sslutils.c
@@ -1,29 +1,29 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2*
3* Monitoring Plugins SSL utilities 3* Monitoring Plugins SSL utilities
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2005-2010 Monitoring Plugins Development Team 6* Copyright (c) 2005-2010 Monitoring Plugins Development Team
7* 7*
8* Description: 8* Description:
9* 9*
10* This file contains common functions for plugins that require SSL. 10* This file contains common functions for plugins that require SSL.
11* 11*
12* 12*
13* This program is free software: you can redistribute it and/or modify 13* This program is free software: you can redistribute it and/or modify
14* it under the terms of the GNU General Public License as published by 14* it under the terms of the GNU General Public License as published by
15* the Free Software Foundation, either version 3 of the License, or 15* the Free Software Foundation, either version 3 of the License, or
16* (at your option) any later version. 16* (at your option) any later version.
17* 17*
18* This program is distributed in the hope that it will be useful, 18* This program is distributed in the hope that it will be useful,
19* but WITHOUT ANY WARRANTY; without even the implied warranty of 19* but WITHOUT ANY WARRANTY; without even the implied warranty of
20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21* GNU General Public License for more details. 21* GNU General Public License for more details.
22* 22*
23* You should have received a copy of the GNU General Public License 23* You should have received a copy of the GNU General Public License
24* along with this program. If not, see <http://www.gnu.org/licenses/>. 24* along with this program. If not, see <http://www.gnu.org/licenses/>.
25* 25*
26* 26*
27*****************************************************************************/ 27*****************************************************************************/
28 28
29#define MAX_CN_LENGTH 256 29#define MAX_CN_LENGTH 256
@@ -193,12 +193,22 @@ int np_net_ssl_read(void *buf, int num) {
193 193
194int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){ 194int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){
195# ifdef USE_OPENSSL 195# ifdef USE_OPENSSL
196 X509 *certificate=NULL; 196 X509 *certificate = NULL;
197 certificate=SSL_get_peer_certificate(s);
198 return(np_net_ssl_check_certificate(certificate, days_till_exp_warn, days_till_exp_crit));
199# else /* ifndef USE_OPENSSL */
200 printf("%s\n", _("WARNING - Plugin does not support checking certificates."));
201 return STATE_WARNING;
202# endif /* USE_OPENSSL */
203}
204
205int np_net_ssl_check_certificate(X509 *certificate, int days_till_exp_warn, int days_till_exp_crit){
206# ifdef USE_OPENSSL
197 X509_NAME *subj=NULL; 207 X509_NAME *subj=NULL;
198 char timestamp[50] = ""; 208 char timestamp[50] = "";
199 char cn[MAX_CN_LENGTH]= ""; 209 char cn[MAX_CN_LENGTH]= "";
200 char *tz; 210 char *tz;
201 211
202 int cnlen =-1; 212 int cnlen =-1;
203 int status=STATE_UNKNOWN; 213 int status=STATE_UNKNOWN;
204 214
@@ -210,7 +220,6 @@ int np_net_ssl_check_cert(int days_till_exp_warn, int days_till_exp_crit){
210 int time_remaining; 220 int time_remaining;
211 time_t tm_t; 221 time_t tm_t;
212 222
213 certificate=SSL_get_peer_certificate(s);
214 if (!certificate) { 223 if (!certificate) {
215 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate.")); 224 printf("%s\n",_("CRITICAL - Cannot retrieve server certificate."));
216 return STATE_CRITICAL; 225 return STATE_CRITICAL;
diff --git a/plugins/t/check_curl.t b/plugins/t/check_curl.t
new file mode 100644
index 00000000..e67fafb6
--- /dev/null
+++ b/plugins/t/check_curl.t
@@ -0,0 +1,204 @@
1#! /usr/bin/perl -w -I ..
2#
3# HyperText Transfer Protocol (HTTP) Test via check_http
4#
5#
6
7use strict;
8use Test::More;
9use POSIX qw/mktime strftime/;
10use NPTest;
11
12plan tests => 49;
13
14my $successOutput = '/OK.*HTTP.*second/';
15
16my $res;
17my $plugin = 'check_http';
18$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
19
20my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP",
21 "A host providing the HTTP Service (a web server)",
22 "localhost" );
23
24my $host_tls_http = getTestParameter( "host_tls_http", "NP_HOST_TLS_HTTP", "localhost",
25 "A host providing the HTTPS Service (a tls web server)" );
26
27my $host_tls_cert = getTestParameter( "host_tls_cert", "NP_HOST_TLS_CERT", "localhost",
28 "the common name of the certificate." );
29
30
31my $host_nonresponsive = getTestParameter( "NP_HOST_NONRESPONSIVE",
32 "The hostname of system not responsive to network requests",
33 "10.0.0.1" );
34
35my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36 "An invalid (not known to DNS) hostname",
37 "nosuchhost");
38
39my $internet_access = getTestParameter( "NP_INTERNET_ACCESS",
40 "Is this system directly connected to the internet?",
41 "yes");
42
43my $host_tcp_http2 = getTestParameter( "NP_HOST_TCP_HTTP2",
44 "A host providing an index page containing the string 'monitoring'",
45 "test.monitoring-plugins.org" );
46
47my $faketime = -x '/usr/bin/faketime' ? 1 : 0;
48
49
50$res = NPTest->testCmd(
51 "./$plugin $host_tcp_http -wt 300 -ct 600"
52 );
53cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" );
54like( $res->output, $successOutput, "Output OK" );
55
56$res = NPTest->testCmd(
57 "./$plugin $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'"
58 );
59like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" );
60
61$res = NPTest->testCmd(
62 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
63 );
64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
65# was CRITICAL only, but both check_curl and check_http print HTTP CRITICAL (puzzle?!)
66cmp_ok( $res->output, 'eq', "HTTP CRITICAL - Invalid HTTP response received from host on port 80: cURL returned 28 - Timeout was reached", "Output OK");
67
68$res = NPTest->testCmd(
69 "./$plugin $hostname_invalid -wt 1 -ct 2"
70 );
71cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
72# The first part of the message comes from the OS catalogue, so cannot check this.
73# On Debian, it is Name or service not known, on Darwin, it is No address associated with nodename
74# Is also possible to get a socket timeout if DNS is not responding fast enough
75# cURL gives us consistent strings from it's own 'lib/strerror.c'
76like( $res->output, "/cURL returned 6 - Couldn't resolve host name/", "Output OK");
77
78# host header checks
79$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http");
80like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
81
82$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
83like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
84
85$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
86like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
87
88$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
89like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
90
91SKIP: {
92 skip "No internet access", 3 if $internet_access eq "no";
93
94 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
95 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
96
97 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
98 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
99
100 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
101 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
102};
103
104SKIP: {
105 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
106
107 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
108 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
109
110 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
111 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
112 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
113
114 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
115 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
116
117 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
118 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
119 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
120
121 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
122 cmp_ok( $res->return_code, "==", 0, "And also when not found");
123}
124SKIP: {
125 skip "No internet access", 16 if $internet_access eq "no";
126
127 $res = NPTest->testCmd(
128 "./$plugin --ssl $host_tls_http"
129 );
130 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
131
132 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
133 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
134 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
135 my $saved_cert_output = $res->output;
136
137 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
138 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
139 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
140
141 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
142 is( $res->return_code, 0, "Old syntax for cert checking okay" );
143 is( $res->output, $saved_cert_output, "Same output as new syntax" );
144
145 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
146 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
147 is( $res->output, $saved_cert_output, "Same output as new syntax" );
148
149 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
150 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
151
152 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
153 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
154
155 # run some certificate checks with faketime
156 SKIP: {
157 skip "No faketime binary found", 12 if !$faketime;
158 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
159 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
160 is( $res->return_code, 0, "Catch cert output exit code" );
161 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
162 if(!defined $year) {
163 die("parsing date failed from: ".$res->output);
164 }
165 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
166 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
167 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
168 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
169 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
170 is( $res->return_code, 2, "Output on expire date" );
171
172 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
173 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
174 is( $res->return_code, 2, "cert expires in 1 second exit code" );
175
176 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
177 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
178 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
179
180 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
181 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
182 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
183
184 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
185 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
186 is( $res->return_code, 2, "Certificate expired exit code" );
187 };
188
189 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
190 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
191 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
192
193 $res = NPTest->testCmd(
194 "./$plugin --ssl -H www.e-paycobalt.com"
195 );
196 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
197
198
199 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
200 is( $res->return_code, 0, "Redirection based on location is okay");
201
202 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
203 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
204}
diff --git a/plugins/t/check_http.t b/plugins/t/check_http.t
index 8bd484a0..281fa362 100644
--- a/plugins/t/check_http.t
+++ b/plugins/t/check_http.t
@@ -14,6 +14,8 @@ plan tests => 49;
14my $successOutput = '/OK.*HTTP.*second/'; 14my $successOutput = '/OK.*HTTP.*second/';
15 15
16my $res; 16my $res;
17my $plugin = 'check_http';
18$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
17 19
18my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP", 20my $host_tcp_http = getTestParameter( "NP_HOST_TCP_HTTP",
19 "A host providing the HTTP Service (a web server)", 21 "A host providing the HTTP Service (a web server)",
@@ -46,24 +48,24 @@ my $faketime = -x '/usr/bin/faketime' ? 1 : 0;
46 48
47 49
48$res = NPTest->testCmd( 50$res = NPTest->testCmd(
49 "./check_http $host_tcp_http -wt 300 -ct 600" 51 "./$plugin $host_tcp_http -wt 300 -ct 600"
50 ); 52 );
51cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" ); 53cmp_ok( $res->return_code, '==', 0, "Webserver $host_tcp_http responded" );
52like( $res->output, $successOutput, "Output OK" ); 54like( $res->output, $successOutput, "Output OK" );
53 55
54$res = NPTest->testCmd( 56$res = NPTest->testCmd(
55 "./check_http $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'" 57 "./$plugin $host_tcp_http -wt 300 -ct 600 -v -v -v -k 'bob:there' -k 'carl:frown'"
56 ); 58 );
57like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" ); 59like( $res->output, '/bob:there\r\ncarl:frown\r\n/', "Got headers with multiple -k options" );
58 60
59$res = NPTest->testCmd( 61$res = NPTest->testCmd(
60 "./check_http $host_nonresponsive -wt 1 -ct 2 -t 3" 62 "./$plugin $host_nonresponsive -wt 1 -ct 2 -t 3"
61 ); 63 );
62cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" ); 64cmp_ok( $res->return_code, '==', 2, "Webserver $host_nonresponsive not responding" );
63cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK"); 65cmp_ok( $res->output, 'eq', "CRITICAL - Socket timeout after 3 seconds", "Output OK");
64 66
65$res = NPTest->testCmd( 67$res = NPTest->testCmd(
66 "./check_http $hostname_invalid -wt 1 -ct 2" 68 "./$plugin $hostname_invalid -wt 1 -ct 2"
67 ); 69 );
68cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" ); 70cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
69# The first part of the message comes from the OS catalogue, so cannot check this. 71# The first part of the message comes from the OS catalogue, so cannot check this.
@@ -72,86 +74,86 @@ cmp_ok( $res->return_code, '==', 2, "Webserver $hostname_invalid not valid" );
72like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK"); 74like( $res->output, "/Unable to open TCP socket|Socket timeout after/", "Output OK");
73 75
74# host header checks 76# host header checks
75$res = NPTest->testCmd("./check_http -v -H $host_tcp_http"); 77$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http");
76like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 78like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
77 79
78$res = NPTest->testCmd("./check_http -v -H $host_tcp_http -p 80"); 80$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http -p 80");
79like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" ); 81like( $res->output, '/^Host: '.$host_tcp_http.'\s*$/ms', "Host Header OK" );
80 82
81$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 83$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
82like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 84like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
83 85
84$res = NPTest->testCmd("./check_http -v -H $host_tcp_http:8080 -p 80"); 86$res = NPTest->testCmd("./$plugin -v -H $host_tcp_http:8080 -p 80");
85like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" ); 87like( $res->output, '/^Host: '.$host_tcp_http.':8080\s*$/ms', "Host Header OK" );
86 88
87SKIP: { 89SKIP: {
88 skip "No internet access", 3 if $internet_access eq "no"; 90 skip "No internet access", 3 if $internet_access eq "no";
89 91
90 $res = NPTest->testCmd("./check_http -v -H $host_tls_http -S"); 92 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http -S");
91 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 93 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
92 94
93 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:8080 -S -p 443"); 95 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:8080 -S -p 443");
94 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" ); 96 like( $res->output, '/^Host: '.$host_tls_http.':8080\s*$/ms', "Host Header OK" );
95 97
96 $res = NPTest->testCmd("./check_http -v -H $host_tls_http:443 -S -p 443"); 98 $res = NPTest->testCmd("./$plugin -v -H $host_tls_http:443 -S -p 443");
97 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" ); 99 like( $res->output, '/^Host: '.$host_tls_http.'\s*$/ms', "Host Header OK" );
98}; 100};
99 101
100SKIP: { 102SKIP: {
101 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2; 103 skip "No host serving monitoring in index file", 7 unless $host_tcp_http2;
102 104
103 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring'" ); 105 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring'" );
104 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'"); 106 cmp_ok( $res->return_code, "==", 0, "Got a reference to 'monitoring'");
105 107
106 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing'" ); 108 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing'" );
107 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'"); 109 cmp_ok( $res->return_code, "==", 2, "Not got 'mONiTORing'");
108 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'"); 110 like ( $res->output, "/pattern not found/", "Error message says 'pattern not found'");
109 111
110 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -R 'mONiTORing'" ); 112 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -R 'mONiTORing'" );
111 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'"); 113 cmp_ok( $res->return_code, "==", 0, "But case insensitive doesn't mind 'mONiTORing'");
112 114
113 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'monitoring' --invert-regex" ); 115 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'monitoring' --invert-regex" );
114 cmp_ok( $res->return_code, "==", 2, "Invert results work when found"); 116 cmp_ok( $res->return_code, "==", 2, "Invert results work when found");
115 like ( $res->output, "/pattern found/", "Error message says 'pattern found'"); 117 like ( $res->output, "/pattern found/", "Error message says 'pattern found'");
116 118
117 $res = NPTest->testCmd( "./check_http -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" ); 119 $res = NPTest->testCmd( "./$plugin -H $host_tcp_http2 -r 'mONiTORing' --invert-regex" );
118 cmp_ok( $res->return_code, "==", 0, "And also when not found"); 120 cmp_ok( $res->return_code, "==", 0, "And also when not found");
119} 121}
120SKIP: { 122SKIP: {
121 skip "No internet access", 16 if $internet_access eq "no"; 123 skip "No internet access", 16 if $internet_access eq "no";
122 124
123 $res = NPTest->testCmd( 125 $res = NPTest->testCmd(
124 "./check_http --ssl $host_tls_http" 126 "./$plugin --ssl $host_tls_http"
125 ); 127 );
126 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" ); 128 cmp_ok( $res->return_code, '==', 0, "Can read https for $host_tls_http" );
127 129
128 $res = NPTest->testCmd( "./check_http -C 1 --ssl $host_tls_http" ); 130 $res = NPTest->testCmd( "./$plugin -C 1 --ssl $host_tls_http" );
129 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http"); 131 cmp_ok( $res->return_code, '==', 0, "Checking certificate for $host_tls_http");
130 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" ); 132 like ( $res->output, "/Certificate '$host_tls_cert' will expire on/", "Output OK" );
131 my $saved_cert_output = $res->output; 133 my $saved_cert_output = $res->output;
132 134
133 $res = NPTest->testCmd( "./check_http -C 8000,1 --ssl $host_tls_http" ); 135 $res = NPTest->testCmd( "./$plugin -C 8000,1 --ssl $host_tls_http" );
134 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http"); 136 cmp_ok( $res->return_code, '==', 1, "Checking certificate for $host_tls_http");
135 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" ); 137 like ( $res->output, qr/WARNING - Certificate '$host_tls_cert' expires in \d+ day/, "Output Warning" );
136 138
137 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 139 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
138 is( $res->return_code, 0, "Old syntax for cert checking okay" ); 140 is( $res->return_code, 0, "Old syntax for cert checking okay" );
139 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 141 is( $res->output, $saved_cert_output, "Same output as new syntax" );
140 142
141 $res = NPTest->testCmd( "./check_http -H $host_tls_http -C 1" ); 143 $res = NPTest->testCmd( "./$plugin -H $host_tls_http -C 1" );
142 is( $res->return_code, 0, "Updated syntax for cert checking okay" ); 144 is( $res->return_code, 0, "Updated syntax for cert checking okay" );
143 is( $res->output, $saved_cert_output, "Same output as new syntax" ); 145 is( $res->output, $saved_cert_output, "Same output as new syntax" );
144 146
145 $res = NPTest->testCmd( "./check_http -C 1 $host_tls_http" ); 147 $res = NPTest->testCmd( "./$plugin -C 1 $host_tls_http" );
146 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added"); 148 cmp_ok( $res->output, 'eq', $saved_cert_output, "--ssl option automatically added");
147 149
148 $res = NPTest->testCmd( "./check_http $host_tls_http -C 1" ); 150 $res = NPTest->testCmd( "./$plugin $host_tls_http -C 1" );
149 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works"); 151 cmp_ok( $res->output, 'eq', $saved_cert_output, "Old syntax for cert checking still works");
150 152
151 # run some certificate checks with faketime 153 # run some certificate checks with faketime
152 SKIP: { 154 SKIP: {
153 skip "No faketime binary found", 12 if !$faketime; 155 skip "No faketime binary found", 12 if !$faketime;
154 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./check_http -C 1 $host_tls_http"); 156 $res = NPTest->testCmd("LC_TIME=C TZ=UTC ./$plugin -C 1 $host_tls_http");
155 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output"); 157 like($res->output, qr/OK - Certificate '$host_tls_cert' will expire on/, "Catch cert output");
156 is( $res->return_code, 0, "Catch cert output exit code" ); 158 is( $res->return_code, 0, "Catch cert output exit code" );
157 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/); 159 my($mon,$day,$hour,$min,$sec,$year) = ($res->output =~ /(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\d+)/);
@@ -161,40 +163,40 @@ SKIP: {
161 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11}; 163 my $months = {'Jan' => 0, 'Feb' => 1, 'Mar' => 2, 'Apr' => 3, 'May' => 4, 'Jun' => 5, 'Jul' => 6, 'Aug' => 7, 'Sep' => 8, 'Oct' => 9, 'Nov' => 10, 'Dec' => 11};
162 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900); 164 my $ts = mktime($sec, $min, $hour, $day, $months->{$mon}, $year-1900);
163 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts)); 165 my $time = strftime("%Y-%m-%d %H:%M:%S", localtime($ts));
164 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./check_http -C 1 $host_tls_http"); 166 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts))."' ./$plugin -C 1 $host_tls_http");
165 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date"); 167 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' just expired/, "Output on expire date");
166 is( $res->return_code, 2, "Output on expire date" ); 168 is( $res->return_code, 2, "Output on expire date" );
167 169
168 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./check_http -C 1 $host_tls_http"); 170 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-1))."' ./$plugin -C 1 $host_tls_http");
169 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output"); 171 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 0 minutes/, "cert expires in 1 second output");
170 is( $res->return_code, 2, "cert expires in 1 second exit code" ); 172 is( $res->return_code, 2, "cert expires in 1 second exit code" );
171 173
172 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./check_http -C 1 $host_tls_http"); 174 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-120))."' ./$plugin -C 1 $host_tls_http");
173 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output"); 175 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 minutes/, "cert expires in 2 minutes output");
174 is( $res->return_code, 2, "cert expires in 2 minutes exit code" ); 176 is( $res->return_code, 2, "cert expires in 2 minutes exit code" );
175 177
176 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./check_http -C 1 $host_tls_http"); 178 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts-7200))."' ./$plugin -C 1 $host_tls_http");
177 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output"); 179 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expires in 2 hours/, "cert expires in 2 hours output");
178 is( $res->return_code, 2, "cert expires in 2 hours exit code" ); 180 is( $res->return_code, 2, "cert expires in 2 hours exit code" );
179 181
180 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./check_http -C 1 $host_tls_http"); 182 $res = NPTest->testCmd("LC_TIME=C TZ=UTC faketime -f '".strftime("%Y-%m-%d %H:%M:%S", localtime($ts+1))."' ./$plugin -C 1 $host_tls_http");
181 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output"); 183 like($res->output, qr/CRITICAL - Certificate '$host_tls_cert' expired on/, "Certificate expired output");
182 is( $res->return_code, 2, "Certificate expired exit code" ); 184 is( $res->return_code, 2, "Certificate expired exit code" );
183 }; 185 };
184 186
185 $res = NPTest->testCmd( "./check_http --ssl $host_tls_http -E" ); 187 $res = NPTest->testCmd( "./$plugin --ssl $host_tls_http -E" );
186 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 188 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
187 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' ); 189 like ( $res->output, '/time_ssl=[\d\.]+/', 'Extended Performance Data SSL Output OK' );
188 190
189 $res = NPTest->testCmd( 191 $res = NPTest->testCmd(
190 "./check_http --ssl -H www.e-paycobalt.com" 192 "./$plugin --ssl -H www.e-paycobalt.com"
191 ); 193 );
192 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" ); 194 cmp_ok( $res->return_code, "==", 0, "Can read https for www.e-paycobalt.com (uses AES certificate)" );
193 195
194 196
195 $res = NPTest->testCmd( "./check_http -H www.mozilla.com -u /firefox -f follow" ); 197 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com -u /firefox -f follow" );
196 is( $res->return_code, 0, "Redirection based on location is okay"); 198 is( $res->return_code, 0, "Redirection based on location is okay");
197 199
198 $res = NPTest->testCmd( "./check_http -H www.mozilla.com --extended-perfdata" ); 200 $res = NPTest->testCmd( "./$plugin -H www.mozilla.com --extended-perfdata" );
199 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' ); 201 like ( $res->output, '/time_connect=[\d\.]+/', 'Extended Performance Data Output OK' );
200} 202}
diff --git a/plugins/tests/check_curl.t b/plugins/tests/check_curl.t
new file mode 100755
index 00000000..28dacd0d
--- /dev/null
+++ b/plugins/tests/check_curl.t
@@ -0,0 +1,455 @@
1#! /usr/bin/perl -w -I ..
2#
3# Test check_http by having an actual HTTP server running
4#
5# To create the https server certificate:
6# openssl req -new -x509 -keyout server-key.pem -out server-cert.pem -days 3650 -nodes
7# Country Name (2 letter code) [AU]:UK
8# State or Province Name (full name) [Some-State]:Derbyshire
9# Locality Name (eg, city) []:Belper
10# Organization Name (eg, company) [Internet Widgits Pty Ltd]:Monitoring Plugins
11# Organizational Unit Name (eg, section) []:
12# Common Name (eg, YOUR name) []:Ton Voon
13# Email Address []:tonvoon@mac.com
14
15use strict;
16use Test::More;
17use NPTest;
18use FindBin qw($Bin);
19
20$ENV{'LC_TIME'} = "C";
21
22my $common_tests = 70;
23my $virtual_port_tests = 8;
24my $ssl_only_tests = 8;
25# Check that all dependent modules are available
26eval "use HTTP::Daemon 6.01;";
27plan skip_all => 'HTTP::Daemon >= 6.01 required' if $@;
28eval {
29 require HTTP::Status;
30 require HTTP::Response;
31};
32
33my $plugin = 'check_http';
34$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
35
36if ($@) {
37 plan skip_all => "Missing required module for test: $@";
38} else {
39 if (-x "./$plugin") {
40 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests;
41 } else {
42 plan skip_all => "No $plugin compiled";
43 }
44}
45
46my $servers = { http => 0 }; # HTTP::Daemon should always be available
47eval { require HTTP::Daemon::SSL };
48if ($@) {
49 diag "Cannot load HTTP::Daemon::SSL: $@";
50} else {
51 $servers->{https} = 0;
52}
53
54# set a fixed version, so the header size doesn't vary
55$HTTP::Daemon::VERSION = "1.00";
56
57my $port_http = 50000 + int(rand(1000));
58my $port_https = $port_http + 1;
59my $port_https_expired = $port_http + 2;
60
61# This array keeps sockets around for implementing timeouts
62my @persist;
63
64# Start up all servers
65my @pids;
66my $pid = fork();
67if ($pid) {
68 # Parent
69 push @pids, $pid;
70 if (exists $servers->{https}) {
71 # Fork a normal HTTPS server
72 $pid = fork();
73 if ($pid) {
74 # Parent
75 push @pids, $pid;
76 # Fork an expired cert server
77 $pid = fork();
78 if ($pid) {
79 push @pids, $pid;
80 } else {
81 my $d = HTTP::Daemon::SSL->new(
82 LocalPort => $port_https_expired,
83 LocalAddr => "127.0.0.1",
84 SSL_cert_file => "$Bin/certs/expired-cert.pem",
85 SSL_key_file => "$Bin/certs/expired-key.pem",
86 ) || die;
87 print "Please contact https expired at: <URL:", $d->url, ">\n";
88 run_server( $d );
89 exit;
90 }
91 } else {
92 my $d = HTTP::Daemon::SSL->new(
93 LocalPort => $port_https,
94 LocalAddr => "127.0.0.1",
95 SSL_cert_file => "$Bin/certs/server-cert.pem",
96 SSL_key_file => "$Bin/certs/server-key.pem",
97 ) || die;
98 print "Please contact https at: <URL:", $d->url, ">\n";
99 run_server( $d );
100 exit;
101 }
102 }
103 # give our webservers some time to startup
104 sleep(1);
105} else {
106 # Child
107 #print "child\n";
108 my $d = HTTP::Daemon->new(
109 LocalPort => $port_http,
110 LocalAddr => "127.0.0.1",
111 ) || die;
112 print "Please contact http at: <URL:", $d->url, ">\n";
113 run_server( $d );
114 exit;
115}
116
117# Run the same server on http and https
118sub run_server {
119 my $d = shift;
120 MAINLOOP: while (my $c = $d->accept ) {
121 while (my $r = $c->get_request) {
122 if ($r->method eq "GET" and $r->url->path =~ m^/statuscode/(\d+)^) {
123 $c->send_basic_header($1);
124 $c->send_crlf;
125 } elsif ($r->method eq "GET" and $r->url->path =~ m^/file/(.*)^) {
126 $c->send_basic_header;
127 $c->send_crlf;
128 $c->send_file_response("$Bin/var/$1");
129 } elsif ($r->method eq "GET" and $r->url->path eq "/slow") {
130 $c->send_basic_header;
131 $c->send_crlf;
132 sleep 1;
133 $c->send_response("slow");
134 } elsif ($r->url->path eq "/method") {
135 if ($r->method eq "DELETE") {
136 $c->send_error(HTTP::Status->RC_METHOD_NOT_ALLOWED);
137 } elsif ($r->method eq "foo") {
138 $c->send_error(HTTP::Status->RC_NOT_IMPLEMENTED);
139 } else {
140 $c->send_status_line(200, $r->method);
141 }
142 } elsif ($r->url->path eq "/postdata") {
143 $c->send_basic_header;
144 $c->send_crlf;
145 $c->send_response($r->method.":".$r->content);
146 } elsif ($r->url->path eq "/redirect") {
147 $c->send_redirect( "/redirect2" );
148 } elsif ($r->url->path eq "/redir_external") {
149 $c->send_redirect(($d->isa('HTTP::Daemon::SSL') ? "https" : "http") . "://169.254.169.254/redirect2" );
150 } elsif ($r->url->path eq "/redirect2") {
151 $c->send_basic_header;
152 $c->send_crlf;
153 $c->send_response(HTTP::Response->new( 200, 'OK', undef, 'redirected' ));
154 } elsif ($r->url->path eq "/redir_timeout") {
155 $c->send_redirect( "/timeout" );
156 } elsif ($r->url->path eq "/timeout") {
157 # Keep $c from being destroyed, but prevent severe leaks
158 unshift @persist, $c;
159 delete($persist[1000]);
160 next MAINLOOP;
161 } elsif ($r->url->path eq "/header_check") {
162 $c->send_basic_header;
163 $c->send_header('foo');
164 $c->send_crlf;
165 } elsif ($r->url->path eq "/virtual_port") {
166 # return sent Host header
167 $c->send_basic_header;
168 $c->send_crlf;
169 $c->send_response(HTTP::Response->new( 200, 'OK', undef, $r->header ('Host')));
170 } else {
171 $c->send_error(HTTP::Status->RC_FORBIDDEN);
172 }
173 $c->close;
174 }
175 }
176}
177
178END {
179 foreach my $pid (@pids) {
180 if ($pid) { print "Killing $pid\n"; kill "INT", $pid }
181 }
182};
183
184if ($ARGV[0] && $ARGV[0] eq "-d") {
185 while (1) {
186 sleep 100;
187 }
188}
189
190my $result;
191my $command = "./$plugin -H 127.0.0.1";
192
193run_common_tests( { command => "$command -p $port_http" } );
194SKIP: {
195 skip "HTTP::Daemon::SSL not installed", $common_tests + $ssl_only_tests if ! exists $servers->{https};
196 run_common_tests( { command => "$command -p $port_https", ssl => 1 } );
197
198 $result = NPTest->testCmd( "$command -p $port_https -S -C 14" );
199 is( $result->return_code, 0, "$command -p $port_https -S -C 14" );
200 is( $result->output, 'OK - Certificate \'Ton Voon\' will expire on Sun Mar 3 21:41:28 2019 +0000.', "output ok" );
201
202 $result = NPTest->testCmd( "$command -p $port_https -S -C 14000" );
203 is( $result->return_code, 1, "$command -p $port_https -S -C 14000" );
204 like( $result->output, '/WARNING - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
205
206 # Expired cert tests
207 $result = NPTest->testCmd( "$command -p $port_https -S -C 13960,14000" );
208 is( $result->return_code, 2, "$command -p $port_https -S -C 13960,14000" );
209 like( $result->output, '/CRITICAL - Certificate \'Ton Voon\' expires in \d+ day\(s\) \(Sun Mar 3 21:41:28 2019 \+0000\)./', "output ok" );
210
211 $result = NPTest->testCmd( "$command -p $port_https_expired -S -C 7" );
212 is( $result->return_code, 2, "$command -p $port_https_expired -S -C 7" );
213 is( $result->output,
214 'CRITICAL - Certificate \'Ton Voon\' expired on Thu Mar 5 00:13:16 2009 +0000.',
215 "output ok" );
216
217}
218
219my $cmd;
220# check virtual port behaviour
221#
222# http without virtual port
223$cmd = "$command -p $port_http -u /virtual_port -r ^127.0.0.1:$port_http\$";
224$result = NPTest->testCmd( $cmd );
225is( $result->return_code, 0, $cmd);
226like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
227
228# http with virtual port
229$cmd = "$command:80 -p $port_http -u /virtual_port -r ^127.0.0.1\$";
230$result = NPTest->testCmd( $cmd );
231is( $result->return_code, 0, $cmd);
232like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
233
234SKIP: {
235 skip "HTTP::Daemon::SSL not installed", 4 if ! exists $servers->{https};
236 # https without virtual port
237 $cmd = "$command -p $port_https --ssl -u /virtual_port -r ^127.0.0.1:$port_https\$";
238 $result = NPTest->testCmd( $cmd );
239 is( $result->return_code, 0, $cmd);
240 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
241
242 # https with virtual port
243 $cmd = "$command:443 -p $port_https --ssl -u /virtual_port -r ^127.0.0.1\$";
244 $result = NPTest->testCmd( $cmd );
245 is( $result->return_code, 0, $cmd);
246 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
247}
248
249
250sub run_common_tests {
251 my ($opts) = @_;
252 my $command = $opts->{command};
253 if ($opts->{ssl}) {
254 $command .= " --ssl";
255 }
256
257 $result = NPTest->testCmd( "$command -u /file/root" );
258 is( $result->return_code, 0, "/file/root");
259 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
260
261 $result = NPTest->testCmd( "$command -u /file/root -s Root" );
262 is( $result->return_code, 0, "/file/root search for string");
263 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 274 bytes in [\d\.]+ second/', "Output correct" );
264
265 $result = NPTest->testCmd( "$command -u /file/root -s NonRoot" );
266 is( $result->return_code, 2, "Missing string check");
267 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRoot' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
268
269 $result = NPTest->testCmd( "$command -u /file/root -s NonRootWithOver30charsAndMoreFunThanAWetFish" );
270 is( $result->return_code, 2, "Missing string check");
271 like( $result->output, qr%HTTP CRITICAL: HTTP/1\.1 200 OK - string 'NonRootWithOver30charsAndM...' not found on 'https?://127\.0\.0\.1:\d+/file/root'%, "Shows search string and location");
272
273 $result = NPTest->testCmd( "$command -u /header_check -d foo" );
274 is( $result->return_code, 0, "header_check search for string");
275 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - 96 bytes in [\d\.]+ second/', "Output correct" );
276
277 $result = NPTest->testCmd( "$command -u /header_check -d bar" );
278 is( $result->return_code, 2, "Missing header string check");
279 like( $result->output, qr%^HTTP CRITICAL: HTTP/1\.1 200 OK - header 'bar' not found on 'https?://127\.0\.0\.1:\d+/header_check'%, "Shows search string and location");
280
281 my $cmd;
282 $cmd = "$command -u /slow";
283 $result = NPTest->testCmd( $cmd );
284 is( $result->return_code, 0, "$cmd");
285 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
286 $result->output =~ /in ([\d\.]+) second/;
287 cmp_ok( $1, ">", 1, "Time is > 1 second" );
288
289 $cmd = "$command -u /statuscode/200";
290 $result = NPTest->testCmd( $cmd );
291 is( $result->return_code, 0, $cmd);
292 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
293
294 $cmd = "$command -u /statuscode/200 -e 200";
295 $result = NPTest->testCmd( $cmd );
296 is( $result->return_code, 0, $cmd);
297 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
298
299 $cmd = "$command -u /statuscode/201";
300 $result = NPTest->testCmd( $cmd );
301 is( $result->return_code, 0, $cmd);
302 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
303
304 $cmd = "$command -u /statuscode/201 -e 201";
305 $result = NPTest->testCmd( $cmd );
306 is( $result->return_code, 0, $cmd);
307 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "201" - \d+ bytes in [\d\.]+ second /', "Output correct: ".$result->output );
308
309 $cmd = "$command -u /statuscode/201 -e 200";
310 $result = NPTest->testCmd( $cmd );
311 is( $result->return_code, 2, $cmd);
312 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port \d+: HTTP/1.1 201 Created/', "Output correct: ".$result->output );
313
314 $cmd = "$command -u /statuscode/200 -e 200,201,202";
315 $result = NPTest->testCmd( $cmd );
316 is( $result->return_code, 0, $cmd);
317 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
318
319 $cmd = "$command -u /statuscode/201 -e 200,201,202";
320 $result = NPTest->testCmd( $cmd );
321 is( $result->return_code, 0, $cmd);
322 like( $result->output, '/^HTTP OK: HTTP/1.1 201 Created - Status line output matched "200,201,202" - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
323
324 $cmd = "$command -u /statuscode/203 -e 200,201,202";
325 $result = NPTest->testCmd( $cmd );
326 is( $result->return_code, 2, $cmd);
327 like( $result->output, '/^HTTP CRITICAL - Invalid HTTP response received from host on port (\d+): HTTP/1.1 203 Non-Authoritative Information/', "Output correct: ".$result->output );
328
329 $cmd = "$command -j HEAD -u /method";
330 $result = NPTest->testCmd( $cmd );
331 is( $result->return_code, 0, $cmd);
332 like( $result->output, '/^HTTP OK: HTTP/1.1 200 HEAD - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
333
334 $cmd = "$command -j POST -u /method";
335 $result = NPTest->testCmd( $cmd );
336 is( $result->return_code, 0, $cmd);
337 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
338
339 $cmd = "$command -j GET -u /method";
340 $result = NPTest->testCmd( $cmd );
341 is( $result->return_code, 0, $cmd);
342 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
343
344 $cmd = "$command -u /method";
345 $result = NPTest->testCmd( $cmd );
346 is( $result->return_code, 0, $cmd);
347 like( $result->output, '/^HTTP OK: HTTP/1.1 200 GET - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
348
349 $cmd = "$command -P foo -u /method";
350 $result = NPTest->testCmd( $cmd );
351 is( $result->return_code, 0, $cmd);
352 like( $result->output, '/^HTTP OK: HTTP/1.1 200 POST - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
353
354 $cmd = "$command -j DELETE -u /method";
355 $result = NPTest->testCmd( $cmd );
356 is( $result->return_code, 1, $cmd);
357 like( $result->output, '/^HTTP WARNING: HTTP/1.1 405 Method Not Allowed/', "Output correct: ".$result->output );
358
359 $cmd = "$command -j foo -u /method";
360 $result = NPTest->testCmd( $cmd );
361 is( $result->return_code, 2, $cmd);
362 like( $result->output, '/^HTTP CRITICAL: HTTP/1.1 501 Not Implemented/', "Output correct: ".$result->output );
363
364 $cmd = "$command -P stufftoinclude -u /postdata -s POST:stufftoinclude";
365 $result = NPTest->testCmd( $cmd );
366 is( $result->return_code, 0, $cmd);
367 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
368
369 $cmd = "$command -j PUT -P stufftoinclude -u /postdata -s PUT:stufftoinclude";
370 $result = NPTest->testCmd( $cmd );
371 is( $result->return_code, 0, $cmd);
372 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
373
374 # To confirm that the free doesn't segfault
375 $cmd = "$command -P stufftoinclude -j PUT -u /postdata -s PUT:stufftoinclude";
376 $result = NPTest->testCmd( $cmd );
377 is( $result->return_code, 0, $cmd);
378 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
379
380 $cmd = "$command -u /redirect";
381 $result = NPTest->testCmd( $cmd );
382 is( $result->return_code, 0, $cmd);
383 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
384
385 $cmd = "$command -f follow -u /redirect";
386 $result = NPTest->testCmd( $cmd );
387 is( $result->return_code, 0, $cmd);
388 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
389
390 $cmd = "$command -u /redirect -k 'follow: me'";
391 $result = NPTest->testCmd( $cmd );
392 is( $result->return_code, 0, $cmd);
393 like( $result->output, '/^HTTP OK: HTTP/1.1 301 Moved Permanently - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
394
395 $cmd = "$command -f follow -u /redirect -k 'follow: me'";
396 $result = NPTest->testCmd( $cmd );
397 is( $result->return_code, 0, $cmd);
398 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
399
400 $cmd = "$command -f sticky -u /redirect -k 'follow: me'";
401 $result = NPTest->testCmd( $cmd );
402 is( $result->return_code, 0, $cmd);
403 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
404
405 $cmd = "$command -f stickyport -u /redirect -k 'follow: me'";
406 $result = NPTest->testCmd( $cmd );
407 is( $result->return_code, 0, $cmd);
408 like( $result->output, '/^HTTP OK: HTTP/1.1 200 OK - \d+ bytes in [\d\.]+ second/', "Output correct: ".$result->output );
409
410 # These tests may block
411 print "ALRM\n";
412
413 # stickyport - on full urlS port is set back to 80 otherwise
414 $cmd = "$command -f stickyport -u /redir_external -t 5 -s redirected";
415 eval {
416 local $SIG{ALRM} = sub { die "alarm\n" };
417 alarm(2);
418 $result = NPTest->testCmd( $cmd );
419 alarm(0); };
420 isnt( $@, "alarm\n", $cmd );
421 is( $result->return_code, 0, $cmd );
422
423 # Let's hope there won't be any web server on :80 returning "redirected"!
424 $cmd = "$command -f sticky -u /redir_external -t 5 -s redirected";
425 eval {
426 local $SIG{ALRM} = sub { die "alarm\n" };
427 alarm(2);
428 $result = NPTest->testCmd( $cmd );
429 alarm(0); };
430 isnt( $@, "alarm\n", $cmd );
431 isnt( $result->return_code, 0, $cmd );
432
433 # Test an external address - timeout
434 SKIP: {
435 skip "This doesn't seem to work all the time", 1 unless ($ENV{HTTP_EXTERNAL});
436 $cmd = "$command -f follow -u /redir_external -t 5";
437 eval {
438 $result = NPTest->testCmd( $cmd, 2 );
439 };
440 like( $@, "/timeout in command: $cmd/", $cmd );
441 }
442
443 $cmd = "$command -u /timeout -t 5";
444 eval {
445 $result = NPTest->testCmd( $cmd, 2 );
446 };
447 like( $@, "/timeout in command: $cmd/", $cmd );
448
449 $cmd = "$command -f follow -u /redir_timeout -t 2";
450 eval {
451 $result = NPTest->testCmd( $cmd, 5 );
452 };
453 is( $@, "", $cmd );
454
455}
diff --git a/plugins/tests/check_http.t b/plugins/tests/check_http.t
index d6d31de1..c9e65951 100755
--- a/plugins/tests/check_http.t
+++ b/plugins/tests/check_http.t
@@ -30,13 +30,16 @@ eval {
30 require HTTP::Response; 30 require HTTP::Response;
31}; 31};
32 32
33my $plugin = 'check_http';
34$plugin = 'check_curl' if $0 =~ m/check_curl/mx;
35
33if ($@) { 36if ($@) {
34 plan skip_all => "Missing required module for test: $@"; 37 plan skip_all => "Missing required module for test: $@";
35} else { 38} else {
36 if (-x "./check_http") { 39 if (-x "./check_http") {
37 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests; 40 plan tests => $common_tests * 2 + $ssl_only_tests + $virtual_port_tests;
38 } else { 41 } else {
39 plan skip_all => "No check_http compiled"; 42 plan skip_all => "No $plugin compiled";
40 } 43 }
41} 44}
42 45
@@ -185,7 +188,7 @@ if ($ARGV[0] && $ARGV[0] eq "-d") {
185} 188}
186 189
187my $result; 190my $result;
188my $command = "./check_http -H 127.0.0.1"; 191my $command = "./$plugin -H 127.0.0.1";
189 192
190run_common_tests( { command => "$command -p $port_http" } ); 193run_common_tests( { command => "$command -p $port_http" } );
191SKIP: { 194SKIP: {
diff --git a/plugins/uriparser/Makefile.am b/plugins/uriparser/Makefile.am
new file mode 100644
index 00000000..703e6c51
--- /dev/null
+++ b/plugins/uriparser/Makefile.am
@@ -0,0 +1,11 @@
1noinst_LIBRARIES = liburiparser.a
2
3liburiparser_a_SOURCES = UriCommon.c UriCompare.c \
4 UriEscape.c UriFile.c UriIp4.c \
5 UriIp4Base.c UriNormalize.c \
6 UriNormalizeBase.c UriParse.c \
7 UriParseBase.c UriQuery.c \
8 UriRecompose.c UriResolve.c \
9 UriShorten.c
10
11liburiparser_a_CFLAGS = $(AM_CFLAGS) -I..
diff --git a/plugins/uriparser/Uri.h b/plugins/uriparser/Uri.h
new file mode 100644
index 00000000..4a185808
--- /dev/null
+++ b/plugins/uriparser/Uri.h
@@ -0,0 +1,777 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file Uri.h
42 * Holds the RFC 3986 %URI parser interface.
43 * NOTE: This header includes itself twice.
44 */
45
46#if (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI)) \
47 || (defined(URI_PASS_UNICODE) && !defined(URI_H_UNICODE)) \
48 || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
49/* What encodings are enabled? */
50#include "UriDefsConfig.h"
51#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
52/* Include SELF twice */
53# ifdef URI_ENABLE_ANSI
54# define URI_PASS_ANSI 1
55# include "Uri.h"
56# undef URI_PASS_ANSI
57# endif
58# ifdef URI_ENABLE_UNICODE
59# define URI_PASS_UNICODE 1
60# include "Uri.h"
61# undef URI_PASS_UNICODE
62# endif
63/* Only one pass for each encoding */
64#elif (defined(URI_PASS_ANSI) && !defined(URI_H_ANSI) \
65 && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
66 && !defined(URI_H_UNICODE) && defined(URI_ENABLE_UNICODE))
67# ifdef URI_PASS_ANSI
68# define URI_H_ANSI 1
69# include "UriDefsAnsi.h"
70# else
71# define URI_H_UNICODE 1
72# include "UriDefsUnicode.h"
73# endif
74
75
76
77#ifdef __cplusplus
78extern "C" {
79#endif
80
81
82
83#ifndef URI_DOXYGEN
84# include "UriBase.h"
85#endif
86
87
88
89/**
90 * Specifies a range of characters within a string.
91 * The range includes all characters from <c>first</c>
92 * to one before <c>afterLast</c>. So if both are
93 * non-NULL the difference is the length of the text range.
94 *
95 * @see UriUriA
96 * @see UriPathSegmentA
97 * @see UriHostDataA
98 * @since 0.3.0
99 */
100typedef struct URI_TYPE(TextRangeStruct) {
101 const URI_CHAR * first; /**< Pointer to first character */
102 const URI_CHAR * afterLast; /**< Pointer to character after the last one still in */
103} URI_TYPE(TextRange); /**< @copydoc UriTextRangeStructA */
104
105
106
107/**
108 * Represents a path segment within a %URI path.
109 * More precisely it is a node in a linked
110 * list of path segments.
111 *
112 * @see UriUriA
113 * @since 0.3.0
114 */
115typedef struct URI_TYPE(PathSegmentStruct) {
116 URI_TYPE(TextRange) text; /**< Path segment name */
117 struct URI_TYPE(PathSegmentStruct) * next; /**< Pointer to the next path segment in the list, can be NULL if last already */
118
119 void * reserved; /**< Reserved to the parser */
120} URI_TYPE(PathSegment); /**< @copydoc UriPathSegmentStructA */
121
122
123
124/**
125 * Holds structured host information.
126 * This is either a IPv4, IPv6, plain
127 * text for IPvFuture or all zero for
128 * a registered name.
129 *
130 * @see UriUriA
131 * @since 0.3.0
132 */
133typedef struct URI_TYPE(HostDataStruct) {
134 UriIp4 * ip4; /**< IPv4 address */
135 UriIp6 * ip6; /**< IPv6 address */
136 URI_TYPE(TextRange) ipFuture; /**< IPvFuture address */
137} URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */
138
139
140
141/**
142 * Represents an RFC 3986 %URI.
143 * Missing components can be {NULL, NULL} ranges.
144 *
145 * @see uriParseUriA
146 * @see uriFreeUriMembersA
147 * @see UriParserStateA
148 * @since 0.3.0
149 */
150typedef struct URI_TYPE(UriStruct) {
151 URI_TYPE(TextRange) scheme; /**< Scheme (e.g. "http") */
152 URI_TYPE(TextRange) userInfo; /**< User info (e.g. "user:pass") */
153 URI_TYPE(TextRange) hostText; /**< Host text (set for all hosts, excluding square brackets) */
154 URI_TYPE(HostData) hostData; /**< Structured host type specific data */
155 URI_TYPE(TextRange) portText; /**< Port (e.g. "80") */
156 URI_TYPE(PathSegment) * pathHead; /**< Head of a linked list of path segments */
157 URI_TYPE(PathSegment) * pathTail; /**< Tail of the list behind pathHead */
158 URI_TYPE(TextRange) query; /**< Query without leading "?" */
159 URI_TYPE(TextRange) fragment; /**< Query without leading "#" */
160 UriBool absolutePath; /**< Absolute path flag, distincting "a" and "/a" */
161 UriBool owner; /**< Memory owner flag */
162
163 void * reserved; /**< Reserved to the parser */
164} URI_TYPE(Uri); /**< @copydoc UriUriStructA */
165
166
167
168/**
169 * Represents a state of the %URI parser.
170 * Missing components can be NULL to reflect
171 * a components absence.
172 *
173 * @see uriFreeUriMembersA
174 * @since 0.3.0
175 */
176typedef struct URI_TYPE(ParserStateStruct) {
177 URI_TYPE(Uri) * uri; /**< Plug in the %URI structure to be filled while parsing here */
178 int errorCode; /**< Code identifying the occured error */
179 const URI_CHAR * errorPos; /**< Pointer to position in case of a syntax error */
180
181 void * reserved; /**< Reserved to the parser */
182} URI_TYPE(ParserState); /**< @copydoc UriParserStateStructA */
183
184
185
186/**
187 * Represents a query element.
188 * More precisely it is a node in a linked
189 * list of query elements.
190 *
191 * @since 0.7.0
192 */
193typedef struct URI_TYPE(QueryListStruct) {
194 const URI_CHAR * key; /**< Key of the query element */
195 const URI_CHAR * value; /**< Value of the query element, can be NULL */
196
197 struct URI_TYPE(QueryListStruct) * next; /**< Pointer to the next key/value pair in the list, can be NULL if last already */
198} URI_TYPE(QueryList); /**< @copydoc UriQueryListStructA */
199
200
201
202/**
203 * Parses a RFC 3986 URI.
204 *
205 * @param state <b>INOUT</b>: Parser state with set output %URI, must not be NULL
206 * @param first <b>IN</b>: Pointer to the first character to parse, must not be NULL
207 * @param afterLast <b>IN</b>: Pointer to the character after the last to parse, must not be NULL
208 * @return 0 on success, error code otherwise
209 *
210 * @see uriParseUriA
211 * @see uriToStringA
212 * @since 0.3.0
213 */
214int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state,
215 const URI_CHAR * first, const URI_CHAR * afterLast);
216
217
218
219/**
220 * Parses a RFC 3986 %URI.
221 *
222 * @param state <b>INOUT</b>: Parser state with set output %URI, must not be NULL
223 * @param text <b>IN</b>: Text to parse, must not be NULL
224 * @return 0 on success, error code otherwise
225 *
226 * @see uriParseUriExA
227 * @see uriToStringA
228 * @since 0.3.0
229 */
230int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state,
231 const URI_CHAR * text);
232
233
234
235/**
236 * Frees all memory associated with the members
237 * of the %URI structure. Note that the structure
238 * itself is not freed, only its members.
239 *
240 * @param uri <b>INOUT</b>: %URI structure whose members should be freed
241 *
242 * @since 0.3.0
243 */
244void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri);
245
246
247
248/**
249 * Percent-encodes all unreserved characters from the input string and
250 * writes the encoded version to the output string.
251 * Be sure to allocate <b>3 times</b> the space of the input buffer for
252 * the output buffer for <c>normalizeBreaks == URI_FALSE</c> and <b>6 times</b>
253 * the space for <c>normalizeBreaks == URI_TRUE</c>
254 * (since e.g. "\x0d" becomes "%0D%0A" in that case)
255 *
256 * @param inFirst <b>IN</b>: Pointer to first character of the input text
257 * @param inAfterLast <b>IN</b>: Pointer after the last character of the input text
258 * @param out <b>OUT</b>: Encoded text destination
259 * @param spaceToPlus <b>IN</b>: Wether to convert ' ' to '+' or not
260 * @param normalizeBreaks <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
261 * @return Position of terminator in output string
262 *
263 * @see uriEscapeA
264 * @see uriUnescapeInPlaceExA
265 * @since 0.5.2
266 */
267URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst,
268 const URI_CHAR * inAfterLast, URI_CHAR * out,
269 UriBool spaceToPlus, UriBool normalizeBreaks);
270
271
272
273/**
274 * Percent-encodes all unreserved characters from the input string and
275 * writes the encoded version to the output string.
276 * Be sure to allocate <b>3 times</b> the space of the input buffer for
277 * the output buffer for <c>normalizeBreaks == URI_FALSE</c> and <b>6 times</b>
278 * the space for <c>normalizeBreaks == URI_TRUE</c>
279 * (since e.g. "\x0d" becomes "%0D%0A" in that case)
280 *
281 * @param in <b>IN</b>: Text source
282 * @param out <b>OUT</b>: Encoded text destination
283 * @param spaceToPlus <b>IN</b>: Wether to convert ' ' to '+' or not
284 * @param normalizeBreaks <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
285 * @return Position of terminator in output string
286 *
287 * @see uriEscapeExA
288 * @see uriUnescapeInPlaceA
289 * @since 0.5.0
290 */
291URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out,
292 UriBool spaceToPlus, UriBool normalizeBreaks);
293
294
295
296/**
297 * Unescapes percent-encoded groups in a given string.
298 * E.g. "%20" will become " ". Unescaping is done in place.
299 * The return value will be point to the new position
300 * of the terminating zero. Use this value to get the new
301 * length of the string. NULL is only returned if <c>inout</c>
302 * is NULL.
303 *
304 * @param inout <b>INOUT</b>: Text to unescape/decode
305 * @param plusToSpace <b>IN</b>: Whether to convert '+' to ' ' or not
306 * @param breakConversion <b>IN</b>: Line break conversion mode
307 * @return Pointer to new position of the terminating zero
308 *
309 * @see uriUnescapeInPlaceA
310 * @see uriEscapeExA
311 * @since 0.5.0
312 */
313const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout,
314 UriBool plusToSpace, UriBreakConversion breakConversion);
315
316
317
318/**
319 * Unescapes percent-encoded groups in a given string.
320 * E.g. "%20" will become " ". Unescaping is done in place.
321 * The return value will be point to the new position
322 * of the terminating zero. Use this value to get the new
323 * length of the string. NULL is only returned if <c>inout</c>
324 * is NULL.
325 *
326 * NOTE: '+' is not decoded to ' ' and line breaks are not converted.
327 * Use the more advanced UnescapeInPlaceEx for that features instead.
328 *
329 * @param inout <b>INOUT</b>: Text to unescape/decode
330 * @return Pointer to new position of the terminating zero
331 *
332 * @see uriUnescapeInPlaceExA
333 * @see uriEscapeA
334 * @since 0.3.0
335 */
336const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout);
337
338
339
340/**
341 * Performs reference resolution as described in
342 * <a href="http://tools.ietf.org/html/rfc3986#section-5.2.2">section 5.2.2 of RFC 3986</a>.
343 * NOTE: On success you have to call uriFreeUriMembersA on \p absoluteDest manually later.
344 *
345 * @param absoluteDest <b>OUT</b>: Result %URI
346 * @param relativeSource <b>IN</b>: Reference to resolve
347 * @param absoluteBase <b>IN</b>: Base %URI to apply
348 * @return Error code or 0 on success
349 *
350 * @see uriRemoveBaseUriA, uriAddBaseUriExA
351 * @since 0.4.0
352 */
353int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absoluteDest,
354 const URI_TYPE(Uri) * relativeSource,
355 const URI_TYPE(Uri) * absoluteBase);
356
357
358
359/**
360 * Performs reference resolution as described in
361 * <a href="http://tools.ietf.org/html/rfc3986#section-5.2.2">section 5.2.2 of RFC 3986</a>.
362 * NOTE: On success you have to call uriFreeUriMembersA on \p absoluteDest manually later.
363 *
364 * @param absoluteDest <b>OUT</b>: Result %URI
365 * @param relativeSource <b>IN</b>: Reference to resolve
366 * @param absoluteBase <b>IN</b>: Base %URI to apply
367 * @param options <b>IN</b>: Configuration to apply
368 * @return Error code or 0 on success
369 *
370 * @see uriRemoveBaseUriA, uriAddBaseUriA
371 * @since 0.8.1
372 */
373int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absoluteDest,
374 const URI_TYPE(Uri) * relativeSource,
375 const URI_TYPE(Uri) * absoluteBase,
376 UriResolutionOptions options);
377
378
379
380/**
381 * Tries to make a relative %URI (a reference) from an
382 * absolute %URI and a given base %URI. This can only work if
383 * the absolute %URI shares scheme and authority with
384 * the base %URI. If it does not the result will still be
385 * an absolute URI (with scheme part if necessary).
386 * NOTE: On success you have to call uriFreeUriMembersA on
387 * \p dest manually later.
388 *
389 * @param dest <b>OUT</b>: Result %URI
390 * @param absoluteSource <b>IN</b>: Absolute %URI to make relative
391 * @param absoluteBase <b>IN</b>: Base %URI
392 * @param domainRootMode <b>IN</b>: Create %URI with path relative to domain root
393 * @return Error code or 0 on success
394 *
395 * @see uriAddBaseUriA, uriAddBaseUriExA
396 * @since 0.5.2
397 */
398int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest,
399 const URI_TYPE(Uri) * absoluteSource,
400 const URI_TYPE(Uri) * absoluteBase,
401 UriBool domainRootMode);
402
403
404
405/**
406 * Checks two URIs for equivalence. Comparison is done
407 * the naive way, without prior normalization.
408 * NOTE: Two <c>NULL</c> URIs are equal as well.
409 *
410 * @param a <b>IN</b>: First %URI
411 * @param b <b>IN</b>: Second %URI
412 * @return <c>URI_TRUE</c> when equal, <c>URI_FAlSE</c> else
413 *
414 * @since 0.4.0
415 */
416UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a, const URI_TYPE(Uri) * b);
417
418
419
420/**
421 * Calculates the number of characters needed to store the
422 * string representation of the given %URI excluding the
423 * terminator.
424 *
425 * @param uri <b>IN</b>: %URI to measure
426 * @param charsRequired <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
427 * @return Error code or 0 on success
428 *
429 * @see uriToStringA
430 * @since 0.5.0
431 */
432int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri,
433 int * charsRequired);
434
435
436
437/**
438 * Converts a %URI structure back to text as described in
439 * <a href="http://tools.ietf.org/html/rfc3986#section-5.3">section 5.3 of RFC 3986</a>.
440 *
441 * @param dest <b>OUT</b>: Output destination
442 * @param uri <b>IN</b>: %URI to convert
443 * @param maxChars <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
444 * @param charsWritten <b>OUT</b>: Number of characters written, can be lower than maxChars even if the %URI is too long!
445 * @return Error code or 0 on success
446 *
447 * @see uriToStringCharsRequiredA
448 * @since 0.4.0
449 */
450int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten);
451
452
453
454/**
455 * Determines the components of a %URI that are not normalized.
456 *
457 * @param uri <b>IN</b>: %URI to check
458 * @return Normalization job mask
459 *
460 * @see uriNormalizeSyntaxA
461 * @since 0.5.0
462 */
463unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri);
464
465
466
467/**
468 * Normalizes a %URI using a normalization mask.
469 * The normalization mask decides what components are normalized.
470 *
471 * NOTE: If necessary the %URI becomes owner of all memory
472 * behind the text pointed to. Text is duplicated in that case.
473 *
474 * @param uri <b>INOUT</b>: %URI to normalize
475 * @param mask <b>IN</b>: Normalization mask
476 * @return Error code or 0 on success
477 *
478 * @see uriNormalizeSyntaxA
479 * @see uriNormalizeSyntaxMaskRequiredA
480 * @since 0.5.0
481 */
482int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask);
483
484
485
486/**
487 * Normalizes all components of a %URI.
488 *
489 * NOTE: If necessary the %URI becomes owner of all memory
490 * behind the text pointed to. Text is duplicated in that case.
491 *
492 * @param uri <b>INOUT</b>: %URI to normalize
493 * @return Error code or 0 on success
494 *
495 * @see uriNormalizeSyntaxExA
496 * @see uriNormalizeSyntaxMaskRequiredA
497 * @since 0.5.0
498 */
499int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri);
500
501
502
503/**
504 * Converts a Unix filename to a %URI string.
505 * The destination buffer must be large enough to hold 7 + 3 * len(filename) + 1
506 * characters in case of an absolute filename or 3 * len(filename) + 1 in case
507 * of a relative filename.
508 *
509 * EXAMPLE
510 * Input: "/bin/bash"
511 * Output: "file:///bin/bash"
512 *
513 * @param filename <b>IN</b>: Unix filename to convert
514 * @param uriString <b>OUT</b>: Destination to write %URI string to
515 * @return Error code or 0 on success
516 *
517 * @see uriUriStringToUnixFilenameA
518 * @see uriWindowsFilenameToUriStringA
519 * @since 0.5.2
520 */
521int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename,
522 URI_CHAR * uriString);
523
524
525
526/**
527 * Converts a Windows filename to a %URI string.
528 * The destination buffer must be large enough to hold 8 + 3 * len(filename) + 1
529 * characters in case of an absolute filename or 3 * len(filename) + 1 in case
530 * of a relative filename.
531 *
532 * EXAMPLE
533 * Input: "E:\\Documents and Settings"
534 * Output: "file:///E:/Documents%20and%20Settings"
535 *
536 * @param filename <b>IN</b>: Windows filename to convert
537 * @param uriString <b>OUT</b>: Destination to write %URI string to
538 * @return Error code or 0 on success
539 *
540 * @see uriUriStringToWindowsFilenameA
541 * @see uriUnixFilenameToUriStringA
542 * @since 0.5.2
543 */
544int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename,
545 URI_CHAR * uriString);
546
547
548
549/**
550 * Extracts a Unix filename from a %URI string.
551 * The destination buffer must be large enough to hold len(uriString) + 1 - 7
552 * characters in case of an absolute %URI or len(uriString) + 1 in case
553 * of a relative %URI.
554 *
555 * @param uriString <b>IN</b>: %URI string to convert
556 * @param filename <b>OUT</b>: Destination to write filename to
557 * @return Error code or 0 on success
558 *
559 * @see uriUnixFilenameToUriStringA
560 * @see uriUriStringToWindowsFilenameA
561 * @since 0.5.2
562 */
563int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString,
564 URI_CHAR * filename);
565
566
567
568/**
569 * Extracts a Windows filename from a %URI string.
570 * The destination buffer must be large enough to hold len(uriString) + 1 - 8
571 * characters in case of an absolute %URI or len(uriString) + 1 in case
572 * of a relative %URI.
573 *
574 * @param uriString <b>IN</b>: %URI string to convert
575 * @param filename <b>OUT</b>: Destination to write filename to
576 * @return Error code or 0 on success
577 *
578 * @see uriWindowsFilenameToUriStringA
579 * @see uriUriStringToUnixFilenameA
580 * @since 0.5.2
581 */
582int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString,
583 URI_CHAR * filename);
584
585
586
587/**
588 * Calculates the number of characters needed to store the
589 * string representation of the given query list excluding the
590 * terminator. It is assumed that line breaks are will be
591 * normalized to "%0D%0A".
592 *
593 * @param queryList <b>IN</b>: Query list to measure
594 * @param charsRequired <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
595 * @return Error code or 0 on success
596 *
597 * @see uriComposeQueryCharsRequiredExA
598 * @see uriComposeQueryA
599 * @since 0.7.0
600 */
601int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList,
602 int * charsRequired);
603
604
605
606/**
607 * Calculates the number of characters needed to store the
608 * string representation of the given query list excluding the
609 * terminator.
610 *
611 * @param queryList <b>IN</b>: Query list to measure
612 * @param charsRequired <b>OUT</b>: Length of the string representation in characters <b>excluding</b> terminator
613 * @param spaceToPlus <b>IN</b>: Wether to convert ' ' to '+' or not
614 * @param normalizeBreaks <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
615 * @return Error code or 0 on success
616 *
617 * @see uriComposeQueryCharsRequiredA
618 * @see uriComposeQueryExA
619 * @since 0.7.0
620 */
621int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList,
622 int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks);
623
624
625
626/**
627 * Converts a query list structure back to a query string.
628 * The composed string does not start with '?',
629 * on the way ' ' is converted to '+' and line breaks are
630 * normalized to "%0D%0A".
631 *
632 * @param dest <b>OUT</b>: Output destination
633 * @param queryList <b>IN</b>: Query list to convert
634 * @param maxChars <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
635 * @param charsWritten <b>OUT</b>: Number of characters written, can be lower than maxChars even if the query list is too long!
636 * @return Error code or 0 on success
637 *
638 * @see uriComposeQueryExA
639 * @see uriComposeQueryMallocA
640 * @see uriComposeQueryCharsRequiredA
641 * @see uriDissectQueryMallocA
642 * @since 0.7.0
643 */
644int URI_FUNC(ComposeQuery)(URI_CHAR * dest,
645 const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten);
646
647
648
649/**
650 * Converts a query list structure back to a query string.
651 * The composed string does not start with '?'.
652 *
653 * @param dest <b>OUT</b>: Output destination
654 * @param queryList <b>IN</b>: Query list to convert
655 * @param maxChars <b>IN</b>: Maximum number of characters to copy <b>including</b> terminator
656 * @param charsWritten <b>OUT</b>: Number of characters written, can be lower than maxChars even if the query list is too long!
657 * @param spaceToPlus <b>IN</b>: Wether to convert ' ' to '+' or not
658 * @param normalizeBreaks <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
659 * @return Error code or 0 on success
660 *
661 * @see uriComposeQueryA
662 * @see uriComposeQueryMallocExA
663 * @see uriComposeQueryCharsRequiredExA
664 * @see uriDissectQueryMallocExA
665 * @since 0.7.0
666 */
667int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest,
668 const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten,
669 UriBool spaceToPlus, UriBool normalizeBreaks);
670
671
672
673/**
674 * Converts a query list structure back to a query string.
675 * Memory for this string is allocated internally.
676 * The composed string does not start with '?',
677 * on the way ' ' is converted to '+' and line breaks are
678 * normalized to "%0D%0A".
679 *
680 * @param dest <b>OUT</b>: Output destination
681 * @param queryList <b>IN</b>: Query list to convert
682 * @return Error code or 0 on success
683 *
684 * @see uriComposeQueryMallocExA
685 * @see uriComposeQueryA
686 * @see uriDissectQueryMallocA
687 * @since 0.7.0
688 */
689int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest,
690 const URI_TYPE(QueryList) * queryList);
691
692
693
694/**
695 * Converts a query list structure back to a query string.
696 * Memory for this string is allocated internally.
697 * The composed string does not start with '?'.
698 *
699 * @param dest <b>OUT</b>: Output destination
700 * @param queryList <b>IN</b>: Query list to convert
701 * @param spaceToPlus <b>IN</b>: Wether to convert ' ' to '+' or not
702 * @param normalizeBreaks <b>IN</b>: Wether to convert CR and LF to CR-LF or not.
703 * @return Error code or 0 on success
704 *
705 * @see uriComposeQueryMallocA
706 * @see uriComposeQueryExA
707 * @see uriDissectQueryMallocExA
708 * @since 0.7.0
709 */
710int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest,
711 const URI_TYPE(QueryList) * queryList,
712 UriBool spaceToPlus, UriBool normalizeBreaks);
713
714
715
716/**
717 * Constructs a query list from the raw query string of a given URI.
718 * On the way '+' is converted back to ' ', line breaks are not modified.
719 *
720 * @param dest <b>OUT</b>: Output destination
721 * @param itemCount <b>OUT</b>: Number of items found, can be NULL
722 * @param first <b>IN</b>: Pointer to first character <b>after</b> '?'
723 * @param afterLast <b>IN</b>: Pointer to character after the last one still in
724 * @return Error code or 0 on success
725 *
726 * @see uriDissectQueryMallocExA
727 * @see uriComposeQueryA
728 * @see uriFreeQueryListA
729 * @since 0.7.0
730 */
731int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount,
732 const URI_CHAR * first, const URI_CHAR * afterLast);
733
734
735
736/**
737 * Constructs a query list from the raw query string of a given URI.
738 *
739 * @param dest <b>OUT</b>: Output destination
740 * @param itemCount <b>OUT</b>: Number of items found, can be NULL
741 * @param first <b>IN</b>: Pointer to first character <b>after</b> '?'
742 * @param afterLast <b>IN</b>: Pointer to character after the last one still in
743 * @param plusToSpace <b>IN</b>: Whether to convert '+' to ' ' or not
744 * @param breakConversion <b>IN</b>: Line break conversion mode
745 * @return Error code or 0 on success
746 *
747 * @see uriDissectQueryMallocA
748 * @see uriComposeQueryExA
749 * @see uriFreeQueryListA
750 * @since 0.7.0
751 */
752int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount,
753 const URI_CHAR * first, const URI_CHAR * afterLast,
754 UriBool plusToSpace, UriBreakConversion breakConversion);
755
756
757
758/**
759 * Frees all memory associated with the given query list.
760 * The structure itself is freed as well.
761 *
762 * @param queryList <b>INOUT</b>: Query list to free
763 *
764 * @since 0.7.0
765 */
766void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList);
767
768
769
770#ifdef __cplusplus
771}
772#endif
773
774
775
776#endif
777#endif
diff --git a/plugins/uriparser/UriBase.h b/plugins/uriparser/UriBase.h
new file mode 100644
index 00000000..bc63b059
--- /dev/null
+++ b/plugins/uriparser/UriBase.h
@@ -0,0 +1,197 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriBase.h
42 * Holds definitions independent of the encoding pass.
43 */
44
45#ifndef URI_BASE_H
46#define URI_BASE_H 1
47
48
49
50/* Version helper macro */
51#define URI_ANSI_TO_UNICODE(x) L##x
52
53
54
55/* Version */
56#define URI_VER_MAJOR 0
57#define URI_VER_MINOR 8
58#define URI_VER_RELEASE 4
59#define URI_VER_SUFFIX_ANSI ""
60#define URI_VER_SUFFIX_UNICODE URI_ANSI_TO_UNICODE(URI_VER_SUFFIX_ANSI)
61
62
63
64/* More version helper macros */
65#define URI_INT_TO_ANSI_HELPER(x) #x
66#define URI_INT_TO_ANSI(x) URI_INT_TO_ANSI_HELPER(x)
67
68#define URI_INT_TO_UNICODE_HELPER(x) URI_ANSI_TO_UNICODE(#x)
69#define URI_INT_TO_UNICODE(x) URI_INT_TO_UNICODE_HELPER(x)
70
71#define URI_VER_ANSI_HELPER(ma, mi, r, s) \
72 URI_INT_TO_ANSI(ma) "." \
73 URI_INT_TO_ANSI(mi) "." \
74 URI_INT_TO_ANSI(r) \
75 s
76
77#define URI_VER_UNICODE_HELPER(ma, mi, r, s) \
78 URI_INT_TO_UNICODE(ma) L"." \
79 URI_INT_TO_UNICODE(mi) L"." \
80 URI_INT_TO_UNICODE(r) \
81 s
82
83
84
85/* Full version strings */
86#define URI_VER_ANSI URI_VER_ANSI_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_ANSI)
87#define URI_VER_UNICODE URI_VER_UNICODE_HELPER(URI_VER_MAJOR, URI_VER_MINOR, URI_VER_RELEASE, URI_VER_SUFFIX_UNICODE)
88
89
90
91/* Unused parameter macro */
92#ifdef __GNUC__
93# define URI_UNUSED(x) unused_##x __attribute__((unused))
94#else
95# define URI_UNUSED(x) x
96#endif
97
98
99
100typedef int UriBool; /**< Boolean type */
101
102#define URI_TRUE 1
103#define URI_FALSE 0
104
105
106
107/* Shared errors */
108#define URI_SUCCESS 0
109#define URI_ERROR_SYNTAX 1 /* Parsed text violates expected format */
110#define URI_ERROR_NULL 2 /* One of the params passed was NULL
111 although it mustn't be */
112#define URI_ERROR_MALLOC 3 /* Requested memory could not be allocated */
113#define URI_ERROR_OUTPUT_TOO_LARGE 4 /* Some output is to large for the receiving buffer */
114#define URI_ERROR_NOT_IMPLEMENTED 8 /* The called function is not implemented yet */
115#define URI_ERROR_RANGE_INVALID 9 /* The parameters passed contained invalid ranges */
116
117
118/* Errors specific to ToString */
119#define URI_ERROR_TOSTRING_TOO_LONG URI_ERROR_OUTPUT_TOO_LARGE /* Deprecated, test for URI_ERROR_OUTPUT_TOO_LARGE instead */
120
121/* Errors specific to AddBaseUri */
122#define URI_ERROR_ADDBASE_REL_BASE 5 /* Given base is not absolute */
123
124/* Errors specific to RemoveBaseUri */
125#define URI_ERROR_REMOVEBASE_REL_BASE 6 /* Given base is not absolute */
126#define URI_ERROR_REMOVEBASE_REL_SOURCE 7 /* Given base is not absolute */
127
128
129
130#ifndef URI_DOXYGEN
131# include <stdio.h> /* For NULL, snprintf */
132# include <ctype.h> /* For wchar_t */
133# include <string.h> /* For strlen, memset, memcpy */
134# include <stdlib.h> /* For malloc */
135#endif /* URI_DOXYGEN */
136
137
138
139/**
140 * Holds an IPv4 address.
141 */
142typedef struct UriIp4Struct {
143 unsigned char data[4]; /**< Each octet in one byte */
144} UriIp4; /**< @copydoc UriIp4Struct */
145
146
147
148/**
149 * Holds an IPv6 address.
150 */
151typedef struct UriIp6Struct {
152 unsigned char data[16]; /**< Each quad in two bytes */
153} UriIp6; /**< @copydoc UriIp6Struct */
154
155
156
157/**
158 * Specifies a line break conversion mode.
159 */
160typedef enum UriBreakConversionEnum {
161 URI_BR_TO_LF, /**< Convert to Unix line breaks ("\\x0a") */
162 URI_BR_TO_CRLF, /**< Convert to Windows line breaks ("\\x0d\\x0a") */
163 URI_BR_TO_CR, /**< Convert to Macintosh line breaks ("\\x0d") */
164 URI_BR_TO_UNIX = URI_BR_TO_LF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_LF */
165 URI_BR_TO_WINDOWS = URI_BR_TO_CRLF, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CRLF */
166 URI_BR_TO_MAC = URI_BR_TO_CR, /**< @copydoc UriBreakConversionEnum::URI_BR_TO_CR */
167 URI_BR_DONT_TOUCH /**< Copy line breaks unmodified */
168} UriBreakConversion; /**< @copydoc UriBreakConversionEnum */
169
170
171
172/**
173 * Specifies which component of a %URI has to be normalized.
174 */
175typedef enum UriNormalizationMaskEnum {
176 URI_NORMALIZED = 0, /**< Do not normalize anything */
177 URI_NORMALIZE_SCHEME = 1 << 0, /**< Normalize scheme (fix uppercase letters) */
178 URI_NORMALIZE_USER_INFO = 1 << 1, /**< Normalize user info (fix uppercase percent-encodings) */
179 URI_NORMALIZE_HOST = 1 << 2, /**< Normalize host (fix uppercase letters) */
180 URI_NORMALIZE_PATH = 1 << 3, /**< Normalize path (fix uppercase percent-encodings and redundant dot segments) */
181 URI_NORMALIZE_QUERY = 1 << 4, /**< Normalize query (fix uppercase percent-encodings) */
182 URI_NORMALIZE_FRAGMENT = 1 << 5 /**< Normalize fragment (fix uppercase percent-encodings) */
183} UriNormalizationMask; /**< @copydoc UriNormalizationMaskEnum */
184
185
186
187/**
188 * Specifies how to resolve %URI references.
189 */
190typedef enum UriResolutionOptionsEnum {
191 URI_RESOLVE_STRICTLY = 0, /**< Full RFC conformance */
192 URI_RESOLVE_IDENTICAL_SCHEME_COMPAT = 1 << 0 /**< Treat %URI to resolve with identical scheme as having no scheme */
193} UriResolutionOptions; /**< @copydoc UriResolutionOptionsEnum */
194
195
196
197#endif /* URI_BASE_H */
diff --git a/plugins/uriparser/UriCommon.c b/plugins/uriparser/UriCommon.c
new file mode 100644
index 00000000..37d6b39d
--- /dev/null
+++ b/plugins/uriparser/UriCommon.c
@@ -0,0 +1,567 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriCommon.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriCommon.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71/*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X");
72/*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT(".");
73/*extern*/ const URI_CHAR * const URI_FUNC(ConstParent) = _UT("..");
74
75
76
77void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri) {
78 memset(uri, 0, sizeof(URI_TYPE(Uri)));
79}
80
81
82
83/* Compares two text ranges for equal text content */
84int URI_FUNC(CompareRange)(
85 const URI_TYPE(TextRange) * a,
86 const URI_TYPE(TextRange) * b) {
87 int diff;
88
89 /* NOTE: Both NULL means equal! */
90 if ((a == NULL) || (b == NULL)) {
91 return ((a == NULL) ? 0 : 1) - ((b == NULL) ? 0 : 1);
92 }
93
94 /* NOTE: Both NULL means equal! */
95 if ((a->first == NULL) || (b->first == NULL)) {
96 return ((a->first == NULL) ? 0 : 1) - ((b->first == NULL) ? 0 : 1);
97 }
98
99 diff = ((int)(a->afterLast - a->first) - (int)(b->afterLast - b->first));
100 if (diff > 0) {
101 return 1;
102 } else if (diff < 0) {
103 return -1;
104 }
105
106 diff = URI_STRNCMP(a->first, b->first, (a->afterLast - a->first));
107
108 if (diff > 0) {
109 return 1;
110 } else if (diff < 0) {
111 return -1;
112 }
113
114 return diff;
115}
116
117
118
119/* Properly removes "." and ".." path segments */
120UriBool URI_FUNC(RemoveDotSegments)(URI_TYPE(Uri) * uri,
121 UriBool relative) {
122 if (uri == NULL) {
123 return URI_TRUE;
124 }
125 return URI_FUNC(RemoveDotSegmentsEx)(uri, relative, uri->owner);
126}
127
128
129
130UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
131 UriBool relative, UriBool pathOwned) {
132 URI_TYPE(PathSegment) * walker;
133 if ((uri == NULL) || (uri->pathHead == NULL)) {
134 return URI_TRUE;
135 }
136
137 walker = uri->pathHead;
138 walker->reserved = NULL; /* Prev pointer */
139 do {
140 UriBool removeSegment = URI_FALSE;
141 int len = (int)(walker->text.afterLast - walker->text.first);
142 switch (len) {
143 case 1:
144 if ((walker->text.first)[0] == _UT('.')) {
145 /* "." segment -> remove if not essential */
146 URI_TYPE(PathSegment) * const prev = walker->reserved;
147 URI_TYPE(PathSegment) * const nextBackup = walker->next;
148
149 /* Is this dot segment essential? */
150 removeSegment = URI_TRUE;
151 if (relative && (walker == uri->pathHead) && (walker->next != NULL)) {
152 const URI_CHAR * ch = walker->next->text.first;
153 for (; ch < walker->next->text.afterLast; ch++) {
154 if (*ch == _UT(':')) {
155 removeSegment = URI_FALSE;
156 break;
157 }
158 }
159 }
160
161 if (removeSegment) {
162 /* Last segment? */
163 if (walker->next != NULL) {
164 /* Not last segment */
165 walker->next->reserved = prev;
166
167 if (prev == NULL) {
168 /* First but not last segment */
169 uri->pathHead = walker->next;
170 } else {
171 /* Middle segment */
172 prev->next = walker->next;
173 }
174
175 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
176 free((URI_CHAR *)walker->text.first);
177 }
178 free(walker);
179 } else {
180 /* Last segment */
181 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
182 free((URI_CHAR *)walker->text.first);
183 }
184
185 if (prev == NULL) {
186 /* Last and first */
187 if (URI_FUNC(IsHostSet)(uri)) {
188 /* Replace "." with empty segment to represent trailing slash */
189 walker->text.first = URI_FUNC(SafeToPointTo);
190 walker->text.afterLast = URI_FUNC(SafeToPointTo);
191 } else {
192 free(walker);
193
194 uri->pathHead = NULL;
195 uri->pathTail = NULL;
196 }
197 } else {
198 /* Last but not first, replace "." with empty segment to represent trailing slash */
199 walker->text.first = URI_FUNC(SafeToPointTo);
200 walker->text.afterLast = URI_FUNC(SafeToPointTo);
201 }
202 }
203
204 walker = nextBackup;
205 }
206 }
207 break;
208
209 case 2:
210 if (((walker->text.first)[0] == _UT('.'))
211 && ((walker->text.first)[1] == _UT('.'))) {
212 /* Path ".." -> remove this and the previous segment */
213 URI_TYPE(PathSegment) * const prev = walker->reserved;
214 URI_TYPE(PathSegment) * prevPrev;
215 URI_TYPE(PathSegment) * const nextBackup = walker->next;
216
217 removeSegment = URI_TRUE;
218 if (relative) {
219 if (prev == NULL) {
220 removeSegment = URI_FALSE;
221 } else if ((prev != NULL)
222 && ((prev->text.afterLast - prev->text.first) == 2)
223 && ((prev->text.first)[0] == _UT('.'))
224 && ((prev->text.first)[1] == _UT('.'))) {
225 removeSegment = URI_FALSE;
226 }
227 }
228
229 if (removeSegment) {
230 if (prev != NULL) {
231 /* Not first segment */
232 prevPrev = prev->reserved;
233 if (prevPrev != NULL) {
234 /* Not even prev is the first one */
235 prevPrev->next = walker->next;
236 if (walker->next != NULL) {
237 walker->next->reserved = prevPrev;
238 } else {
239 /* Last segment -> insert "" segment to represent trailing slash, update tail */
240 URI_TYPE(PathSegment) * const segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
241 if (segment == NULL) {
242 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
243 free((URI_CHAR *)walker->text.first);
244 }
245 free(walker);
246
247 if (pathOwned && (prev->text.first != prev->text.afterLast)) {
248 free((URI_CHAR *)prev->text.first);
249 }
250 free(prev);
251
252 return URI_FALSE; /* Raises malloc error */
253 }
254 memset(segment, 0, sizeof(URI_TYPE(PathSegment)));
255 segment->text.first = URI_FUNC(SafeToPointTo);
256 segment->text.afterLast = URI_FUNC(SafeToPointTo);
257 prevPrev->next = segment;
258 uri->pathTail = segment;
259 }
260
261 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
262 free((URI_CHAR *)walker->text.first);
263 }
264 free(walker);
265
266 if (pathOwned && (prev->text.first != prev->text.afterLast)) {
267 free((URI_CHAR *)prev->text.first);
268 }
269 free(prev);
270
271 walker = nextBackup;
272 } else {
273 /* Prev is the first segment */
274 if (walker->next != NULL) {
275 uri->pathHead = walker->next;
276 walker->next->reserved = NULL;
277
278 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
279 free((URI_CHAR *)walker->text.first);
280 }
281 free(walker);
282 } else {
283 /* Re-use segment for "" path segment to represent trailing slash, update tail */
284 URI_TYPE(PathSegment) * const segment = walker;
285 if (pathOwned && (segment->text.first != segment->text.afterLast)) {
286 free((URI_CHAR *)segment->text.first);
287 }
288 segment->text.first = URI_FUNC(SafeToPointTo);
289 segment->text.afterLast = URI_FUNC(SafeToPointTo);
290 uri->pathHead = segment;
291 uri->pathTail = segment;
292 }
293
294 if (pathOwned && (prev->text.first != prev->text.afterLast)) {
295 free((URI_CHAR *)prev->text.first);
296 }
297 free(prev);
298
299 walker = nextBackup;
300 }
301 } else {
302 URI_TYPE(PathSegment) * const anotherNextBackup = walker->next;
303 /* First segment -> update head pointer */
304 uri->pathHead = walker->next;
305 if (walker->next != NULL) {
306 walker->next->reserved = NULL;
307 } else {
308 /* Last segment -> update tail */
309 uri->pathTail = NULL;
310 }
311
312 if (pathOwned && (walker->text.first != walker->text.afterLast)) {
313 free((URI_CHAR *)walker->text.first);
314 }
315 free(walker);
316
317 walker = anotherNextBackup;
318 }
319 }
320 }
321 break;
322
323 }
324
325 if (!removeSegment) {
326 if (walker->next != NULL) {
327 walker->next->reserved = walker;
328 } else {
329 /* Last segment -> update tail */
330 uri->pathTail = walker;
331 }
332 walker = walker->next;
333 }
334 } while (walker != NULL);
335
336 return URI_TRUE;
337}
338
339
340
341/* Properly removes "." and ".." path segments */
342UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri) {
343 const UriBool ABSOLUTE = URI_FALSE;
344 return URI_FUNC(RemoveDotSegments)(uri, ABSOLUTE);
345}
346
347
348
349unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig) {
350 switch (hexdig) {
351 case _UT('0'):
352 case _UT('1'):
353 case _UT('2'):
354 case _UT('3'):
355 case _UT('4'):
356 case _UT('5'):
357 case _UT('6'):
358 case _UT('7'):
359 case _UT('8'):
360 case _UT('9'):
361 return (unsigned char)(9 + hexdig - _UT('9'));
362
363 case _UT('a'):
364 case _UT('b'):
365 case _UT('c'):
366 case _UT('d'):
367 case _UT('e'):
368 case _UT('f'):
369 return (unsigned char)(15 + hexdig - _UT('f'));
370
371 case _UT('A'):
372 case _UT('B'):
373 case _UT('C'):
374 case _UT('D'):
375 case _UT('E'):
376 case _UT('F'):
377 return (unsigned char)(15 + hexdig - _UT('F'));
378
379 default:
380 return 0;
381 }
382}
383
384
385
386URI_CHAR URI_FUNC(HexToLetter)(unsigned int value) {
387 /* Uppercase recommended in section 2.1. of RFC 3986 *
388 * http://tools.ietf.org/html/rfc3986#section-2.1 */
389 return URI_FUNC(HexToLetterEx)(value, URI_TRUE);
390}
391
392
393
394URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) {
395 switch (value) {
396 case 0: return _UT('0');
397 case 1: return _UT('1');
398 case 2: return _UT('2');
399 case 3: return _UT('3');
400 case 4: return _UT('4');
401 case 5: return _UT('5');
402 case 6: return _UT('6');
403 case 7: return _UT('7');
404 case 8: return _UT('8');
405 case 9: return _UT('9');
406
407 case 10: return (uppercase == URI_TRUE) ? _UT('A') : _UT('a');
408 case 11: return (uppercase == URI_TRUE) ? _UT('B') : _UT('b');
409 case 12: return (uppercase == URI_TRUE) ? _UT('C') : _UT('c');
410 case 13: return (uppercase == URI_TRUE) ? _UT('D') : _UT('d');
411 case 14: return (uppercase == URI_TRUE) ? _UT('E') : _UT('e');
412 default: return (uppercase == URI_TRUE) ? _UT('F') : _UT('f');
413 }
414}
415
416
417
418/* Checks if a URI has the host component set. */
419UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri) {
420 return (uri != NULL)
421 && ((uri->hostText.first != NULL)
422 || (uri->hostData.ip4 != NULL)
423 || (uri->hostData.ip6 != NULL)
424 || (uri->hostData.ipFuture.first != NULL)
425 );
426}
427
428
429
430/* Copies the path segment list from one URI to another. */
431UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest,
432 const URI_TYPE(Uri) * source) {
433 if (source->pathHead == NULL) {
434 /* No path component */
435 dest->pathHead = NULL;
436 dest->pathTail = NULL;
437 } else {
438 /* Copy list but not the text contained */
439 URI_TYPE(PathSegment) * sourceWalker = source->pathHead;
440 URI_TYPE(PathSegment) * destPrev = NULL;
441 do {
442 URI_TYPE(PathSegment) * cur = malloc(sizeof(URI_TYPE(PathSegment)));
443 if (cur == NULL) {
444 /* Fix broken list */
445 if (destPrev != NULL) {
446 destPrev->next = NULL;
447 }
448 return URI_FALSE; /* Raises malloc error */
449 }
450
451 /* From this functions usage we know that *
452 * the dest URI cannot be uri->owner */
453 cur->text = sourceWalker->text;
454 if (destPrev == NULL) {
455 /* First segment ever */
456 dest->pathHead = cur;
457 } else {
458 destPrev->next = cur;
459 }
460 destPrev = cur;
461 sourceWalker = sourceWalker->next;
462 } while (sourceWalker != NULL);
463 dest->pathTail = destPrev;
464 dest->pathTail->next = NULL;
465 }
466
467 dest->absolutePath = source->absolutePath;
468 return URI_TRUE;
469}
470
471
472
473/* Copies the authority part of an URI over to another. */
474UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest,
475 const URI_TYPE(Uri) * source) {
476 /* From this functions usage we know that *
477 * the dest URI cannot be uri->owner */
478
479 /* Copy userInfo */
480 dest->userInfo = source->userInfo;
481
482 /* Copy hostText */
483 dest->hostText = source->hostText;
484
485 /* Copy hostData */
486 if (source->hostData.ip4 != NULL) {
487 dest->hostData.ip4 = malloc(sizeof(UriIp4));
488 if (dest->hostData.ip4 == NULL) {
489 return URI_FALSE; /* Raises malloc error */
490 }
491 *(dest->hostData.ip4) = *(source->hostData.ip4);
492 dest->hostData.ip6 = NULL;
493 dest->hostData.ipFuture.first = NULL;
494 dest->hostData.ipFuture.afterLast = NULL;
495 } else if (source->hostData.ip6 != NULL) {
496 dest->hostData.ip4 = NULL;
497 dest->hostData.ip6 = malloc(sizeof(UriIp6));
498 if (dest->hostData.ip6 == NULL) {
499 return URI_FALSE; /* Raises malloc error */
500 }
501 *(dest->hostData.ip6) = *(source->hostData.ip6);
502 dest->hostData.ipFuture.first = NULL;
503 dest->hostData.ipFuture.afterLast = NULL;
504 } else {
505 dest->hostData.ip4 = NULL;
506 dest->hostData.ip6 = NULL;
507 dest->hostData.ipFuture = source->hostData.ipFuture;
508 }
509
510 /* Copy portText */
511 dest->portText = source->portText;
512
513 return URI_TRUE;
514}
515
516
517
518UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri) {
519 URI_TYPE(PathSegment) * segment;
520
521 if ( /* Case 1: absolute path, empty first segment */
522 (uri->absolutePath
523 && (uri->pathHead != NULL)
524 && (uri->pathHead->text.afterLast == uri->pathHead->text.first))
525
526 /* Case 2: relative path, empty first and second segment */
527 || (!uri->absolutePath
528 && (uri->pathHead != NULL)
529 && (uri->pathHead->next != NULL)
530 && (uri->pathHead->text.afterLast == uri->pathHead->text.first)
531 && (uri->pathHead->next->text.afterLast == uri->pathHead->next->text.first))) {
532 /* NOOP */
533 } else {
534 return URI_TRUE;
535 }
536
537 segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
538 if (segment == NULL) {
539 return URI_FALSE; /* Raises malloc error */
540 }
541
542 /* Insert "." segment in front */
543 segment->next = uri->pathHead;
544 segment->text.first = URI_FUNC(ConstPwd);
545 segment->text.afterLast = URI_FUNC(ConstPwd) + 1;
546 uri->pathHead = segment;
547 return URI_TRUE;
548}
549
550
551
552void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri) {
553 /* Fix path if only one empty segment */
554 if (!uri->absolutePath
555 && !URI_FUNC(IsHostSet)(uri)
556 && (uri->pathHead != NULL)
557 && (uri->pathHead->next == NULL)
558 && (uri->pathHead->text.first == uri->pathHead->text.afterLast)) {
559 free(uri->pathHead);
560 uri->pathHead = NULL;
561 uri->pathTail = NULL;
562 }
563}
564
565
566
567#endif
diff --git a/plugins/uriparser/UriCommon.h b/plugins/uriparser/UriCommon.h
new file mode 100644
index 00000000..f6bc2ccf
--- /dev/null
+++ b/plugins/uriparser/UriCommon.h
@@ -0,0 +1,104 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#if (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI)) \
41 || (defined(URI_PASS_UNICODE) && !defined(URI_COMMON_H_UNICODE)) \
42 || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* What encodings are enabled? */
44#include <uriparser/UriDefsConfig.h>
45#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
46/* Include SELF twice */
47# ifdef URI_ENABLE_ANSI
48# define URI_PASS_ANSI 1
49# include "UriCommon.h"
50# undef URI_PASS_ANSI
51# endif
52# ifdef URI_ENABLE_UNICODE
53# define URI_PASS_UNICODE 1
54# include "UriCommon.h"
55# undef URI_PASS_UNICODE
56# endif
57/* Only one pass for each encoding */
58#elif (defined(URI_PASS_ANSI) && !defined(URI_COMMON_H_ANSI) \
59 && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
60 && !defined(URI_COMMON_H_UNICODE) && defined(URI_ENABLE_UNICODE))
61# ifdef URI_PASS_ANSI
62# define URI_COMMON_H_ANSI 1
63# include <uriparser/UriDefsAnsi.h>
64# else
65# define URI_COMMON_H_UNICODE 1
66# include <uriparser/UriDefsUnicode.h>
67# endif
68
69
70
71/* Used to point to from empty path segments.
72 * X.first and X.afterLast must be the same non-NULL value then. */
73extern const URI_CHAR * const URI_FUNC(SafeToPointTo);
74extern const URI_CHAR * const URI_FUNC(ConstPwd);
75extern const URI_CHAR * const URI_FUNC(ConstParent);
76
77
78
79void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri);
80
81int URI_FUNC(CompareRange)(
82 const URI_TYPE(TextRange) * a,
83 const URI_TYPE(TextRange) * b);
84
85UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri);
86UriBool URI_FUNC(RemoveDotSegments)(URI_TYPE(Uri) * uri, UriBool relative);
87UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri,
88 UriBool relative, UriBool pathOwned);
89
90unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig);
91URI_CHAR URI_FUNC(HexToLetter)(unsigned int value);
92URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase);
93
94UriBool URI_FUNC(IsHostSet)(const URI_TYPE(Uri) * uri);
95
96UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source);
97UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source);
98
99UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri);
100void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri);
101
102
103#endif
104#endif
diff --git a/plugins/uriparser/UriCompare.c b/plugins/uriparser/UriCompare.c
new file mode 100644
index 00000000..6896f64e
--- /dev/null
+++ b/plugins/uriparser/UriCompare.c
@@ -0,0 +1,168 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriCompare.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriCompare.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include <uriparser/UriIp4.h>
67# include "UriCommon.h"
68#endif
69
70
71
72UriBool URI_FUNC(EqualsUri)(const URI_TYPE(Uri) * a,
73 const URI_TYPE(Uri) * b) {
74 /* NOTE: Both NULL means equal! */
75 if ((a == NULL) || (b == NULL)) {
76 return ((a == NULL) && (b == NULL)) ? URI_TRUE : URI_FALSE;
77 }
78
79 /* scheme */
80 if (URI_FUNC(CompareRange)(&(a->scheme), &(b->scheme))) {
81 return URI_FALSE;
82 }
83
84 /* absolutePath */
85 if ((a->scheme.first == NULL)&& (a->absolutePath != b->absolutePath)) {
86 return URI_FALSE;
87 }
88
89 /* userInfo */
90 if (URI_FUNC(CompareRange)(&(a->userInfo), &(b->userInfo))) {
91 return URI_FALSE;
92 }
93
94 /* Host */
95 if (((a->hostData.ip4 == NULL) != (b->hostData.ip4 == NULL))
96 || ((a->hostData.ip6 == NULL) != (b->hostData.ip6 == NULL))
97 || ((a->hostData.ipFuture.first == NULL)
98 != (b->hostData.ipFuture.first == NULL))) {
99 return URI_FALSE;
100 }
101
102 if (a->hostData.ip4 != NULL) {
103 if (memcmp(a->hostData.ip4->data, b->hostData.ip4->data, 4)) {
104 return URI_FALSE;
105 }
106 }
107
108 if (a->hostData.ip6 != NULL) {
109 if (memcmp(a->hostData.ip6->data, b->hostData.ip6->data, 16)) {
110 return URI_FALSE;
111 }
112 }
113
114 if (a->hostData.ipFuture.first != NULL) {
115 if (URI_FUNC(CompareRange)(&(a->hostData.ipFuture), &(b->hostData.ipFuture))) {
116 return URI_FALSE;
117 }
118 }
119
120 if ((a->hostData.ip4 == NULL)
121 && (a->hostData.ip6 == NULL)
122 && (a->hostData.ipFuture.first == NULL)) {
123 if (URI_FUNC(CompareRange)(&(a->hostText), &(b->hostText))) {
124 return URI_FALSE;
125 }
126 }
127
128 /* portText */
129 if (URI_FUNC(CompareRange)(&(a->portText), &(b->portText))) {
130 return URI_FALSE;
131 }
132
133 /* Path */
134 if ((a->pathHead == NULL) != (b->pathHead == NULL)) {
135 return URI_FALSE;
136 }
137
138 if (a->pathHead != NULL) {
139 URI_TYPE(PathSegment) * walkA = a->pathHead;
140 URI_TYPE(PathSegment) * walkB = b->pathHead;
141 do {
142 if (URI_FUNC(CompareRange)(&(walkA->text), &(walkB->text))) {
143 return URI_FALSE;
144 }
145 if ((walkA->next == NULL) != (walkB->next == NULL)) {
146 return URI_FALSE;
147 }
148 walkA = walkA->next;
149 walkB = walkB->next;
150 } while (walkA != NULL);
151 }
152
153 /* query */
154 if (URI_FUNC(CompareRange)(&(a->query), &(b->query))) {
155 return URI_FALSE;
156 }
157
158 /* fragment */
159 if (URI_FUNC(CompareRange)(&(a->fragment), &(b->fragment))) {
160 return URI_FALSE;
161 }
162
163 return URI_TRUE; /* Equal*/
164}
165
166
167
168#endif
diff --git a/plugins/uriparser/UriDefsAnsi.h b/plugins/uriparser/UriDefsAnsi.h
new file mode 100644
index 00000000..deaefaa5
--- /dev/null
+++ b/plugins/uriparser/UriDefsAnsi.h
@@ -0,0 +1,82 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriDefsAnsi.h
42 * Holds definitions for the ANSI pass.
43 * NOTE: This header is included N times, not once.
44 */
45
46/* Allow multi inclusion */
47#include "UriDefsConfig.h"
48
49
50
51#undef URI_CHAR
52#define URI_CHAR char
53
54#undef _UT
55#define _UT(x) x
56
57
58
59#undef URI_FUNC
60#define URI_FUNC(x) uri##x##A
61
62#undef URI_TYPE
63#define URI_TYPE(x) Uri##x##A
64
65
66
67#undef URI_STRLEN
68#define URI_STRLEN strlen
69#undef URI_STRCPY
70#define URI_STRCPY strcpy
71#undef URI_STRCMP
72#define URI_STRCMP strcmp
73#undef URI_STRNCMP
74#define URI_STRNCMP strncmp
75
76/* TODO Remove on next source-compatibility break */
77#undef URI_SNPRINTF
78#if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32))
79# define URI_SNPRINTF _snprintf
80#else
81# define URI_SNPRINTF snprintf
82#endif
diff --git a/plugins/uriparser/UriDefsConfig.h b/plugins/uriparser/UriDefsConfig.h
new file mode 100644
index 00000000..d87ccb63
--- /dev/null
+++ b/plugins/uriparser/UriDefsConfig.h
@@ -0,0 +1,101 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriDefsConfig.h
42 * Adjusts the internal configuration after processing external definitions.
43 */
44
45#ifndef URI_DEFS_CONFIG_H
46#define URI_DEFS_CONFIG_H 1
47
48
49
50/* Deny external overriding */
51#undef URI_ENABLE_ANSI /* Internal for !URI_NO_ANSI */
52#undef URI_ENABLE_UNICODE /* Internal for !URI_NO_UNICODE */
53
54
55
56/* Encoding */
57#ifdef URI_NO_ANSI
58# ifdef URI_NO_UNICODE
59/* No encoding at all */
60# error URI_NO_ANSI and URI_NO_UNICODE cannot go together.
61# else
62/* Unicode only */
63# define URI_ENABLE_UNICODE 1
64# endif
65#else
66# ifdef URI_NO_UNICODE
67/* ANSI only */
68# define URI_ENABLE_ANSI 1
69# else
70/* Both ANSI and Unicode */
71# define URI_ENABLE_ANSI 1
72# define URI_ENABLE_UNICODE 1
73# endif
74#endif
75
76
77
78/* Function inlining, not ANSI/ISO C! */
79#if (defined(URI_DOXYGEN) || defined(URI_SIZEDOWN))
80# define URI_INLINE
81#elif defined(__INTEL_COMPILER)
82/* Intel C/C++ */
83/* http://predef.sourceforge.net/precomp.html#sec20 */
84/* http://www.intel.com/support/performancetools/c/windows/sb/CS-007751.htm#2 */
85# define URI_INLINE __force_inline
86#elif defined(_MSC_VER)
87/* Microsoft Visual C++ */
88/* http://predef.sourceforge.net/precomp.html#sec32 */
89/* http://msdn2.microsoft.com/en-us/library/ms882281.aspx */
90# define URI_INLINE __forceinline
91#elif (__STDC_VERSION__ >= 199901L)
92/* C99, "inline" is a keyword */
93# define URI_INLINE inline
94#else
95/* No inlining */
96# define URI_INLINE
97#endif
98
99
100
101#endif /* URI_DEFS_CONFIG_H */
diff --git a/plugins/uriparser/UriDefsUnicode.h b/plugins/uriparser/UriDefsUnicode.h
new file mode 100644
index 00000000..fa4befcd
--- /dev/null
+++ b/plugins/uriparser/UriDefsUnicode.h
@@ -0,0 +1,82 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriDefsUnicode.h
42 * Holds definitions for the Unicode pass.
43 * NOTE: This header is included N times, not once.
44 */
45
46/* Allow multi inclusion */
47#include "UriDefsConfig.h"
48
49
50
51#undef URI_CHAR
52#define URI_CHAR wchar_t
53
54#undef _UT
55#define _UT(x) L##x
56
57
58
59#undef URI_FUNC
60#define URI_FUNC(x) uri##x##W
61
62#undef URI_TYPE
63#define URI_TYPE(x) Uri##x##W
64
65
66
67#undef URI_STRLEN
68#define URI_STRLEN wcslen
69#undef URI_STRCPY
70#define URI_STRCPY wcscpy
71#undef URI_STRCMP
72#define URI_STRCMP wcscmp
73#undef URI_STRNCMP
74#define URI_STRNCMP wcsncmp
75
76/* TODO Remove on next source-compatibility break */
77#undef URI_SNPRINTF
78#if (defined(__WIN32__) || defined(_WIN32) || defined(WIN32))
79# define URI_SNPRINTF _snwprintf
80#else
81# define URI_SNPRINTF swprintf
82#endif
diff --git a/plugins/uriparser/UriEscape.c b/plugins/uriparser/UriEscape.c
new file mode 100644
index 00000000..79ee3a68
--- /dev/null
+++ b/plugins/uriparser/UriEscape.c
@@ -0,0 +1,453 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriEscape.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriEscape.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71URI_CHAR * URI_FUNC(Escape)(const URI_CHAR * in, URI_CHAR * out,
72 UriBool spaceToPlus, UriBool normalizeBreaks) {
73 return URI_FUNC(EscapeEx)(in, NULL, out, spaceToPlus, normalizeBreaks);
74}
75
76
77
78URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst,
79 const URI_CHAR * inAfterLast, URI_CHAR * out,
80 UriBool spaceToPlus, UriBool normalizeBreaks) {
81 const URI_CHAR * read = inFirst;
82 URI_CHAR * write = out;
83 UriBool prevWasCr = URI_FALSE;
84 if ((out == NULL) || (inFirst == out)) {
85 return NULL;
86 } else if (inFirst == NULL) {
87 if (out != NULL) {
88 out[0] = _UT('\0');
89 }
90 return out;
91 }
92
93 for (;;) {
94 if ((inAfterLast != NULL) && (read >= inAfterLast)) {
95 write[0] = _UT('\0');
96 return write;
97 }
98
99 switch (read[0]) {
100 case _UT('\0'):
101 write[0] = _UT('\0');
102 return write;
103
104 case _UT(' '):
105 if (spaceToPlus) {
106 write[0] = _UT('+');
107 write++;
108 } else {
109 write[0] = _UT('%');
110 write[1] = _UT('2');
111 write[2] = _UT('0');
112 write += 3;
113 }
114 prevWasCr = URI_FALSE;
115 break;
116
117 case _UT('a'): /* ALPHA */
118 case _UT('A'):
119 case _UT('b'):
120 case _UT('B'):
121 case _UT('c'):
122 case _UT('C'):
123 case _UT('d'):
124 case _UT('D'):
125 case _UT('e'):
126 case _UT('E'):
127 case _UT('f'):
128 case _UT('F'):
129 case _UT('g'):
130 case _UT('G'):
131 case _UT('h'):
132 case _UT('H'):
133 case _UT('i'):
134 case _UT('I'):
135 case _UT('j'):
136 case _UT('J'):
137 case _UT('k'):
138 case _UT('K'):
139 case _UT('l'):
140 case _UT('L'):
141 case _UT('m'):
142 case _UT('M'):
143 case _UT('n'):
144 case _UT('N'):
145 case _UT('o'):
146 case _UT('O'):
147 case _UT('p'):
148 case _UT('P'):
149 case _UT('q'):
150 case _UT('Q'):
151 case _UT('r'):
152 case _UT('R'):
153 case _UT('s'):
154 case _UT('S'):
155 case _UT('t'):
156 case _UT('T'):
157 case _UT('u'):
158 case _UT('U'):
159 case _UT('v'):
160 case _UT('V'):
161 case _UT('w'):
162 case _UT('W'):
163 case _UT('x'):
164 case _UT('X'):
165 case _UT('y'):
166 case _UT('Y'):
167 case _UT('z'):
168 case _UT('Z'):
169 case _UT('0'): /* DIGIT */
170 case _UT('1'):
171 case _UT('2'):
172 case _UT('3'):
173 case _UT('4'):
174 case _UT('5'):
175 case _UT('6'):
176 case _UT('7'):
177 case _UT('8'):
178 case _UT('9'):
179 case _UT('-'): /* "-" / "." / "_" / "~" */
180 case _UT('.'):
181 case _UT('_'):
182 case _UT('~'):
183 /* Copy unmodified */
184 write[0] = read[0];
185 write++;
186
187 prevWasCr = URI_FALSE;
188 break;
189
190 case _UT('\x0a'):
191 if (normalizeBreaks) {
192 if (!prevWasCr) {
193 write[0] = _UT('%');
194 write[1] = _UT('0');
195 write[2] = _UT('D');
196 write[3] = _UT('%');
197 write[4] = _UT('0');
198 write[5] = _UT('A');
199 write += 6;
200 }
201 } else {
202 write[0] = _UT('%');
203 write[1] = _UT('0');
204 write[2] = _UT('A');
205 write += 3;
206 }
207 prevWasCr = URI_FALSE;
208 break;
209
210 case _UT('\x0d'):
211 if (normalizeBreaks) {
212 write[0] = _UT('%');
213 write[1] = _UT('0');
214 write[2] = _UT('D');
215 write[3] = _UT('%');
216 write[4] = _UT('0');
217 write[5] = _UT('A');
218 write += 6;
219 } else {
220 write[0] = _UT('%');
221 write[1] = _UT('0');
222 write[2] = _UT('D');
223 write += 3;
224 }
225 prevWasCr = URI_TRUE;
226 break;
227
228 default:
229 /* Percent encode */
230 {
231 const unsigned char code = (unsigned char)read[0];
232 write[0] = _UT('%');
233 write[1] = URI_FUNC(HexToLetter)(code >> 4);
234 write[2] = URI_FUNC(HexToLetter)(code & 0x0f);
235 write += 3;
236 }
237 prevWasCr = URI_FALSE;
238 break;
239 }
240
241 read++;
242 }
243}
244
245
246
247const URI_CHAR * URI_FUNC(UnescapeInPlace)(URI_CHAR * inout) {
248 return URI_FUNC(UnescapeInPlaceEx)(inout, URI_FALSE, URI_BR_DONT_TOUCH);
249}
250
251
252
253const URI_CHAR * URI_FUNC(UnescapeInPlaceEx)(URI_CHAR * inout,
254 UriBool plusToSpace, UriBreakConversion breakConversion) {
255 URI_CHAR * read = inout;
256 URI_CHAR * write = inout;
257 UriBool prevWasCr = URI_FALSE;
258
259 if (inout == NULL) {
260 return NULL;
261 }
262
263 for (;;) {
264 switch (read[0]) {
265 case _UT('\0'):
266 if (read > write) {
267 write[0] = _UT('\0');
268 }
269 return write;
270
271 case _UT('%'):
272 switch (read[1]) {
273 case _UT('0'):
274 case _UT('1'):
275 case _UT('2'):
276 case _UT('3'):
277 case _UT('4'):
278 case _UT('5'):
279 case _UT('6'):
280 case _UT('7'):
281 case _UT('8'):
282 case _UT('9'):
283 case _UT('a'):
284 case _UT('b'):
285 case _UT('c'):
286 case _UT('d'):
287 case _UT('e'):
288 case _UT('f'):
289 case _UT('A'):
290 case _UT('B'):
291 case _UT('C'):
292 case _UT('D'):
293 case _UT('E'):
294 case _UT('F'):
295 switch (read[2]) {
296 case _UT('0'):
297 case _UT('1'):
298 case _UT('2'):
299 case _UT('3'):
300 case _UT('4'):
301 case _UT('5'):
302 case _UT('6'):
303 case _UT('7'):
304 case _UT('8'):
305 case _UT('9'):
306 case _UT('a'):
307 case _UT('b'):
308 case _UT('c'):
309 case _UT('d'):
310 case _UT('e'):
311 case _UT('f'):
312 case _UT('A'):
313 case _UT('B'):
314 case _UT('C'):
315 case _UT('D'):
316 case _UT('E'):
317 case _UT('F'):
318 {
319 /* Percent group found */
320 const unsigned char left = URI_FUNC(HexdigToInt)(read[1]);
321 const unsigned char right = URI_FUNC(HexdigToInt)(read[2]);
322 const int code = 16 * left + right;
323 switch (code) {
324 case 10:
325 switch (breakConversion) {
326 case URI_BR_TO_LF:
327 if (!prevWasCr) {
328 write[0] = (URI_CHAR)10;
329 write++;
330 }
331 break;
332
333 case URI_BR_TO_CRLF:
334 if (!prevWasCr) {
335 write[0] = (URI_CHAR)13;
336 write[1] = (URI_CHAR)10;
337 write += 2;
338 }
339 break;
340
341 case URI_BR_TO_CR:
342 if (!prevWasCr) {
343 write[0] = (URI_CHAR)13;
344 write++;
345 }
346 break;
347
348 case URI_BR_DONT_TOUCH:
349 default:
350 write[0] = (URI_CHAR)10;
351 write++;
352
353 }
354 prevWasCr = URI_FALSE;
355 break;
356
357 case 13:
358 switch (breakConversion) {
359 case URI_BR_TO_LF:
360 write[0] = (URI_CHAR)10;
361 write++;
362 break;
363
364 case URI_BR_TO_CRLF:
365 write[0] = (URI_CHAR)13;
366 write[1] = (URI_CHAR)10;
367 write += 2;
368 break;
369
370 case URI_BR_TO_CR:
371 write[0] = (URI_CHAR)13;
372 write++;
373 break;
374
375 case URI_BR_DONT_TOUCH:
376 default:
377 write[0] = (URI_CHAR)13;
378 write++;
379
380 }
381 prevWasCr = URI_TRUE;
382 break;
383
384 default:
385 write[0] = (URI_CHAR)(code);
386 write++;
387
388 prevWasCr = URI_FALSE;
389
390 }
391 read += 3;
392 }
393 break;
394
395 default:
396 /* Copy two chars unmodified and */
397 /* look at this char again */
398 if (read > write) {
399 write[0] = read[0];
400 write[1] = read[1];
401 }
402 read += 2;
403 write += 2;
404
405 prevWasCr = URI_FALSE;
406 }
407 break;
408
409 default:
410 /* Copy one char unmodified and */
411 /* look at this char again */
412 if (read > write) {
413 write[0] = read[0];
414 }
415 read++;
416 write++;
417
418 prevWasCr = URI_FALSE;
419 }
420 break;
421
422 case _UT('+'):
423 if (plusToSpace) {
424 /* Convert '+' to ' ' */
425 write[0] = _UT(' ');
426 } else {
427 /* Copy one char unmodified */
428 if (read > write) {
429 write[0] = read[0];
430 }
431 }
432 read++;
433 write++;
434
435 prevWasCr = URI_FALSE;
436 break;
437
438 default:
439 /* Copy one char unmodified */
440 if (read > write) {
441 write[0] = read[0];
442 }
443 read++;
444 write++;
445
446 prevWasCr = URI_FALSE;
447 }
448 }
449}
450
451
452
453#endif
diff --git a/plugins/uriparser/UriFile.c b/plugins/uriparser/UriFile.c
new file mode 100644
index 00000000..9cf788fc
--- /dev/null
+++ b/plugins/uriparser/UriFile.c
@@ -0,0 +1,226 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriFile.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriFile.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66#endif
67
68
69
70static URI_INLINE int URI_FUNC(FilenameToUriString)(const URI_CHAR * filename,
71 URI_CHAR * uriString, UriBool fromUnix) {
72 const URI_CHAR * input = filename;
73 const URI_CHAR * lastSep = input - 1;
74 UriBool firstSegment = URI_TRUE;
75 URI_CHAR * output = uriString;
76 UriBool absolute;
77 UriBool is_windows_network;
78
79 if ((filename == NULL) || (uriString == NULL)) {
80 return URI_ERROR_NULL;
81 }
82
83 is_windows_network = (filename[0] == _UT('\\')) && (filename[1] == _UT('\\'));
84 absolute = fromUnix
85 ? (filename[0] == _UT('/'))
86 : ((filename[0] != _UT('\0')) && (filename[1] == _UT(':'))
87 || is_windows_network);
88
89 if (absolute) {
90 const URI_CHAR * const prefix = fromUnix
91 ? _UT("file://")
92 : is_windows_network
93 ? _UT("file:")
94 : _UT("file:///");
95 const int prefixLen = URI_STRLEN(prefix);
96
97 /* Copy prefix */
98 memcpy(uriString, prefix, prefixLen * sizeof(URI_CHAR));
99 output += prefixLen;
100 }
101
102 /* Copy and escape on the fly */
103 for (;;) {
104 if ((input[0] == _UT('\0'))
105 || (fromUnix && input[0] == _UT('/'))
106 || (!fromUnix && input[0] == _UT('\\'))) {
107 /* Copy text after last seperator */
108 if (lastSep + 1 < input) {
109 if (!fromUnix && absolute && (firstSegment == URI_TRUE)) {
110 /* Quick hack to not convert "C:" to "C%3A" */
111 const int charsToCopy = (int)(input - (lastSep + 1));
112 memcpy(output, lastSep + 1, charsToCopy * sizeof(URI_CHAR));
113 output += charsToCopy;
114 } else {
115 output = URI_FUNC(EscapeEx)(lastSep + 1, input, output,
116 URI_FALSE, URI_FALSE);
117 }
118 }
119 firstSegment = URI_FALSE;
120 }
121
122 if (input[0] == _UT('\0')) {
123 output[0] = _UT('\0');
124 break;
125 } else if (fromUnix && (input[0] == _UT('/'))) {
126 /* Copy separators unmodified */
127 output[0] = _UT('/');
128 output++;
129 lastSep = input;
130 } else if (!fromUnix && (input[0] == _UT('\\'))) {
131 /* Convert backslashes to forward slashes */
132 output[0] = _UT('/');
133 output++;
134 lastSep = input;
135 }
136 input++;
137 }
138
139 return URI_SUCCESS;
140}
141
142
143
144static URI_INLINE int URI_FUNC(UriStringToFilename)(const URI_CHAR * uriString,
145 URI_CHAR * filename, UriBool toUnix) {
146 if ((uriString == NULL) || (filename == NULL)) {
147 return URI_ERROR_NULL;
148 }
149
150 {
151 const UriBool file_two_slashes =
152 URI_STRNCMP(uriString, _UT("file://"), URI_STRLEN(_UT("file://"))) == 0;
153 const UriBool file_three_slashes = file_two_slashes
154 && (URI_STRNCMP(uriString, _UT("file:///"), URI_STRLEN(_UT("file:///"))) == 0);
155
156 const size_t charsToSkip = file_two_slashes
157 ? file_three_slashes
158 ? toUnix
159 /* file:///bin/bash */
160 ? URI_STRLEN(_UT("file://"))
161 /* file:///E:/Documents%20and%20Settings */
162 : URI_STRLEN(_UT("file:///"))
163 /* file://Server01/Letter.txt */
164 : URI_STRLEN(_UT("file://"))
165 : 0;
166 const size_t charsToCopy = URI_STRLEN(uriString + charsToSkip) + 1;
167
168 const UriBool is_windows_network_with_authority =
169 (toUnix == URI_FALSE)
170 && file_two_slashes
171 && ! file_three_slashes;
172
173 URI_CHAR * const unescape_target = is_windows_network_with_authority
174 ? (filename + 2)
175 : filename;
176
177 if (is_windows_network_with_authority) {
178 filename[0] = '\\';
179 filename[1] = '\\';
180 }
181
182 memcpy(unescape_target, uriString + charsToSkip, charsToCopy * sizeof(URI_CHAR));
183 URI_FUNC(UnescapeInPlaceEx)(filename, URI_FALSE, URI_BR_DONT_TOUCH);
184 }
185
186 /* Convert forward slashes to backslashes */
187 if (!toUnix) {
188 URI_CHAR * walker = filename;
189 while (walker[0] != _UT('\0')) {
190 if (walker[0] == _UT('/')) {
191 walker[0] = _UT('\\');
192 }
193 walker++;
194 }
195 }
196
197 return URI_SUCCESS;
198}
199
200
201
202int URI_FUNC(UnixFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) {
203 return URI_FUNC(FilenameToUriString)(filename, uriString, URI_TRUE);
204}
205
206
207
208int URI_FUNC(WindowsFilenameToUriString)(const URI_CHAR * filename, URI_CHAR * uriString) {
209 return URI_FUNC(FilenameToUriString)(filename, uriString, URI_FALSE);
210}
211
212
213
214int URI_FUNC(UriStringToUnixFilename)(const URI_CHAR * uriString, URI_CHAR * filename) {
215 return URI_FUNC(UriStringToFilename)(uriString, filename, URI_TRUE);
216}
217
218
219
220int URI_FUNC(UriStringToWindowsFilename)(const URI_CHAR * uriString, URI_CHAR * filename) {
221 return URI_FUNC(UriStringToFilename)(uriString, filename, URI_FALSE);
222}
223
224
225
226#endif
diff --git a/plugins/uriparser/UriIp4.c b/plugins/uriparser/UriIp4.c
new file mode 100644
index 00000000..c00910bb
--- /dev/null
+++ b/plugins/uriparser/UriIp4.c
@@ -0,0 +1,329 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriIp4.c
42 * Holds the IPv4 parser implementation.
43 * NOTE: This source file includes itself twice.
44 */
45
46/* What encodings are enabled? */
47#include <uriparser/UriDefsConfig.h>
48#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
49/* Include SELF twice */
50# ifdef URI_ENABLE_ANSI
51# define URI_PASS_ANSI 1
52# include "UriIp4.c"
53# undef URI_PASS_ANSI
54# endif
55# ifdef URI_ENABLE_UNICODE
56# define URI_PASS_UNICODE 1
57# include "UriIp4.c"
58# undef URI_PASS_UNICODE
59# endif
60#else
61# ifdef URI_PASS_ANSI
62# include <uriparser/UriDefsAnsi.h>
63# else
64# include <uriparser/UriDefsUnicode.h>
65# endif
66
67
68
69#ifndef URI_DOXYGEN
70# include <uriparser/UriIp4.h>
71# include "UriIp4Base.h"
72# include <uriparser/UriBase.h>
73#endif
74
75
76
77/* Prototypes */
78static const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser,
79 const URI_CHAR * first, const URI_CHAR * afterLast);
80static const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser,
81 const URI_CHAR * first, const URI_CHAR * afterLast);
82static const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser,
83 const URI_CHAR * first, const URI_CHAR * afterLast);
84static const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser,
85 const URI_CHAR * first, const URI_CHAR * afterLast);
86static const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser,
87 const URI_CHAR * first, const URI_CHAR * afterLast);
88
89
90
91/*
92 * [ipFourAddress]->[decOctet]<.>[decOctet]<.>[decOctet]<.>[decOctet]
93 */
94int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput,
95 const URI_CHAR * first, const URI_CHAR * afterLast) {
96 const URI_CHAR * after;
97 UriIp4Parser parser;
98
99 /* Essential checks */
100 if ((octetOutput == NULL) || (first == NULL)
101 || (afterLast <= first)) {
102 return URI_ERROR_SYNTAX;
103 }
104
105 /* Reset parser */
106 parser.stackCount = 0;
107
108 /* Octet #1 */
109 after = URI_FUNC(ParseDecOctet)(&parser, first, afterLast);
110 if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
111 return URI_ERROR_SYNTAX;
112 }
113 uriStackToOctet(&parser, octetOutput);
114
115 /* Octet #2 */
116 after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
117 if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
118 return URI_ERROR_SYNTAX;
119 }
120 uriStackToOctet(&parser, octetOutput + 1);
121
122 /* Octet #3 */
123 after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
124 if ((after == NULL) || (after >= afterLast) || (*after != _UT('.'))) {
125 return URI_ERROR_SYNTAX;
126 }
127 uriStackToOctet(&parser, octetOutput + 2);
128
129 /* Octet #4 */
130 after = URI_FUNC(ParseDecOctet)(&parser, after + 1, afterLast);
131 if (after != afterLast) {
132 return URI_ERROR_SYNTAX;
133 }
134 uriStackToOctet(&parser, octetOutput + 3);
135
136 return URI_SUCCESS;
137}
138
139
140
141/*
142 * [decOctet]-><0>
143 * [decOctet]-><1>[decOctetOne]
144 * [decOctet]-><2>[decOctetTwo]
145 * [decOctet]-><3>[decOctetThree]
146 * [decOctet]-><4>[decOctetThree]
147 * [decOctet]-><5>[decOctetThree]
148 * [decOctet]-><6>[decOctetThree]
149 * [decOctet]-><7>[decOctetThree]
150 * [decOctet]-><8>[decOctetThree]
151 * [decOctet]-><9>[decOctetThree]
152 */
153static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctet)(UriIp4Parser * parser,
154 const URI_CHAR * first, const URI_CHAR * afterLast) {
155 if (first >= afterLast) {
156 return NULL;
157 }
158
159 switch (*first) {
160 case _UT('0'):
161 uriPushToStack(parser, 0);
162 return first + 1;
163
164 case _UT('1'):
165 uriPushToStack(parser, 1);
166 return (const URI_CHAR *)URI_FUNC(ParseDecOctetOne)(parser, first + 1, afterLast);
167
168 case _UT('2'):
169 uriPushToStack(parser, 2);
170 return (const URI_CHAR *)URI_FUNC(ParseDecOctetTwo)(parser, first + 1, afterLast);
171
172 case _UT('3'):
173 case _UT('4'):
174 case _UT('5'):
175 case _UT('6'):
176 case _UT('7'):
177 case _UT('8'):
178 case _UT('9'):
179 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
180 return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
181
182 default:
183 return NULL;
184 }
185}
186
187
188
189/*
190 * [decOctetOne]-><NULL>
191 * [decOctetOne]->[DIGIT][decOctetThree]
192 */
193static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetOne)(UriIp4Parser * parser,
194 const URI_CHAR * first, const URI_CHAR * afterLast) {
195 if (first >= afterLast) {
196 return afterLast;
197 }
198
199 switch (*first) {
200 case _UT('0'):
201 case _UT('1'):
202 case _UT('2'):
203 case _UT('3'):
204 case _UT('4'):
205 case _UT('5'):
206 case _UT('6'):
207 case _UT('7'):
208 case _UT('8'):
209 case _UT('9'):
210 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
211 return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
212
213 default:
214 return first;
215 }
216}
217
218
219
220/*
221 * [decOctetTwo]-><NULL>
222 * [decOctetTwo]-><0>[decOctetThree]
223 * [decOctetTwo]-><1>[decOctetThree]
224 * [decOctetTwo]-><2>[decOctetThree]
225 * [decOctetTwo]-><3>[decOctetThree]
226 * [decOctetTwo]-><4>[decOctetThree]
227 * [decOctetTwo]-><5>[decOctetFour]
228 * [decOctetTwo]-><6>
229 * [decOctetTwo]-><7>
230 * [decOctetTwo]-><8>
231 * [decOctetTwo]-><9>
232*/
233static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetTwo)(UriIp4Parser * parser,
234 const URI_CHAR * first, const URI_CHAR * afterLast) {
235 if (first >= afterLast) {
236 return afterLast;
237 }
238
239 switch (*first) {
240 case _UT('0'):
241 case _UT('1'):
242 case _UT('2'):
243 case _UT('3'):
244 case _UT('4'):
245 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
246 return (const URI_CHAR *)URI_FUNC(ParseDecOctetThree)(parser, first + 1, afterLast);
247
248 case _UT('5'):
249 uriPushToStack(parser, 5);
250 return (const URI_CHAR *)URI_FUNC(ParseDecOctetFour)(parser, first + 1, afterLast);
251
252 case _UT('6'):
253 case _UT('7'):
254 case _UT('8'):
255 case _UT('9'):
256 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
257 return first + 1;
258
259 default:
260 return first;
261 }
262}
263
264
265
266/*
267 * [decOctetThree]-><NULL>
268 * [decOctetThree]->[DIGIT]
269 */
270static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetThree)(UriIp4Parser * parser,
271 const URI_CHAR * first, const URI_CHAR * afterLast) {
272 if (first >= afterLast) {
273 return afterLast;
274 }
275
276 switch (*first) {
277 case _UT('0'):
278 case _UT('1'):
279 case _UT('2'):
280 case _UT('3'):
281 case _UT('4'):
282 case _UT('5'):
283 case _UT('6'):
284 case _UT('7'):
285 case _UT('8'):
286 case _UT('9'):
287 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
288 return first + 1;
289
290 default:
291 return first;
292 }
293}
294
295
296
297/*
298 * [decOctetFour]-><NULL>
299 * [decOctetFour]-><0>
300 * [decOctetFour]-><1>
301 * [decOctetFour]-><2>
302 * [decOctetFour]-><3>
303 * [decOctetFour]-><4>
304 * [decOctetFour]-><5>
305 */
306static URI_INLINE const URI_CHAR * URI_FUNC(ParseDecOctetFour)(UriIp4Parser * parser,
307 const URI_CHAR * first, const URI_CHAR * afterLast) {
308 if (first >= afterLast) {
309 return afterLast;
310 }
311
312 switch (*first) {
313 case _UT('0'):
314 case _UT('1'):
315 case _UT('2'):
316 case _UT('3'):
317 case _UT('4'):
318 case _UT('5'):
319 uriPushToStack(parser, (unsigned char)(9 + *first - _UT('9')));
320 return first + 1;
321
322 default:
323 return first;
324 }
325}
326
327
328
329#endif
diff --git a/plugins/uriparser/UriIp4.h b/plugins/uriparser/UriIp4.h
new file mode 100644
index 00000000..885a35ca
--- /dev/null
+++ b/plugins/uriparser/UriIp4.h
@@ -0,0 +1,92 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriIp4.h
42 * Holds the IPv4 parser interface.
43 * NOTE: This header includes itself twice.
44 */
45
46#if (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI)) \
47 || (defined(URI_PASS_UNICODE) && !defined(URI_IP4_TWICE_H_UNICODE)) \
48 || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
49/* What encodings are enabled? */
50#include "UriDefsConfig.h"
51#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
52/* Include SELF twice */
53# ifdef URI_ENABLE_ANSI
54# define URI_PASS_ANSI 1
55# include "UriIp4.h"
56# undef URI_PASS_ANSI
57# endif
58# ifdef URI_ENABLE_UNICODE
59# define URI_PASS_UNICODE 1
60# include "UriIp4.h"
61# undef URI_PASS_UNICODE
62# endif
63/* Only one pass for each encoding */
64#elif (defined(URI_PASS_ANSI) && !defined(URI_IP4_TWICE_H_ANSI) \
65 && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \
66 && !defined(URI_IP4_TWICE_H_UNICODE) && defined(URI_ENABLE_UNICODE))
67# ifdef URI_PASS_ANSI
68# define URI_IP4_TWICE_H_ANSI 1
69# include "UriDefsAnsi.h"
70# else
71# define URI_IP4_TWICE_H_UNICODE 1
72# include "UriDefsUnicode.h"
73# include <wchar.h>
74# endif
75
76
77
78/**
79 * Converts a IPv4 text representation into four bytes.
80 *
81 * @param octetOutput Output destination
82 * @param first First character of IPv4 text to parse
83 * @param afterLast Position to stop parsing at
84 * @return Error code or 0 on success
85 */
86int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput,
87 const URI_CHAR * first, const URI_CHAR * afterLast);
88
89
90
91#endif
92#endif
diff --git a/plugins/uriparser/UriIp4Base.c b/plugins/uriparser/UriIp4Base.c
new file mode 100644
index 00000000..5cd298fa
--- /dev/null
+++ b/plugins/uriparser/UriIp4Base.c
@@ -0,0 +1,96 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriIp4Base.c
42 * Holds code independent of the encoding pass.
43 */
44
45#ifndef URI_DOXYGEN
46# include "UriIp4Base.h"
47#endif
48
49
50
51void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet) {
52 switch (parser->stackCount) {
53 case 1:
54 *octet = parser->stackOne;
55 break;
56
57 case 2:
58 *octet = parser->stackOne * 10
59 + parser->stackTwo;
60 break;
61
62 case 3:
63 *octet = parser->stackOne * 100
64 + parser->stackTwo * 10
65 + parser->stackThree;
66 break;
67
68 default:
69 ;
70 }
71 parser->stackCount = 0;
72}
73
74
75
76void uriPushToStack(UriIp4Parser * parser, unsigned char digit) {
77 switch (parser->stackCount) {
78 case 0:
79 parser->stackOne = digit;
80 parser->stackCount = 1;
81 break;
82
83 case 1:
84 parser->stackTwo = digit;
85 parser->stackCount = 2;
86 break;
87
88 case 2:
89 parser->stackThree = digit;
90 parser->stackCount = 3;
91 break;
92
93 default:
94 ;
95 }
96}
diff --git a/plugins/uriparser/UriIp4Base.h b/plugins/uriparser/UriIp4Base.h
new file mode 100644
index 00000000..23b838e2
--- /dev/null
+++ b/plugins/uriparser/UriIp4Base.h
@@ -0,0 +1,59 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef URI_IP4_BASE_H
41#define URI_IP4_BASE_H 1
42
43
44
45typedef struct UriIp4ParserStruct {
46 unsigned char stackCount;
47 unsigned char stackOne;
48 unsigned char stackTwo;
49 unsigned char stackThree;
50} UriIp4Parser;
51
52
53
54void uriPushToStack(UriIp4Parser * parser, unsigned char digit);
55void uriStackToOctet(UriIp4Parser * parser, unsigned char * octet);
56
57
58
59#endif /* URI_IP4_BASE_H */
diff --git a/plugins/uriparser/UriNormalize.c b/plugins/uriparser/UriNormalize.c
new file mode 100644
index 00000000..49db9fff
--- /dev/null
+++ b/plugins/uriparser/UriNormalize.c
@@ -0,0 +1,728 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriNormalize.c
42 * Holds the RFC 3986 %URI normalization implementation.
43 * NOTE: This source file includes itself twice.
44 */
45
46/* What encodings are enabled? */
47#include <uriparser/UriDefsConfig.h>
48#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
49/* Include SELF twice */
50# ifdef URI_ENABLE_ANSI
51# define URI_PASS_ANSI 1
52# include "UriNormalize.c"
53# undef URI_PASS_ANSI
54# endif
55# ifdef URI_ENABLE_UNICODE
56# define URI_PASS_UNICODE 1
57# include "UriNormalize.c"
58# undef URI_PASS_UNICODE
59# endif
60#else
61# ifdef URI_PASS_ANSI
62# include <uriparser/UriDefsAnsi.h>
63# else
64# include <uriparser/UriDefsUnicode.h>
65# include <wchar.h>
66# endif
67
68
69
70#ifndef URI_DOXYGEN
71# include <uriparser/Uri.h>
72# include "UriNormalizeBase.h"
73# include "UriCommon.h"
74#endif
75
76
77
78static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask,
79 unsigned int * outMask);
80
81static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask,
82 unsigned int maskTest, URI_TYPE(TextRange) * range);
83static UriBool URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri,
84 unsigned int * doneMask);
85
86static void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
87 const URI_CHAR ** afterLast);
88static UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
89 const URI_CHAR ** afterLast);
90static void URI_FUNC(FixPercentEncodingEngine)(
91 const URI_CHAR * inFirst, const URI_CHAR * inAfterLast,
92 const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast);
93
94static UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
95 const URI_CHAR * afterLast);
96static UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first,
97 const URI_CHAR * afterLast);
98
99static void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
100 const URI_CHAR * afterLast);
101static UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
102 const URI_CHAR ** afterLast);
103
104static void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri,
105 unsigned int revertMask);
106
107
108
109static URI_INLINE void URI_FUNC(PreventLeakage)(URI_TYPE(Uri) * uri,
110 unsigned int revertMask) {
111 if (revertMask & URI_NORMALIZE_SCHEME) {
112 free((URI_CHAR *)uri->scheme.first);
113 uri->scheme.first = NULL;
114 uri->scheme.afterLast = NULL;
115 }
116
117 if (revertMask & URI_NORMALIZE_USER_INFO) {
118 free((URI_CHAR *)uri->userInfo.first);
119 uri->userInfo.first = NULL;
120 uri->userInfo.afterLast = NULL;
121 }
122
123 if (revertMask & URI_NORMALIZE_HOST) {
124 if (uri->hostData.ipFuture.first != NULL) {
125 /* IPvFuture */
126 free((URI_CHAR *)uri->hostData.ipFuture.first);
127 uri->hostData.ipFuture.first = NULL;
128 uri->hostData.ipFuture.afterLast = NULL;
129 uri->hostText.first = NULL;
130 uri->hostText.afterLast = NULL;
131 } else if ((uri->hostText.first != NULL)
132 && (uri->hostData.ip4 == NULL)
133 && (uri->hostData.ip6 == NULL)) {
134 /* Regname */
135 free((URI_CHAR *)uri->hostText.first);
136 uri->hostText.first = NULL;
137 uri->hostText.afterLast = NULL;
138 }
139 }
140
141 /* NOTE: Port cannot happen! */
142
143 if (revertMask & URI_NORMALIZE_PATH) {
144 URI_TYPE(PathSegment) * walker = uri->pathHead;
145 while (walker != NULL) {
146 URI_TYPE(PathSegment) * const next = walker->next;
147 if (walker->text.afterLast > walker->text.first) {
148 free((URI_CHAR *)walker->text.first);
149 }
150 free(walker);
151 walker = next;
152 }
153 uri->pathHead = NULL;
154 uri->pathTail = NULL;
155 }
156
157 if (revertMask & URI_NORMALIZE_QUERY) {
158 free((URI_CHAR *)uri->query.first);
159 uri->query.first = NULL;
160 uri->query.afterLast = NULL;
161 }
162
163 if (revertMask & URI_NORMALIZE_FRAGMENT) {
164 free((URI_CHAR *)uri->fragment.first);
165 uri->fragment.first = NULL;
166 uri->fragment.afterLast = NULL;
167 }
168}
169
170
171
172static URI_INLINE UriBool URI_FUNC(ContainsUppercaseLetters)(const URI_CHAR * first,
173 const URI_CHAR * afterLast) {
174 if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
175 const URI_CHAR * i = first;
176 for (; i < afterLast; i++) {
177 /* 6.2.2.1 Case Normalization: uppercase letters in scheme or host */
178 if ((*i >= _UT('A')) && (*i <= _UT('Z'))) {
179 return URI_TRUE;
180 }
181 }
182 }
183 return URI_FALSE;
184}
185
186
187
188static URI_INLINE UriBool URI_FUNC(ContainsUglyPercentEncoding)(const URI_CHAR * first,
189 const URI_CHAR * afterLast) {
190 if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
191 const URI_CHAR * i = first;
192 for (; i + 2 < afterLast; i++) {
193 if (i[0] == _UT('%')) {
194 /* 6.2.2.1 Case Normalization: *
195 * lowercase percent-encodings */
196 if (((i[1] >= _UT('a')) && (i[1] <= _UT('f')))
197 || ((i[2] >= _UT('a')) && (i[2] <= _UT('f')))) {
198 return URI_TRUE;
199 } else {
200 /* 6.2.2.2 Percent-Encoding Normalization: *
201 * percent-encoded unreserved characters */
202 const unsigned char left = URI_FUNC(HexdigToInt)(i[1]);
203 const unsigned char right = URI_FUNC(HexdigToInt)(i[2]);
204 const int code = 16 * left + right;
205 if (uriIsUnreserved(code)) {
206 return URI_TRUE;
207 }
208 }
209 }
210 }
211 }
212 return URI_FALSE;
213}
214
215
216
217static URI_INLINE void URI_FUNC(LowercaseInplace)(const URI_CHAR * first,
218 const URI_CHAR * afterLast) {
219 if ((first != NULL) && (afterLast != NULL) && (afterLast > first)) {
220 URI_CHAR * i = (URI_CHAR *)first;
221 const int lowerUpperDiff = (_UT('a') - _UT('A'));
222 for (; i < afterLast; i++) {
223 if ((*i >= _UT('A')) && (*i <=_UT('Z'))) {
224 *i = (URI_CHAR)(*i + lowerUpperDiff);
225 }
226 }
227 }
228}
229
230
231
232static URI_INLINE UriBool URI_FUNC(LowercaseMalloc)(const URI_CHAR ** first,
233 const URI_CHAR ** afterLast) {
234 int lenInChars;
235 const int lowerUpperDiff = (_UT('a') - _UT('A'));
236 URI_CHAR * buffer;
237 int i = 0;
238
239 if ((first == NULL) || (afterLast == NULL) || (*first == NULL)
240 || (*afterLast == NULL)) {
241 return URI_FALSE;
242 }
243
244 lenInChars = (int)(*afterLast - *first);
245 if (lenInChars == 0) {
246 return URI_TRUE;
247 } else if (lenInChars < 0) {
248 return URI_FALSE;
249 }
250
251 buffer = malloc(lenInChars * sizeof(URI_CHAR));
252 if (buffer == NULL) {
253 return URI_FALSE;
254 }
255
256 for (; i < lenInChars; i++) {
257 if (((*first)[i] >= _UT('A')) && ((*first)[i] <=_UT('Z'))) {
258 buffer[i] = (URI_CHAR)((*first)[i] + lowerUpperDiff);
259 } else {
260 buffer[i] = (*first)[i];
261 }
262 }
263
264 *first = buffer;
265 *afterLast = buffer + lenInChars;
266 return URI_TRUE;
267}
268
269
270
271/* NOTE: Implementation must stay inplace-compatible */
272static URI_INLINE void URI_FUNC(FixPercentEncodingEngine)(
273 const URI_CHAR * inFirst, const URI_CHAR * inAfterLast,
274 const URI_CHAR * outFirst, const URI_CHAR ** outAfterLast) {
275 URI_CHAR * write = (URI_CHAR *)outFirst;
276 const int lenInChars = (int)(inAfterLast - inFirst);
277 int i = 0;
278
279 /* All but last two */
280 for (; i + 2 < lenInChars; i++) {
281 if (inFirst[i] != _UT('%')) {
282 write[0] = inFirst[i];
283 write++;
284 } else {
285 /* 6.2.2.2 Percent-Encoding Normalization: *
286 * percent-encoded unreserved characters */
287 const URI_CHAR one = inFirst[i + 1];
288 const URI_CHAR two = inFirst[i + 2];
289 const unsigned char left = URI_FUNC(HexdigToInt)(one);
290 const unsigned char right = URI_FUNC(HexdigToInt)(two);
291 const int code = 16 * left + right;
292 if (uriIsUnreserved(code)) {
293 write[0] = (URI_CHAR)(code);
294 write++;
295 } else {
296 /* 6.2.2.1 Case Normalization: *
297 * lowercase percent-encodings */
298 write[0] = _UT('%');
299 write[1] = URI_FUNC(HexToLetter)(left);
300 write[2] = URI_FUNC(HexToLetter)(right);
301 write += 3;
302 }
303
304 i += 2; /* For the two chars of the percent group we just ate */
305 }
306 }
307
308 /* Last two */
309 for (; i < lenInChars; i++) {
310 write[0] = inFirst[i];
311 write++;
312 }
313
314 *outAfterLast = write;
315}
316
317
318
319static URI_INLINE void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first,
320 const URI_CHAR ** afterLast) {
321 /* Death checks */
322 if ((first == NULL) || (afterLast == NULL) || (*afterLast == NULL)) {
323 return;
324 }
325
326 /* Fix inplace */
327 URI_FUNC(FixPercentEncodingEngine)(first, *afterLast, first, afterLast);
328}
329
330
331
332static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** first,
333 const URI_CHAR ** afterLast) {
334 int lenInChars;
335 URI_CHAR * buffer;
336
337 /* Death checks */
338 if ((first == NULL) || (afterLast == NULL)
339 || (*first == NULL) || (*afterLast == NULL)) {
340 return URI_FALSE;
341 }
342
343 /* Old text length */
344 lenInChars = (int)(*afterLast - *first);
345 if (lenInChars == 0) {
346 return URI_TRUE;
347 } else if (lenInChars < 0) {
348 return URI_FALSE;
349 }
350
351 /* New buffer */
352 buffer = malloc(lenInChars * sizeof(URI_CHAR));
353 if (buffer == NULL) {
354 return URI_FALSE;
355 }
356
357 /* Fix on copy */
358 URI_FUNC(FixPercentEncodingEngine)(*first, *afterLast, buffer, afterLast);
359 *first = buffer;
360 return URI_TRUE;
361}
362
363
364
365static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask,
366 unsigned int maskTest, URI_TYPE(TextRange) * range) {
367 if (((*doneMask & maskTest) == 0)
368 && (range->first != NULL)
369 && (range->afterLast != NULL)
370 && (range->afterLast > range->first)) {
371 const int lenInChars = (int)(range->afterLast - range->first);
372 const int lenInBytes = lenInChars * sizeof(URI_CHAR);
373 URI_CHAR * dup = malloc(lenInBytes);
374 if (dup == NULL) {
375 return URI_FALSE; /* Raises malloc error */
376 }
377 memcpy(dup, range->first, lenInBytes);
378 range->first = dup;
379 range->afterLast = dup + lenInChars;
380 *doneMask |= maskTest;
381 }
382 return URI_TRUE;
383}
384
385
386
387static URI_INLINE UriBool URI_FUNC(MakeOwner)(URI_TYPE(Uri) * uri,
388 unsigned int * doneMask) {
389 URI_TYPE(PathSegment) * walker = uri->pathHead;
390 if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_SCHEME,
391 &(uri->scheme))
392 || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_USER_INFO,
393 &(uri->userInfo))
394 || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_QUERY,
395 &(uri->query))
396 || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_FRAGMENT,
397 &(uri->fragment))) {
398 return URI_FALSE; /* Raises malloc error */
399 }
400
401 /* Host */
402 if ((*doneMask & URI_NORMALIZE_HOST) == 0) {
403 if ((uri->hostData.ip4 == NULL)
404 && (uri->hostData.ip6 == NULL)) {
405 if (uri->hostData.ipFuture.first != NULL) {
406 /* IPvFuture */
407 if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST,
408 &(uri->hostData.ipFuture))) {
409 return URI_FALSE; /* Raises malloc error */
410 }
411 uri->hostText.first = uri->hostData.ipFuture.first;
412 uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
413 } else if (uri->hostText.first != NULL) {
414 /* Regname */
415 if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST,
416 &(uri->hostText))) {
417 return URI_FALSE; /* Raises malloc error */
418 }
419 }
420 }
421 }
422
423 /* Path */
424 if ((*doneMask & URI_NORMALIZE_PATH) == 0) {
425 while (walker != NULL) {
426 if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(walker->text))) {
427 /* Free allocations done so far and kill path */
428
429 /* Kill path to one before walker (if any) */
430 URI_TYPE(PathSegment) * ranger = uri->pathHead;
431 while (ranger != walker) {
432 URI_TYPE(PathSegment) * const next = ranger->next;
433 if ((ranger->text.first != NULL)
434 && (ranger->text.afterLast != NULL)
435 && (ranger->text.afterLast > ranger->text.first)) {
436 free((URI_CHAR *)ranger->text.first);
437 free(ranger);
438 }
439 ranger = next;
440 }
441
442 /* Kill path from walker */
443 while (walker != NULL) {
444 URI_TYPE(PathSegment) * const next = walker->next;
445 free(walker);
446 walker = next;
447 }
448
449 uri->pathHead = NULL;
450 uri->pathTail = NULL;
451 return URI_FALSE; /* Raises malloc error */
452 }
453 walker = walker->next;
454 }
455 *doneMask |= URI_NORMALIZE_PATH;
456 }
457
458 /* Port text, must come last so we don't have to undo that one if it fails. *
459 * Otherwise we would need and extra enum flag for it although the port *
460 * cannot go unnormalized... */
461 if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(uri->portText))) {
462 return URI_FALSE; /* Raises malloc error */
463 }
464
465 return URI_TRUE;
466}
467
468
469
470unsigned int URI_FUNC(NormalizeSyntaxMaskRequired)(const URI_TYPE(Uri) * uri) {
471 unsigned int res;
472#if defined(__GNUC__) && ((__GNUC__ > 4) \
473 || ((__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 2)))
474 /* Slower code that fixes a warning, not sure if this is a smart idea */
475 URI_TYPE(Uri) writeableClone;
476 memcpy(&writeableClone, uri, 1 * sizeof(URI_TYPE(Uri)));
477 URI_FUNC(NormalizeSyntaxEngine)(&writeableClone, 0, &res);
478#else
479 URI_FUNC(NormalizeSyntaxEngine)((URI_TYPE(Uri) *)uri, 0, &res);
480#endif
481 return res;
482}
483
484
485
486int URI_FUNC(NormalizeSyntaxEx)(URI_TYPE(Uri) * uri, unsigned int mask) {
487 return URI_FUNC(NormalizeSyntaxEngine)(uri, mask, NULL);
488}
489
490
491
492int URI_FUNC(NormalizeSyntax)(URI_TYPE(Uri) * uri) {
493 return URI_FUNC(NormalizeSyntaxEx)(uri, (unsigned int)-1);
494}
495
496
497
498static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask) {
499 unsigned int doneMask = URI_NORMALIZED;
500 if (uri == NULL) {
501 if (outMask != NULL) {
502 *outMask = URI_NORMALIZED;
503 return URI_SUCCESS;
504 } else {
505 return URI_ERROR_NULL;
506 }
507 }
508
509 if (outMask != NULL) {
510 /* Reset mask */
511 *outMask = URI_NORMALIZED;
512 } else if (inMask == URI_NORMALIZED) {
513 /* Nothing to do */
514 return URI_SUCCESS;
515 }
516
517 /* Scheme, host */
518 if (outMask != NULL) {
519 const UriBool normalizeScheme = URI_FUNC(ContainsUppercaseLetters)(
520 uri->scheme.first, uri->scheme.afterLast);
521 const UriBool normalizeHostCase = URI_FUNC(ContainsUppercaseLetters)(
522 uri->hostText.first, uri->hostText.afterLast);
523 if (normalizeScheme) {
524 *outMask |= URI_NORMALIZE_SCHEME;
525 }
526
527 if (normalizeHostCase) {
528 *outMask |= URI_NORMALIZE_HOST;
529 } else {
530 const UriBool normalizeHostPrecent = URI_FUNC(ContainsUglyPercentEncoding)(
531 uri->hostText.first, uri->hostText.afterLast);
532 if (normalizeHostPrecent) {
533 *outMask |= URI_NORMALIZE_HOST;
534 }
535 }
536 } else {
537 /* Scheme */
538 if ((inMask & URI_NORMALIZE_SCHEME) && (uri->scheme.first != NULL)) {
539 if (uri->owner) {
540 URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast);
541 } else {
542 if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first), &(uri->scheme.afterLast))) {
543 URI_FUNC(PreventLeakage)(uri, doneMask);
544 return URI_ERROR_MALLOC;
545 }
546 doneMask |= URI_NORMALIZE_SCHEME;
547 }
548 }
549
550 /* Host */
551 if (inMask & URI_NORMALIZE_HOST) {
552 if (uri->hostData.ipFuture.first != NULL) {
553 /* IPvFuture */
554 if (uri->owner) {
555 URI_FUNC(LowercaseInplace)(uri->hostData.ipFuture.first,
556 uri->hostData.ipFuture.afterLast);
557 } else {
558 if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first),
559 &(uri->hostData.ipFuture.afterLast))) {
560 URI_FUNC(PreventLeakage)(uri, doneMask);
561 return URI_ERROR_MALLOC;
562 }
563 doneMask |= URI_NORMALIZE_HOST;
564 }
565 uri->hostText.first = uri->hostData.ipFuture.first;
566 uri->hostText.afterLast = uri->hostData.ipFuture.afterLast;
567 } else if ((uri->hostText.first != NULL)
568 && (uri->hostData.ip4 == NULL)
569 && (uri->hostData.ip6 == NULL)) {
570 /* Regname */
571 if (uri->owner) {
572 URI_FUNC(FixPercentEncodingInplace)(uri->hostText.first,
573 &(uri->hostText.afterLast));
574 } else {
575 if (!URI_FUNC(FixPercentEncodingMalloc)(
576 &(uri->hostText.first),
577 &(uri->hostText.afterLast))) {
578 URI_FUNC(PreventLeakage)(uri, doneMask);
579 return URI_ERROR_MALLOC;
580 }
581 doneMask |= URI_NORMALIZE_HOST;
582 }
583
584 URI_FUNC(LowercaseInplace)(uri->hostText.first,
585 uri->hostText.afterLast);
586 }
587 }
588 }
589
590 /* User info */
591 if (outMask != NULL) {
592 const UriBool normalizeUserInfo = URI_FUNC(ContainsUglyPercentEncoding)(
593 uri->userInfo.first, uri->userInfo.afterLast);
594 if (normalizeUserInfo) {
595 *outMask |= URI_NORMALIZE_USER_INFO;
596 }
597 } else {
598 if ((inMask & URI_NORMALIZE_USER_INFO) && (uri->userInfo.first != NULL)) {
599 if (uri->owner) {
600 URI_FUNC(FixPercentEncodingInplace)(uri->userInfo.first, &(uri->userInfo.afterLast));
601 } else {
602 if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->userInfo.first),
603 &(uri->userInfo.afterLast))) {
604 URI_FUNC(PreventLeakage)(uri, doneMask);
605 return URI_ERROR_MALLOC;
606 }
607 doneMask |= URI_NORMALIZE_USER_INFO;
608 }
609 }
610 }
611
612 /* Path */
613 if (outMask != NULL) {
614 const URI_TYPE(PathSegment) * walker = uri->pathHead;
615 while (walker != NULL) {
616 const URI_CHAR * const first = walker->text.first;
617 const URI_CHAR * const afterLast = walker->text.afterLast;
618 if ((first != NULL)
619 && (afterLast != NULL)
620 && (afterLast > first)
621 && (
622 (((afterLast - first) == 1)
623 && (first[0] == _UT('.')))
624 ||
625 (((afterLast - first) == 2)
626 && (first[0] == _UT('.'))
627 && (first[1] == _UT('.')))
628 ||
629 URI_FUNC(ContainsUglyPercentEncoding)(first, afterLast)
630 )) {
631 *outMask |= URI_NORMALIZE_PATH;
632 break;
633 }
634 walker = walker->next;
635 }
636 } else if (inMask & URI_NORMALIZE_PATH) {
637 URI_TYPE(PathSegment) * walker;
638 const UriBool relative = ((uri->scheme.first == NULL)
639 && !uri->absolutePath) ? URI_TRUE : URI_FALSE;
640
641 /* Fix percent-encoding for each segment */
642 walker = uri->pathHead;
643 if (uri->owner) {
644 while (walker != NULL) {
645 URI_FUNC(FixPercentEncodingInplace)(walker->text.first, &(walker->text.afterLast));
646 walker = walker->next;
647 }
648 } else {
649 while (walker != NULL) {
650 if (!URI_FUNC(FixPercentEncodingMalloc)(&(walker->text.first),
651 &(walker->text.afterLast))) {
652 URI_FUNC(PreventLeakage)(uri, doneMask);
653 return URI_ERROR_MALLOC;
654 }
655 walker = walker->next;
656 }
657 doneMask |= URI_NORMALIZE_PATH;
658 }
659
660 /* 6.2.2.3 Path Segment Normalization */
661 if (!URI_FUNC(RemoveDotSegmentsEx)(uri, relative,
662 (uri->owner == URI_TRUE)
663 || ((doneMask & URI_NORMALIZE_PATH) != 0)
664 )) {
665 URI_FUNC(PreventLeakage)(uri, doneMask);
666 return URI_ERROR_MALLOC;
667 }
668 URI_FUNC(FixEmptyTrailSegment)(uri);
669 }
670
671 /* Query, fragment */
672 if (outMask != NULL) {
673 const UriBool normalizeQuery = URI_FUNC(ContainsUglyPercentEncoding)(
674 uri->query.first, uri->query.afterLast);
675 const UriBool normalizeFragment = URI_FUNC(ContainsUglyPercentEncoding)(
676 uri->fragment.first, uri->fragment.afterLast);
677 if (normalizeQuery) {
678 *outMask |= URI_NORMALIZE_QUERY;
679 }
680
681 if (normalizeFragment) {
682 *outMask |= URI_NORMALIZE_FRAGMENT;
683 }
684 } else {
685 /* Query */
686 if ((inMask & URI_NORMALIZE_QUERY) && (uri->query.first != NULL)) {
687 if (uri->owner) {
688 URI_FUNC(FixPercentEncodingInplace)(uri->query.first, &(uri->query.afterLast));
689 } else {
690 if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->query.first),
691 &(uri->query.afterLast))) {
692 URI_FUNC(PreventLeakage)(uri, doneMask);
693 return URI_ERROR_MALLOC;
694 }
695 doneMask |= URI_NORMALIZE_QUERY;
696 }
697 }
698
699 /* Fragment */
700 if ((inMask & URI_NORMALIZE_FRAGMENT) && (uri->fragment.first != NULL)) {
701 if (uri->owner) {
702 URI_FUNC(FixPercentEncodingInplace)(uri->fragment.first, &(uri->fragment.afterLast));
703 } else {
704 if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->fragment.first),
705 &(uri->fragment.afterLast))) {
706 URI_FUNC(PreventLeakage)(uri, doneMask);
707 return URI_ERROR_MALLOC;
708 }
709 doneMask |= URI_NORMALIZE_FRAGMENT;
710 }
711 }
712 }
713
714 /* Dup all not duped yet */
715 if ((outMask == NULL) && !uri->owner) {
716 if (!URI_FUNC(MakeOwner)(uri, &doneMask)) {
717 URI_FUNC(PreventLeakage)(uri, doneMask);
718 return URI_ERROR_MALLOC;
719 }
720 uri->owner = URI_TRUE;
721 }
722
723 return URI_SUCCESS;
724}
725
726
727
728#endif
diff --git a/plugins/uriparser/UriNormalizeBase.c b/plugins/uriparser/UriNormalizeBase.c
new file mode 100644
index 00000000..bac58886
--- /dev/null
+++ b/plugins/uriparser/UriNormalizeBase.c
@@ -0,0 +1,119 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef URI_DOXYGEN
41# include "UriNormalizeBase.h"
42#endif
43
44
45
46UriBool uriIsUnreserved(int code) {
47 switch (code) {
48 case L'a': /* ALPHA */
49 case L'A':
50 case L'b':
51 case L'B':
52 case L'c':
53 case L'C':
54 case L'd':
55 case L'D':
56 case L'e':
57 case L'E':
58 case L'f':
59 case L'F':
60 case L'g':
61 case L'G':
62 case L'h':
63 case L'H':
64 case L'i':
65 case L'I':
66 case L'j':
67 case L'J':
68 case L'k':
69 case L'K':
70 case L'l':
71 case L'L':
72 case L'm':
73 case L'M':
74 case L'n':
75 case L'N':
76 case L'o':
77 case L'O':
78 case L'p':
79 case L'P':
80 case L'q':
81 case L'Q':
82 case L'r':
83 case L'R':
84 case L's':
85 case L'S':
86 case L't':
87 case L'T':
88 case L'u':
89 case L'U':
90 case L'v':
91 case L'V':
92 case L'w':
93 case L'W':
94 case L'x':
95 case L'X':
96 case L'y':
97 case L'Y':
98 case L'z':
99 case L'Z':
100 case L'0': /* DIGIT */
101 case L'1':
102 case L'2':
103 case L'3':
104 case L'4':
105 case L'5':
106 case L'6':
107 case L'7':
108 case L'8':
109 case L'9':
110 case L'-': /* "-" / "." / "_" / "~" */
111 case L'.':
112 case L'_':
113 case L'~':
114 return URI_TRUE;
115
116 default:
117 return URI_FALSE;
118 }
119}
diff --git a/plugins/uriparser/UriNormalizeBase.h b/plugins/uriparser/UriNormalizeBase.h
new file mode 100644
index 00000000..9f3abd55
--- /dev/null
+++ b/plugins/uriparser/UriNormalizeBase.h
@@ -0,0 +1,53 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef URI_NORMALIZE_BASE_H
41#define URI_NORMALIZE_BASE_H 1
42
43
44
45#include <uriparser/UriBase.h>
46
47
48
49UriBool uriIsUnreserved(int code);
50
51
52
53#endif /* URI_NORMALIZE_BASE_H */
diff --git a/plugins/uriparser/UriParse.c b/plugins/uriparser/UriParse.c
new file mode 100644
index 00000000..e3cdc68d
--- /dev/null
+++ b/plugins/uriparser/UriParse.c
@@ -0,0 +1,2241 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/**
41 * @file UriParse.c
42 * Holds the RFC 3986 %URI parsing implementation.
43 * NOTE: This source file includes itself twice.
44 */
45
46/* What encodings are enabled? */
47#include <uriparser/UriDefsConfig.h>
48#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
49/* Include SELF twice */
50# ifdef URI_ENABLE_ANSI
51# define URI_PASS_ANSI 1
52# include "UriParse.c"
53# undef URI_PASS_ANSI
54# endif
55# ifdef URI_ENABLE_UNICODE
56# define URI_PASS_UNICODE 1
57# include "UriParse.c"
58# undef URI_PASS_UNICODE
59# endif
60#else
61# ifdef URI_PASS_ANSI
62# include <uriparser/UriDefsAnsi.h>
63# else
64# include <uriparser/UriDefsUnicode.h>
65# include <wchar.h>
66# endif
67
68
69
70#ifndef URI_DOXYGEN
71# include <uriparser/Uri.h>
72# include <uriparser/UriIp4.h>
73# include "UriCommon.h"
74# include "UriParseBase.h"
75#endif
76
77
78
79#define URI_SET_DIGIT \
80 _UT('0'): \
81 case _UT('1'): \
82 case _UT('2'): \
83 case _UT('3'): \
84 case _UT('4'): \
85 case _UT('5'): \
86 case _UT('6'): \
87 case _UT('7'): \
88 case _UT('8'): \
89 case _UT('9')
90
91#define URI_SET_HEX_LETTER_UPPER \
92 _UT('A'): \
93 case _UT('B'): \
94 case _UT('C'): \
95 case _UT('D'): \
96 case _UT('E'): \
97 case _UT('F')
98
99#define URI_SET_HEX_LETTER_LOWER \
100 _UT('a'): \
101 case _UT('b'): \
102 case _UT('c'): \
103 case _UT('d'): \
104 case _UT('e'): \
105 case _UT('f')
106
107#define URI_SET_HEXDIG \
108 URI_SET_DIGIT: \
109 case URI_SET_HEX_LETTER_UPPER: \
110 case URI_SET_HEX_LETTER_LOWER
111
112#define URI_SET_ALPHA \
113 URI_SET_HEX_LETTER_UPPER: \
114 case URI_SET_HEX_LETTER_LOWER: \
115 case _UT('g'): \
116 case _UT('G'): \
117 case _UT('h'): \
118 case _UT('H'): \
119 case _UT('i'): \
120 case _UT('I'): \
121 case _UT('j'): \
122 case _UT('J'): \
123 case _UT('k'): \
124 case _UT('K'): \
125 case _UT('l'): \
126 case _UT('L'): \
127 case _UT('m'): \
128 case _UT('M'): \
129 case _UT('n'): \
130 case _UT('N'): \
131 case _UT('o'): \
132 case _UT('O'): \
133 case _UT('p'): \
134 case _UT('P'): \
135 case _UT('q'): \
136 case _UT('Q'): \
137 case _UT('r'): \
138 case _UT('R'): \
139 case _UT('s'): \
140 case _UT('S'): \
141 case _UT('t'): \
142 case _UT('T'): \
143 case _UT('u'): \
144 case _UT('U'): \
145 case _UT('v'): \
146 case _UT('V'): \
147 case _UT('w'): \
148 case _UT('W'): \
149 case _UT('x'): \
150 case _UT('X'): \
151 case _UT('y'): \
152 case _UT('Y'): \
153 case _UT('z'): \
154 case _UT('Z')
155
156
157
158static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
159static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
160static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
161static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
162static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
163static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
164static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
165static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
166static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
167static const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
168static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
169static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
170static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
171static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
172static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
173static const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
174static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
175static const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
176static const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
177static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
178static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
179static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
180static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
181static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
182static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
183static const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
184static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
185static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
186static const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
187static const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
188static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
189
190static UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
191static UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
192static UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
193static UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first);
194static void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state);
195
196static void URI_FUNC(ResetParserState)(URI_TYPE(ParserState) * state);
197
198static UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast);
199
200static void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state, const URI_CHAR * errorPos);
201static void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state);
202
203
204
205static URI_INLINE void URI_FUNC(StopSyntax)(URI_TYPE(ParserState) * state,
206 const URI_CHAR * errorPos) {
207 URI_FUNC(FreeUriMembers)(state->uri);
208 state->errorPos = errorPos;
209 state->errorCode = URI_ERROR_SYNTAX;
210}
211
212
213
214static URI_INLINE void URI_FUNC(StopMalloc)(URI_TYPE(ParserState) * state) {
215 URI_FUNC(FreeUriMembers)(state->uri);
216 state->errorPos = NULL;
217 state->errorCode = URI_ERROR_MALLOC;
218}
219
220
221
222/*
223 * [authority]-><[>[ipLit2][authorityTwo]
224 * [authority]->[ownHostUserInfoNz]
225 * [authority]-><NULL>
226 */
227static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
228 if (first >= afterLast) {
229 /* "" regname host */
230 state->uri->hostText.first = URI_FUNC(SafeToPointTo);
231 state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo);
232 return afterLast;
233 }
234
235 switch (*first) {
236 case _UT('['):
237 {
238 const URI_CHAR * const afterIpLit2
239 = URI_FUNC(ParseIpLit2)(state, first + 1, afterLast);
240 if (afterIpLit2 == NULL) {
241 return NULL;
242 }
243 state->uri->hostText.first = first + 1; /* HOST BEGIN */
244 return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast);
245 }
246
247 case _UT('!'):
248 case _UT('$'):
249 case _UT('%'):
250 case _UT('&'):
251 case _UT('('):
252 case _UT(')'):
253 case _UT('-'):
254 case _UT('*'):
255 case _UT(','):
256 case _UT('.'):
257 case _UT(':'):
258 case _UT(';'):
259 case _UT('@'):
260 case _UT('\''):
261 case _UT('_'):
262 case _UT('~'):
263 case _UT('+'):
264 case _UT('='):
265 case URI_SET_DIGIT:
266 case URI_SET_ALPHA:
267 state->uri->userInfo.first = first; /* USERINFO BEGIN */
268 return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast);
269
270 default:
271 /* "" regname host */
272 state->uri->hostText.first = URI_FUNC(SafeToPointTo);
273 state->uri->hostText.afterLast = URI_FUNC(SafeToPointTo);
274 return first;
275 }
276}
277
278
279
280/*
281 * [authorityTwo]-><:>[port]
282 * [authorityTwo]-><NULL>
283 */
284static URI_INLINE const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
285 if (first >= afterLast) {
286 return afterLast;
287 }
288
289 switch (*first) {
290 case _UT(':'):
291 {
292 const URI_CHAR * const afterPort = URI_FUNC(ParsePort)(state, first + 1, afterLast);
293 if (afterPort == NULL) {
294 return NULL;
295 }
296 state->uri->portText.first = first + 1; /* PORT BEGIN */
297 state->uri->portText.afterLast = afterPort; /* PORT END */
298 return afterPort;
299 }
300
301 default:
302 return first;
303 }
304}
305
306
307
308/*
309 * [hexZero]->[HEXDIG][hexZero]
310 * [hexZero]-><NULL>
311 */
312static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
313 if (first >= afterLast) {
314 return afterLast;
315 }
316
317 switch (*first) {
318 case URI_SET_HEXDIG:
319 return URI_FUNC(ParseHexZero)(state, first + 1, afterLast);
320
321 default:
322 return first;
323 }
324}
325
326
327
328/*
329 * [hierPart]->[pathRootless]
330 * [hierPart]-></>[partHelperTwo]
331 * [hierPart]-><NULL>
332 */
333static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
334 if (first >= afterLast) {
335 return afterLast;
336 }
337
338 switch (*first) {
339 case _UT('!'):
340 case _UT('$'):
341 case _UT('%'):
342 case _UT('&'):
343 case _UT('('):
344 case _UT(')'):
345 case _UT('-'):
346 case _UT('*'):
347 case _UT(','):
348 case _UT('.'):
349 case _UT(':'):
350 case _UT(';'):
351 case _UT('@'):
352 case _UT('\''):
353 case _UT('_'):
354 case _UT('~'):
355 case _UT('+'):
356 case _UT('='):
357 case URI_SET_DIGIT:
358 case URI_SET_ALPHA:
359 return URI_FUNC(ParsePathRootless)(state, first, afterLast);
360
361 case _UT('/'):
362 return URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast);
363
364 default:
365 return first;
366 }
367}
368
369
370
371/*
372 * [ipFutLoop]->[subDelims][ipFutStopGo]
373 * [ipFutLoop]->[unreserved][ipFutStopGo]
374 * [ipFutLoop]-><:>[ipFutStopGo]
375 */
376static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
377 if (first >= afterLast) {
378 URI_FUNC(StopSyntax)(state, first);
379 return NULL;
380 }
381
382 switch (*first) {
383 case _UT('!'):
384 case _UT('$'):
385 case _UT('&'):
386 case _UT('('):
387 case _UT(')'):
388 case _UT('-'):
389 case _UT('*'):
390 case _UT(','):
391 case _UT('.'):
392 case _UT(':'):
393 case _UT(';'):
394 case _UT('\''):
395 case _UT('_'):
396 case _UT('~'):
397 case _UT('+'):
398 case _UT('='):
399 case URI_SET_DIGIT:
400 case URI_SET_ALPHA:
401 return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast);
402
403 default:
404 URI_FUNC(StopSyntax)(state, first);
405 return NULL;
406 }
407}
408
409
410
411/*
412 * [ipFutStopGo]->[ipFutLoop]
413 * [ipFutStopGo]-><NULL>
414 */
415static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
416 if (first >= afterLast) {
417 return afterLast;
418 }
419
420 switch (*first) {
421 case _UT('!'):
422 case _UT('$'):
423 case _UT('&'):
424 case _UT('('):
425 case _UT(')'):
426 case _UT('-'):
427 case _UT('*'):
428 case _UT(','):
429 case _UT('.'):
430 case _UT(':'):
431 case _UT(';'):
432 case _UT('\''):
433 case _UT('_'):
434 case _UT('~'):
435 case _UT('+'):
436 case _UT('='):
437 case URI_SET_DIGIT:
438 case URI_SET_ALPHA:
439 return URI_FUNC(ParseIpFutLoop)(state, first, afterLast);
440
441 default:
442 return first;
443 }
444}
445
446
447
448/*
449 * [ipFuture]-><v>[HEXDIG][hexZero]<.>[ipFutLoop]
450 */
451static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
452 if (first >= afterLast) {
453 URI_FUNC(StopSyntax)(state, first);
454 return NULL;
455 }
456
457 /*
458 First character has already been
459 checked before entering this rule.
460
461 switch (*first) {
462 case _UT('v'):
463 */
464 if (first + 1 >= afterLast) {
465 URI_FUNC(StopSyntax)(state, first + 1);
466 return NULL;
467 }
468
469 switch (first[1]) {
470 case URI_SET_HEXDIG:
471 {
472 const URI_CHAR * afterIpFutLoop;
473 const URI_CHAR * const afterHexZero
474 = URI_FUNC(ParseHexZero)(state, first + 2, afterLast);
475 if (afterHexZero == NULL) {
476 return NULL;
477 }
478 if ((afterHexZero >= afterLast)
479 || (*afterHexZero != _UT('.'))) {
480 URI_FUNC(StopSyntax)(state, afterHexZero);
481 return NULL;
482 }
483 state->uri->hostText.first = first; /* HOST BEGIN */
484 state->uri->hostData.ipFuture.first = first; /* IPFUTURE BEGIN */
485 afterIpFutLoop = URI_FUNC(ParseIpFutLoop)(state, afterHexZero + 1, afterLast);
486 if (afterIpFutLoop == NULL) {
487 return NULL;
488 }
489 state->uri->hostText.afterLast = afterIpFutLoop; /* HOST END */
490 state->uri->hostData.ipFuture.afterLast = afterIpFutLoop; /* IPFUTURE END */
491 return afterIpFutLoop;
492 }
493
494 default:
495 URI_FUNC(StopSyntax)(state, first + 1);
496 return NULL;
497 }
498
499 /*
500 default:
501 URI_FUNC(StopSyntax)(state, first);
502 return NULL;
503 }
504 */
505}
506
507
508
509/*
510 * [ipLit2]->[ipFuture]<]>
511 * [ipLit2]->[IPv6address2]
512 */
513static URI_INLINE const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
514 if (first >= afterLast) {
515 URI_FUNC(StopSyntax)(state, first);
516 return NULL;
517 }
518
519 switch (*first) {
520 case _UT('v'):
521 {
522 const URI_CHAR * const afterIpFuture
523 = URI_FUNC(ParseIpFuture)(state, first, afterLast);
524 if (afterIpFuture == NULL) {
525 return NULL;
526 }
527 if ((afterIpFuture >= afterLast)
528 || (*afterIpFuture != _UT(']'))) {
529 URI_FUNC(StopSyntax)(state, first);
530 return NULL;
531 }
532 return afterIpFuture + 1;
533 }
534
535 case _UT(':'):
536 case _UT(']'):
537 case URI_SET_HEXDIG:
538 state->uri->hostData.ip6 = malloc(1 * sizeof(UriIp6)); /* Freed when stopping on parse error */
539 if (state->uri->hostData.ip6 == NULL) {
540 URI_FUNC(StopMalloc)(state);
541 return NULL;
542 }
543 return URI_FUNC(ParseIPv6address2)(state, first, afterLast);
544
545 default:
546 URI_FUNC(StopSyntax)(state, first);
547 return NULL;
548 }
549}
550
551
552
553/*
554 * [IPv6address2]->..<]>
555 */
556static const URI_CHAR * URI_FUNC(ParseIPv6address2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
557 int zipperEver = 0;
558 int quadsDone = 0;
559 int digitCount = 0;
560 unsigned char digitHistory[4];
561 int ip4OctetsDone = 0;
562
563 unsigned char quadsAfterZipper[14];
564 int quadsAfterZipperCount = 0;
565
566
567 for (;;) {
568 if (first >= afterLast) {
569 URI_FUNC(StopSyntax)(state, first);
570 return NULL;
571 }
572
573 /* Inside IPv4 part? */
574 if (ip4OctetsDone > 0) {
575 /* Eat rest of IPv4 address */
576 for (;;) {
577 switch (*first) {
578 case URI_SET_DIGIT:
579 if (digitCount == 4) {
580 URI_FUNC(StopSyntax)(state, first);
581 return NULL;
582 }
583 digitHistory[digitCount++] = (unsigned char)(9 + *first - _UT('9'));
584 break;
585
586 case _UT('.'):
587 if ((ip4OctetsDone == 4) /* NOTE! */
588 || (digitCount == 0)
589 || (digitCount == 4)) {
590 /* Invalid digit or octet count */
591 URI_FUNC(StopSyntax)(state, first);
592 return NULL;
593 } else if ((digitCount > 1)
594 && (digitHistory[0] == 0)) {
595 /* Leading zero */
596 URI_FUNC(StopSyntax)(state, first - digitCount);
597 return NULL;
598 } else if ((digitCount > 2)
599 && (digitHistory[1] == 0)) {
600 /* Leading zero */
601 URI_FUNC(StopSyntax)(state, first - digitCount + 1);
602 return NULL;
603 } else if ((digitCount == 3)
604 && (100 * digitHistory[0]
605 + 10 * digitHistory[1]
606 + digitHistory[2] > 255)) {
607 /* Octet value too large */
608 if (digitHistory[0] > 2) {
609 URI_FUNC(StopSyntax)(state, first - 3);
610 } else if (digitHistory[1] > 5) {
611 URI_FUNC(StopSyntax)(state, first - 2);
612 } else {
613 URI_FUNC(StopSyntax)(state, first - 1);
614 }
615 return NULL;
616 }
617
618 /* Copy IPv4 octet */
619 state->uri->hostData.ip6->data[16 - 4 + ip4OctetsDone] = uriGetOctetValue(digitHistory, digitCount);
620 digitCount = 0;
621 ip4OctetsDone++;
622 break;
623
624 case _UT(']'):
625 if ((ip4OctetsDone != 3) /* NOTE! */
626 || (digitCount == 0)
627 || (digitCount == 4)) {
628 /* Invalid digit or octet count */
629 URI_FUNC(StopSyntax)(state, first);
630 return NULL;
631 } else if ((digitCount > 1)
632 && (digitHistory[0] == 0)) {
633 /* Leading zero */
634 URI_FUNC(StopSyntax)(state, first - digitCount);
635 return NULL;
636 } else if ((digitCount > 2)
637 && (digitHistory[1] == 0)) {
638 /* Leading zero */
639 URI_FUNC(StopSyntax)(state, first - digitCount + 1);
640 return NULL;
641 } else if ((digitCount == 3)
642 && (100 * digitHistory[0]
643 + 10 * digitHistory[1]
644 + digitHistory[2] > 255)) {
645 /* Octet value too large */
646 if (digitHistory[0] > 2) {
647 URI_FUNC(StopSyntax)(state, first - 3);
648 } else if (digitHistory[1] > 5) {
649 URI_FUNC(StopSyntax)(state, first - 2);
650 } else {
651 URI_FUNC(StopSyntax)(state, first - 1);
652 }
653 return NULL;
654 }
655
656 state->uri->hostText.afterLast = first; /* HOST END */
657
658 /* Copy missing quads right before IPv4 */
659 memcpy(state->uri->hostData.ip6->data + 16 - 4 - 2 * quadsAfterZipperCount,
660 quadsAfterZipper, 2 * quadsAfterZipperCount);
661
662 /* Copy last IPv4 octet */
663 state->uri->hostData.ip6->data[16 - 4 + 3] = uriGetOctetValue(digitHistory, digitCount);
664
665 return first + 1;
666
667 default:
668 URI_FUNC(StopSyntax)(state, first);
669 return NULL;
670 }
671 first++;
672 }
673 } else {
674 /* Eat while no dot in sight */
675 int letterAmong = 0;
676 int walking = 1;
677 do {
678 switch (*first) {
679 case URI_SET_HEX_LETTER_LOWER:
680 letterAmong = 1;
681 if (digitCount == 4) {
682 URI_FUNC(StopSyntax)(state, first);
683 return NULL;
684 }
685 digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('f'));
686 digitCount++;
687 break;
688
689 case URI_SET_HEX_LETTER_UPPER:
690 letterAmong = 1;
691 if (digitCount == 4) {
692 URI_FUNC(StopSyntax)(state, first);
693 return NULL;
694 }
695 digitHistory[digitCount] = (unsigned char)(15 + *first - _UT('F'));
696 digitCount++;
697 break;
698
699 case URI_SET_DIGIT:
700 if (digitCount == 4) {
701 URI_FUNC(StopSyntax)(state, first);
702 return NULL;
703 }
704 digitHistory[digitCount] = (unsigned char)(9 + *first - _UT('9'));
705 digitCount++;
706 break;
707
708 case _UT(':'):
709 {
710 int setZipper = 0;
711
712 if (digitCount > 0) {
713 if (zipperEver) {
714 uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount);
715 quadsAfterZipperCount++;
716 } else {
717 uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone);
718 }
719 quadsDone++;
720 digitCount = 0;
721 }
722 letterAmong = 0;
723
724 /* Too many quads? */
725 if (quadsDone >= 8 - zipperEver) {
726 URI_FUNC(StopSyntax)(state, first);
727 return NULL;
728 }
729
730 /* "::"? */
731 if (first + 1 >= afterLast) {
732 URI_FUNC(StopSyntax)(state, first + 1);
733 return NULL;
734 }
735 if (first[1] == _UT(':')) {
736 const int resetOffset = 2 * (quadsDone + (digitCount > 0));
737
738 first++;
739 if (zipperEver) {
740 URI_FUNC(StopSyntax)(state, first);
741 return NULL; /* "::.+::" */
742 }
743
744 /* Zero everything after zipper */
745 memset(state->uri->hostData.ip6->data + resetOffset, 0, 16 - resetOffset);
746 setZipper = 1;
747
748 /* ":::+"? */
749 if (first + 1 >= afterLast) {
750 URI_FUNC(StopSyntax)(state, first + 1);
751 return NULL; /* No ']' yet */
752 }
753 if (first[1] == _UT(':')) {
754 URI_FUNC(StopSyntax)(state, first + 1);
755 return NULL; /* ":::+ "*/
756 }
757 }
758
759 if (setZipper) {
760 zipperEver = 1;
761 }
762 }
763 break;
764
765 case _UT('.'):
766 if ((quadsDone > 6) /* NOTE */
767 || (!zipperEver && (quadsDone < 6))
768 || letterAmong
769 || (digitCount == 0)
770 || (digitCount == 4)) {
771 /* Invalid octet before */
772 URI_FUNC(StopSyntax)(state, first);
773 return NULL;
774 } else if ((digitCount > 1)
775 && (digitHistory[0] == 0)) {
776 /* Leading zero */
777 URI_FUNC(StopSyntax)(state, first - digitCount);
778 return NULL;
779 } else if ((digitCount > 2)
780 && (digitHistory[1] == 0)) {
781 /* Leading zero */
782 URI_FUNC(StopSyntax)(state, first - digitCount + 1);
783 return NULL;
784 } else if ((digitCount == 3)
785 && (100 * digitHistory[0]
786 + 10 * digitHistory[1]
787 + digitHistory[2] > 255)) {
788 /* Octet value too large */
789 if (digitHistory[0] > 2) {
790 URI_FUNC(StopSyntax)(state, first - 3);
791 } else if (digitHistory[1] > 5) {
792 URI_FUNC(StopSyntax)(state, first - 2);
793 } else {
794 URI_FUNC(StopSyntax)(state, first - 1);
795 }
796 return NULL;
797 }
798
799 /* Copy first IPv4 octet */
800 state->uri->hostData.ip6->data[16 - 4] = uriGetOctetValue(digitHistory, digitCount);
801 digitCount = 0;
802
803 /* Switch over to IPv4 loop */
804 ip4OctetsDone = 1;
805 walking = 0;
806 break;
807
808 case _UT(']'):
809 /* Too little quads? */
810 if (!zipperEver && !((quadsDone == 7) && (digitCount > 0))) {
811 URI_FUNC(StopSyntax)(state, first);
812 return NULL;
813 }
814
815 if (digitCount > 0) {
816 if (zipperEver) {
817 uriWriteQuadToDoubleByte(digitHistory, digitCount, quadsAfterZipper + 2 * quadsAfterZipperCount);
818 quadsAfterZipperCount++;
819 } else {
820 uriWriteQuadToDoubleByte(digitHistory, digitCount, state->uri->hostData.ip6->data + 2 * quadsDone);
821 }
822 /*
823 quadsDone++;
824 digitCount = 0;
825 */
826 }
827
828 /* Copy missing quads to the end */
829 memcpy(state->uri->hostData.ip6->data + 16 - 2 * quadsAfterZipperCount,
830 quadsAfterZipper, 2 * quadsAfterZipperCount);
831
832 state->uri->hostText.afterLast = first; /* HOST END */
833 return first + 1; /* Fine */
834
835 default:
836 URI_FUNC(StopSyntax)(state, first);
837 return NULL;
838 }
839 first++;
840
841 if (first >= afterLast) {
842 URI_FUNC(StopSyntax)(state, first);
843 return NULL; /* No ']' yet */
844 }
845 } while (walking);
846 }
847 }
848}
849
850
851
852/*
853 * [mustBeSegmentNzNc]->[pctEncoded][mustBeSegmentNzNc]
854 * [mustBeSegmentNzNc]->[subDelims][mustBeSegmentNzNc]
855 * [mustBeSegmentNzNc]->[unreserved][mustBeSegmentNzNc]
856 * [mustBeSegmentNzNc]->[uriTail] // can take <NULL>
857 * [mustBeSegmentNzNc]-></>[segment][zeroMoreSlashSegs][uriTail]
858 * [mustBeSegmentNzNc]-><@>[mustBeSegmentNzNc]
859 */
860static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
861 if (first >= afterLast) {
862 if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
863 URI_FUNC(StopMalloc)(state);
864 return NULL;
865 }
866 state->uri->scheme.first = NULL; /* Not a scheme, reset */
867 return afterLast;
868 }
869
870 switch (*first) {
871 case _UT('%'):
872 {
873 const URI_CHAR * const afterPctEncoded
874 = URI_FUNC(ParsePctEncoded)(state, first, afterLast);
875 if (afterPctEncoded == NULL) {
876 return NULL;
877 }
878 return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
879 }
880
881 case _UT('@'):
882 case _UT('!'):
883 case _UT('$'):
884 case _UT('&'):
885 case _UT('('):
886 case _UT(')'):
887 case _UT('*'):
888 case _UT(','):
889 case _UT(';'):
890 case _UT('\''):
891 case _UT('+'):
892 case _UT('='):
893 case _UT('-'):
894 case _UT('.'):
895 case _UT('_'):
896 case _UT('~'):
897 case URI_SET_DIGIT:
898 case URI_SET_ALPHA:
899 return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
900
901 case _UT('/'):
902 {
903 const URI_CHAR * afterZeroMoreSlashSegs;
904 const URI_CHAR * afterSegment;
905 if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
906 URI_FUNC(StopMalloc)(state);
907 return NULL;
908 }
909 state->uri->scheme.first = NULL; /* Not a scheme, reset */
910 afterSegment = URI_FUNC(ParseSegment)(state, first + 1, afterLast);
911 if (afterSegment == NULL) {
912 return NULL;
913 }
914 if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
915 URI_FUNC(StopMalloc)(state);
916 return NULL;
917 }
918 afterZeroMoreSlashSegs
919 = URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
920 if (afterZeroMoreSlashSegs == NULL) {
921 return NULL;
922 }
923 return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast);
924 }
925
926 default:
927 if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
928 URI_FUNC(StopMalloc)(state);
929 return NULL;
930 }
931 state->uri->scheme.first = NULL; /* Not a scheme, reset */
932 return URI_FUNC(ParseUriTail)(state, first, afterLast);
933 }
934}
935
936
937
938/*
939 * [ownHost]-><[>[ipLit2][authorityTwo]
940 * [ownHost]->[ownHost2] // can take <NULL>
941 */
942static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHost)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
943 if (first >= afterLast) {
944 return afterLast;
945 }
946
947 switch (*first) {
948 case _UT('['):
949 {
950 const URI_CHAR * const afterIpLit2
951 = URI_FUNC(ParseIpLit2)(state, first + 1, afterLast);
952 if (afterIpLit2 == NULL) {
953 return NULL;
954 }
955 state->uri->hostText.first = first + 1; /* HOST BEGIN */
956 return URI_FUNC(ParseAuthorityTwo)(state, afterIpLit2, afterLast);
957 }
958
959 default:
960 return URI_FUNC(ParseOwnHost2)(state, first, afterLast);
961 }
962}
963
964
965
966static URI_INLINE UriBool URI_FUNC(OnExitOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
967 state->uri->hostText.afterLast = first; /* HOST END */
968
969 /* Valid IPv4 or just a regname? */
970 state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
971 if (state->uri->hostData.ip4 == NULL) {
972 return URI_FALSE; /* Raises malloc error */
973 }
974 if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
975 state->uri->hostText.first, state->uri->hostText.afterLast)) {
976 /* Not IPv4 */
977 free(state->uri->hostData.ip4);
978 state->uri->hostData.ip4 = NULL;
979 }
980 return URI_TRUE; /* Success */
981}
982
983
984
985/*
986 * [ownHost2]->[authorityTwo] // can take <NULL>
987 * [ownHost2]->[pctSubUnres][ownHost2]
988 */
989static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
990 if (first >= afterLast) {
991 if (!URI_FUNC(OnExitOwnHost2)(state, first)) {
992 URI_FUNC(StopMalloc)(state);
993 return NULL;
994 }
995 return afterLast;
996 }
997
998 switch (*first) {
999 case _UT('!'):
1000 case _UT('$'):
1001 case _UT('%'):
1002 case _UT('&'):
1003 case _UT('('):
1004 case _UT(')'):
1005 case _UT('-'):
1006 case _UT('*'):
1007 case _UT(','):
1008 case _UT('.'):
1009 case _UT(';'):
1010 case _UT('\''):
1011 case _UT('_'):
1012 case _UT('~'):
1013 case _UT('+'):
1014 case _UT('='):
1015 case URI_SET_DIGIT:
1016 case URI_SET_ALPHA:
1017 {
1018 const URI_CHAR * const afterPctSubUnres
1019 = URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
1020 if (afterPctSubUnres == NULL) {
1021 return NULL;
1022 }
1023 return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast);
1024 }
1025
1026 default:
1027 if (!URI_FUNC(OnExitOwnHost2)(state, first)) {
1028 URI_FUNC(StopMalloc)(state);
1029 return NULL;
1030 }
1031 return URI_FUNC(ParseAuthorityTwo)(state, first, afterLast);
1032 }
1033}
1034
1035
1036
1037static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
1038 state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */
1039 state->uri->userInfo.first = NULL; /* Not a userInfo, reset */
1040 state->uri->hostText.afterLast = first; /* HOST END */
1041
1042 /* Valid IPv4 or just a regname? */
1043 state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
1044 if (state->uri->hostData.ip4 == NULL) {
1045 return URI_FALSE; /* Raises malloc error */
1046 }
1047 if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
1048 state->uri->hostText.first, state->uri->hostText.afterLast)) {
1049 /* Not IPv4 */
1050 free(state->uri->hostData.ip4);
1051 state->uri->hostData.ip4 = NULL;
1052 }
1053 return URI_TRUE; /* Success */
1054}
1055
1056
1057
1058/*
1059 * [ownHostUserInfo]->[ownHostUserInfoNz]
1060 * [ownHostUserInfo]-><NULL>
1061 */
1062static URI_INLINE const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1063 if (first >= afterLast) {
1064 if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first)) {
1065 URI_FUNC(StopMalloc)(state);
1066 return NULL;
1067 }
1068 return afterLast;
1069 }
1070
1071 switch (*first) {
1072 case _UT('!'):
1073 case _UT('$'):
1074 case _UT('%'):
1075 case _UT('&'):
1076 case _UT('('):
1077 case _UT(')'):
1078 case _UT('-'):
1079 case _UT('*'):
1080 case _UT(','):
1081 case _UT('.'):
1082 case _UT(':'):
1083 case _UT(';'):
1084 case _UT('@'):
1085 case _UT('\''):
1086 case _UT('_'):
1087 case _UT('~'):
1088 case _UT('+'):
1089 case _UT('='):
1090 case URI_SET_DIGIT:
1091 case URI_SET_ALPHA:
1092 return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast);
1093
1094 default:
1095 if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first)) {
1096 URI_FUNC(StopMalloc)(state);
1097 return NULL;
1098 }
1099 return first;
1100 }
1101}
1102
1103
1104
1105/*
1106 * [ownHostUserInfoNz]->[pctSubUnres][ownHostUserInfo]
1107 * [ownHostUserInfoNz]-><:>[ownPortUserInfo]
1108 * [ownHostUserInfoNz]-><@>[ownHost]
1109 */
1110static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1111 if (first >= afterLast) {
1112 URI_FUNC(StopSyntax)(state, first);
1113 return NULL;
1114 }
1115
1116 switch (*first) {
1117 case _UT('!'):
1118 case _UT('$'):
1119 case _UT('%'):
1120 case _UT('&'):
1121 case _UT('('):
1122 case _UT(')'):
1123 case _UT('-'):
1124 case _UT('*'):
1125 case _UT(','):
1126 case _UT('.'):
1127 case _UT(';'):
1128 case _UT('\''):
1129 case _UT('_'):
1130 case _UT('~'):
1131 case _UT('+'):
1132 case _UT('='):
1133 case URI_SET_DIGIT:
1134 case URI_SET_ALPHA:
1135 {
1136 const URI_CHAR * const afterPctSubUnres
1137 = URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
1138 if (afterPctSubUnres == NULL) {
1139 return NULL;
1140 }
1141 return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast);
1142 }
1143
1144 case _UT(':'):
1145 state->uri->hostText.afterLast = first; /* HOST END */
1146 state->uri->portText.first = first + 1; /* PORT BEGIN */
1147 return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast);
1148
1149 case _UT('@'):
1150 state->uri->userInfo.afterLast = first; /* USERINFO END */
1151 state->uri->hostText.first = first + 1; /* HOST BEGIN */
1152 return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
1153
1154 default:
1155 URI_FUNC(StopSyntax)(state, first);
1156 return NULL;
1157 }
1158}
1159
1160
1161
1162static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
1163 state->uri->hostText.first = state->uri->userInfo.first; /* Host instead of userInfo, update */
1164 state->uri->userInfo.first = NULL; /* Not a userInfo, reset */
1165 state->uri->portText.afterLast = first; /* PORT END */
1166
1167 /* Valid IPv4 or just a regname? */
1168 state->uri->hostData.ip4 = malloc(1 * sizeof(UriIp4)); /* Freed when stopping on parse error */
1169 if (state->uri->hostData.ip4 == NULL) {
1170 return URI_FALSE; /* Raises malloc error */
1171 }
1172 if (URI_FUNC(ParseIpFourAddress)(state->uri->hostData.ip4->data,
1173 state->uri->hostText.first, state->uri->hostText.afterLast)) {
1174 /* Not IPv4 */
1175 free(state->uri->hostData.ip4);
1176 state->uri->hostData.ip4 = NULL;
1177 }
1178 return URI_TRUE; /* Success */
1179}
1180
1181
1182
1183/*
1184 * [ownPortUserInfo]->[ALPHA][ownUserInfo]
1185 * [ownPortUserInfo]->[DIGIT][ownPortUserInfo]
1186 * [ownPortUserInfo]-><.>[ownUserInfo]
1187 * [ownPortUserInfo]-><_>[ownUserInfo]
1188 * [ownPortUserInfo]-><~>[ownUserInfo]
1189 * [ownPortUserInfo]-><->[ownUserInfo]
1190 * [ownPortUserInfo]->[subDelims][ownUserInfo]
1191 * [ownPortUserInfo]->[pctEncoded][ownUserInfo]
1192 * [ownPortUserInfo]-><:>[ownUserInfo]
1193 * [ownPortUserInfo]-><@>[ownHost]
1194 * [ownPortUserInfo]-><NULL>
1195 */
1196static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1197 if (first >= afterLast) {
1198 if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first)) {
1199 URI_FUNC(StopMalloc)(state);
1200 return NULL;
1201 }
1202 return afterLast;
1203 }
1204
1205 switch (*first) {
1206 /* begin sub-delims */
1207 case _UT('!'):
1208 case _UT('$'):
1209 case _UT('&'):
1210 case _UT('\''):
1211 case _UT('('):
1212 case _UT(')'):
1213 case _UT('*'):
1214 case _UT('+'):
1215 case _UT(','):
1216 case _UT(';'):
1217 case _UT('='):
1218 /* end sub-delims */
1219 /* begin unreserved (except alpha and digit) */
1220 case _UT('-'):
1221 case _UT('.'):
1222 case _UT('_'):
1223 case _UT('~'):
1224 /* end unreserved (except alpha and digit) */
1225 case _UT(':'):
1226 case URI_SET_ALPHA:
1227 state->uri->hostText.afterLast = NULL; /* Not a host, reset */
1228 state->uri->portText.first = NULL; /* Not a port, reset */
1229 return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast);
1230
1231 case URI_SET_DIGIT:
1232 return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast);
1233
1234 case _UT('%'):
1235 state->uri->portText.first = NULL; /* Not a port, reset */
1236 {
1237 const URI_CHAR * const afterPct
1238 = URI_FUNC(ParsePctEncoded)(state, first, afterLast);
1239 if (afterPct == NULL) {
1240 return NULL;
1241 }
1242 return URI_FUNC(ParseOwnUserInfo)(state, afterPct, afterLast);
1243 }
1244
1245 case _UT('@'):
1246 state->uri->hostText.afterLast = NULL; /* Not a host, reset */
1247 state->uri->portText.first = NULL; /* Not a port, reset */
1248 state->uri->userInfo.afterLast = first; /* USERINFO END */
1249 state->uri->hostText.first = first + 1; /* HOST BEGIN */
1250 return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
1251
1252 default:
1253 if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first)) {
1254 URI_FUNC(StopMalloc)(state);
1255 return NULL;
1256 }
1257 return first;
1258 }
1259}
1260
1261
1262
1263/*
1264 * [ownUserInfo]->[pctSubUnres][ownUserInfo]
1265 * [ownUserInfo]-><:>[ownUserInfo]
1266 * [ownUserInfo]-><@>[ownHost]
1267 */
1268static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1269 if (first >= afterLast) {
1270 URI_FUNC(StopSyntax)(state, first);
1271 return NULL;
1272 }
1273
1274 switch (*first) {
1275 case _UT('!'):
1276 case _UT('$'):
1277 case _UT('%'):
1278 case _UT('&'):
1279 case _UT('('):
1280 case _UT(')'):
1281 case _UT('-'):
1282 case _UT('*'):
1283 case _UT(','):
1284 case _UT('.'):
1285 case _UT(';'):
1286 case _UT('\''):
1287 case _UT('_'):
1288 case _UT('~'):
1289 case _UT('+'):
1290 case _UT('='):
1291 case URI_SET_DIGIT:
1292 case URI_SET_ALPHA:
1293 {
1294 const URI_CHAR * const afterPctSubUnres
1295 = URI_FUNC(ParsePctSubUnres)(state, first, afterLast);
1296 if (afterPctSubUnres == NULL) {
1297 return NULL;
1298 }
1299 return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast);
1300 }
1301
1302 case _UT(':'):
1303 return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast);
1304
1305 case _UT('@'):
1306 /* SURE */
1307 state->uri->userInfo.afterLast = first; /* USERINFO END */
1308 state->uri->hostText.first = first + 1; /* HOST BEGIN */
1309 return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast);
1310
1311 default:
1312 URI_FUNC(StopSyntax)(state, first);
1313 return NULL;
1314 }
1315}
1316
1317
1318
1319static URI_INLINE void URI_FUNC(OnExitPartHelperTwo)(URI_TYPE(ParserState) * state) {
1320 state->uri->absolutePath = URI_TRUE;
1321}
1322
1323
1324
1325/*
1326 * [partHelperTwo]->[pathAbsNoLeadSlash] // can take <NULL>
1327 * [partHelperTwo]-></>[authority][pathAbsEmpty]
1328 */
1329static URI_INLINE const URI_CHAR * URI_FUNC(ParsePartHelperTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1330 if (first >= afterLast) {
1331 URI_FUNC(OnExitPartHelperTwo)(state);
1332 return afterLast;
1333 }
1334
1335 switch (*first) {
1336 case _UT('/'):
1337 {
1338 const URI_CHAR * const afterAuthority
1339 = URI_FUNC(ParseAuthority)(state, first + 1, afterLast);
1340 const URI_CHAR * afterPathAbsEmpty;
1341 if (afterAuthority == NULL) {
1342 return NULL;
1343 }
1344 afterPathAbsEmpty = URI_FUNC(ParsePathAbsEmpty)(state, afterAuthority, afterLast);
1345
1346 URI_FUNC(FixEmptyTrailSegment)(state->uri);
1347
1348 return afterPathAbsEmpty;
1349 }
1350
1351 default:
1352 URI_FUNC(OnExitPartHelperTwo)(state);
1353 return URI_FUNC(ParsePathAbsNoLeadSlash)(state, first, afterLast);
1354 }
1355}
1356
1357
1358
1359/*
1360 * [pathAbsEmpty]-></>[segment][pathAbsEmpty]
1361 * [pathAbsEmpty]-><NULL>
1362 */
1363static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1364 if (first >= afterLast) {
1365 return afterLast;
1366 }
1367
1368 switch (*first) {
1369 case _UT('/'):
1370 {
1371 const URI_CHAR * const afterSegment
1372 = URI_FUNC(ParseSegment)(state, first + 1, afterLast);
1373 if (afterSegment == NULL) {
1374 return NULL;
1375 }
1376 if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
1377 URI_FUNC(StopMalloc)(state);
1378 return NULL;
1379 }
1380 return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast);
1381 }
1382
1383 default:
1384 return first;
1385 }
1386}
1387
1388
1389
1390/*
1391 * [pathAbsNoLeadSlash]->[segmentNz][zeroMoreSlashSegs]
1392 * [pathAbsNoLeadSlash]-><NULL>
1393 */
1394static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathAbsNoLeadSlash)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1395 if (first >= afterLast) {
1396 return afterLast;
1397 }
1398
1399 switch (*first) {
1400 case _UT('!'):
1401 case _UT('$'):
1402 case _UT('%'):
1403 case _UT('&'):
1404 case _UT('('):
1405 case _UT(')'):
1406 case _UT('-'):
1407 case _UT('*'):
1408 case _UT(','):
1409 case _UT('.'):
1410 case _UT(':'):
1411 case _UT(';'):
1412 case _UT('@'):
1413 case _UT('\''):
1414 case _UT('_'):
1415 case _UT('~'):
1416 case _UT('+'):
1417 case _UT('='):
1418 case URI_SET_DIGIT:
1419 case URI_SET_ALPHA:
1420 {
1421 const URI_CHAR * const afterSegmentNz
1422 = URI_FUNC(ParseSegmentNz)(state, first, afterLast);
1423 if (afterSegmentNz == NULL) {
1424 return NULL;
1425 }
1426 if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz)) { /* SEGMENT BOTH */
1427 URI_FUNC(StopMalloc)(state);
1428 return NULL;
1429 }
1430 return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast);
1431 }
1432
1433 default:
1434 return first;
1435 }
1436}
1437
1438
1439
1440/*
1441 * [pathRootless]->[segmentNz][zeroMoreSlashSegs]
1442 */
1443static URI_INLINE const URI_CHAR * URI_FUNC(ParsePathRootless)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1444 const URI_CHAR * const afterSegmentNz
1445 = URI_FUNC(ParseSegmentNz)(state, first, afterLast);
1446 if (afterSegmentNz == NULL) {
1447 return NULL;
1448 } else {
1449 if (!URI_FUNC(PushPathSegment)(state, first, afterSegmentNz)) { /* SEGMENT BOTH */
1450 URI_FUNC(StopMalloc)(state);
1451 return NULL;
1452 }
1453 }
1454 return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegmentNz, afterLast);
1455}
1456
1457
1458
1459/*
1460 * [pchar]->[pctEncoded]
1461 * [pchar]->[subDelims]
1462 * [pchar]->[unreserved]
1463 * [pchar]-><:>
1464 * [pchar]-><@>
1465 */
1466static const URI_CHAR * URI_FUNC(ParsePchar)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1467 if (first >= afterLast) {
1468 URI_FUNC(StopSyntax)(state, first);
1469 return NULL;
1470 }
1471
1472 switch (*first) {
1473 case _UT('%'):
1474 return URI_FUNC(ParsePctEncoded)(state, first, afterLast);
1475
1476 case _UT(':'):
1477 case _UT('@'):
1478 case _UT('!'):
1479 case _UT('$'):
1480 case _UT('&'):
1481 case _UT('('):
1482 case _UT(')'):
1483 case _UT('*'):
1484 case _UT(','):
1485 case _UT(';'):
1486 case _UT('\''):
1487 case _UT('+'):
1488 case _UT('='):
1489 case _UT('-'):
1490 case _UT('.'):
1491 case _UT('_'):
1492 case _UT('~'):
1493 case URI_SET_DIGIT:
1494 case URI_SET_ALPHA:
1495 return first + 1;
1496
1497 default:
1498 URI_FUNC(StopSyntax)(state, first);
1499 return NULL;
1500 }
1501}
1502
1503
1504
1505/*
1506 * [pctEncoded]-><%>[HEXDIG][HEXDIG]
1507 */
1508static const URI_CHAR * URI_FUNC(ParsePctEncoded)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1509 if (first >= afterLast) {
1510 URI_FUNC(StopSyntax)(state, first);
1511 return NULL;
1512 }
1513
1514 /*
1515 First character has already been
1516 checked before entering this rule.
1517
1518 switch (*first) {
1519 case _UT('%'):
1520 */
1521 if (first + 1 >= afterLast) {
1522 URI_FUNC(StopSyntax)(state, first + 1);
1523 return NULL;
1524 }
1525
1526 switch (first[1]) {
1527 case URI_SET_HEXDIG:
1528 if (first + 2 >= afterLast) {
1529 URI_FUNC(StopSyntax)(state, first + 2);
1530 return NULL;
1531 }
1532
1533 switch (first[2]) {
1534 case URI_SET_HEXDIG:
1535 return first + 3;
1536
1537 default:
1538 URI_FUNC(StopSyntax)(state, first + 2);
1539 return NULL;
1540 }
1541
1542 default:
1543 URI_FUNC(StopSyntax)(state, first + 1);
1544 return NULL;
1545 }
1546
1547 /*
1548 default:
1549 URI_FUNC(StopSyntax)(state, first);
1550 return NULL;
1551 }
1552 */
1553}
1554
1555
1556
1557/*
1558 * [pctSubUnres]->[pctEncoded]
1559 * [pctSubUnres]->[subDelims]
1560 * [pctSubUnres]->[unreserved]
1561 */
1562static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1563 if (first >= afterLast) {
1564 URI_FUNC(StopSyntax)(state, first);
1565 return NULL;
1566 }
1567
1568 switch (*first) {
1569 case _UT('%'):
1570 return URI_FUNC(ParsePctEncoded)(state, first, afterLast);
1571
1572 case _UT('!'):
1573 case _UT('$'):
1574 case _UT('&'):
1575 case _UT('('):
1576 case _UT(')'):
1577 case _UT('*'):
1578 case _UT(','):
1579 case _UT(';'):
1580 case _UT('\''):
1581 case _UT('+'):
1582 case _UT('='):
1583 case _UT('-'):
1584 case _UT('.'):
1585 case _UT('_'):
1586 case _UT('~'):
1587 case URI_SET_DIGIT:
1588 case URI_SET_ALPHA:
1589 return first + 1;
1590
1591 default:
1592 URI_FUNC(StopSyntax)(state, first);
1593 return NULL;
1594 }
1595}
1596
1597
1598
1599/*
1600 * [port]->[DIGIT][port]
1601 * [port]-><NULL>
1602 */
1603static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1604 if (first >= afterLast) {
1605 return afterLast;
1606 }
1607
1608 switch (*first) {
1609 case URI_SET_DIGIT:
1610 return URI_FUNC(ParsePort)(state, first + 1, afterLast);
1611
1612 default:
1613 return first;
1614 }
1615}
1616
1617
1618
1619/*
1620 * [queryFrag]->[pchar][queryFrag]
1621 * [queryFrag]-></>[queryFrag]
1622 * [queryFrag]-><?>[queryFrag]
1623 * [queryFrag]-><NULL>
1624 */
1625static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1626 if (first >= afterLast) {
1627 return afterLast;
1628 }
1629
1630 switch (*first) {
1631 case _UT('!'):
1632 case _UT('$'):
1633 case _UT('%'):
1634 case _UT('&'):
1635 case _UT('('):
1636 case _UT(')'):
1637 case _UT('-'):
1638 case _UT('*'):
1639 case _UT(','):
1640 case _UT('.'):
1641 case _UT(':'):
1642 case _UT(';'):
1643 case _UT('@'):
1644 case _UT('\''):
1645 case _UT('_'):
1646 case _UT('~'):
1647 case _UT('+'):
1648 case _UT('='):
1649 case URI_SET_DIGIT:
1650 case URI_SET_ALPHA:
1651 {
1652 const URI_CHAR * const afterPchar
1653 = URI_FUNC(ParsePchar)(state, first, afterLast);
1654 if (afterPchar == NULL) {
1655 return NULL;
1656 }
1657 return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast);
1658 }
1659
1660 case _UT('/'):
1661 case _UT('?'):
1662 return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
1663
1664 default:
1665 return first;
1666 }
1667}
1668
1669
1670
1671/*
1672 * [segment]->[pchar][segment]
1673 * [segment]-><NULL>
1674 */
1675static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1676 if (first >= afterLast) {
1677 return afterLast;
1678 }
1679
1680 switch (*first) {
1681 case _UT('!'):
1682 case _UT('$'):
1683 case _UT('%'):
1684 case _UT('&'):
1685 case _UT('('):
1686 case _UT(')'):
1687 case _UT('-'):
1688 case _UT('*'):
1689 case _UT(','):
1690 case _UT('.'):
1691 case _UT(':'):
1692 case _UT(';'):
1693 case _UT('@'):
1694 case _UT('\''):
1695 case _UT('_'):
1696 case _UT('~'):
1697 case _UT('+'):
1698 case _UT('='):
1699 case URI_SET_DIGIT:
1700 case URI_SET_ALPHA:
1701 {
1702 const URI_CHAR * const afterPchar
1703 = URI_FUNC(ParsePchar)(state, first, afterLast);
1704 if (afterPchar == NULL) {
1705 return NULL;
1706 }
1707 return URI_FUNC(ParseSegment)(state, afterPchar, afterLast);
1708 }
1709
1710 default:
1711 return first;
1712 }
1713}
1714
1715
1716
1717/*
1718 * [segmentNz]->[pchar][segment]
1719 */
1720static URI_INLINE const URI_CHAR * URI_FUNC(ParseSegmentNz)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1721 const URI_CHAR * const afterPchar
1722 = URI_FUNC(ParsePchar)(state, first, afterLast);
1723 if (afterPchar == NULL) {
1724 return NULL;
1725 }
1726 return URI_FUNC(ParseSegment)(state, afterPchar, afterLast);
1727}
1728
1729
1730
1731static URI_INLINE UriBool URI_FUNC(OnExitSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first) {
1732 if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
1733 return URI_FALSE; /* Raises malloc error*/
1734 }
1735 state->uri->scheme.first = NULL; /* Not a scheme, reset */
1736 return URI_TRUE; /* Success */
1737}
1738
1739
1740
1741/*
1742 * [segmentNzNcOrScheme2]->[ALPHA][segmentNzNcOrScheme2]
1743 * [segmentNzNcOrScheme2]->[DIGIT][segmentNzNcOrScheme2]
1744 * [segmentNzNcOrScheme2]->[pctEncoded][mustBeSegmentNzNc]
1745 * [segmentNzNcOrScheme2]->[uriTail] // can take <NULL>
1746 * [segmentNzNcOrScheme2]-><!>[mustBeSegmentNzNc]
1747 * [segmentNzNcOrScheme2]-><$>[mustBeSegmentNzNc]
1748 * [segmentNzNcOrScheme2]-><&>[mustBeSegmentNzNc]
1749 * [segmentNzNcOrScheme2]-><(>[mustBeSegmentNzNc]
1750 * [segmentNzNcOrScheme2]-><)>[mustBeSegmentNzNc]
1751 * [segmentNzNcOrScheme2]-><*>[mustBeSegmentNzNc]
1752 * [segmentNzNcOrScheme2]-><,>[mustBeSegmentNzNc]
1753 * [segmentNzNcOrScheme2]-><.>[segmentNzNcOrScheme2]
1754 * [segmentNzNcOrScheme2]-></>[segment][zeroMoreSlashSegs][uriTail]
1755 * [segmentNzNcOrScheme2]-><:>[hierPart][uriTail]
1756 * [segmentNzNcOrScheme2]-><;>[mustBeSegmentNzNc]
1757 * [segmentNzNcOrScheme2]-><@>[mustBeSegmentNzNc]
1758 * [segmentNzNcOrScheme2]-><_>[mustBeSegmentNzNc]
1759 * [segmentNzNcOrScheme2]-><~>[mustBeSegmentNzNc]
1760 * [segmentNzNcOrScheme2]-><+>[segmentNzNcOrScheme2]
1761 * [segmentNzNcOrScheme2]-><=>[mustBeSegmentNzNc]
1762 * [segmentNzNcOrScheme2]-><'>[mustBeSegmentNzNc]
1763 * [segmentNzNcOrScheme2]-><->[segmentNzNcOrScheme2]
1764 */
1765static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1766 if (first >= afterLast) {
1767 if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first)) {
1768 URI_FUNC(StopMalloc)(state);
1769 return NULL;
1770 }
1771 return afterLast;
1772 }
1773
1774 switch (*first) {
1775 case _UT('.'):
1776 case _UT('+'):
1777 case _UT('-'):
1778 case URI_SET_ALPHA:
1779 case URI_SET_DIGIT:
1780 return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast);
1781
1782 case _UT('%'):
1783 {
1784 const URI_CHAR * const afterPctEncoded
1785 = URI_FUNC(ParsePctEncoded)(state, first, afterLast);
1786 if (afterPctEncoded == NULL) {
1787 return NULL;
1788 }
1789 return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
1790 }
1791
1792 case _UT('!'):
1793 case _UT('$'):
1794 case _UT('&'):
1795 case _UT('('):
1796 case _UT(')'):
1797 case _UT('*'):
1798 case _UT(','):
1799 case _UT(';'):
1800 case _UT('@'):
1801 case _UT('_'):
1802 case _UT('~'):
1803 case _UT('='):
1804 case _UT('\''):
1805 return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
1806
1807 case _UT('/'):
1808 {
1809 const URI_CHAR * afterZeroMoreSlashSegs;
1810 const URI_CHAR * const afterSegment
1811 = URI_FUNC(ParseSegment)(state, first + 1, afterLast);
1812 if (afterSegment == NULL) {
1813 return NULL;
1814 }
1815 if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first)) { /* SEGMENT BOTH */
1816 URI_FUNC(StopMalloc)(state);
1817 return NULL;
1818 }
1819 state->uri->scheme.first = NULL; /* Not a scheme, reset */
1820 if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
1821 URI_FUNC(StopMalloc)(state);
1822 return NULL;
1823 }
1824 afterZeroMoreSlashSegs
1825 = URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
1826 if (afterZeroMoreSlashSegs == NULL) {
1827 return NULL;
1828 }
1829 return URI_FUNC(ParseUriTail)(state, afterZeroMoreSlashSegs, afterLast);
1830 }
1831
1832 case _UT(':'):
1833 {
1834 const URI_CHAR * const afterHierPart
1835 = URI_FUNC(ParseHierPart)(state, first + 1, afterLast);
1836 state->uri->scheme.afterLast = first; /* SCHEME END */
1837 if (afterHierPart == NULL) {
1838 return NULL;
1839 }
1840 return URI_FUNC(ParseUriTail)(state, afterHierPart, afterLast);
1841 }
1842
1843 default:
1844 if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first)) {
1845 URI_FUNC(StopMalloc)(state);
1846 return NULL;
1847 }
1848 return URI_FUNC(ParseUriTail)(state, first, afterLast);
1849 }
1850}
1851
1852
1853
1854/*
1855 * [uriReference]->[ALPHA][segmentNzNcOrScheme2]
1856 * [uriReference]->[DIGIT][mustBeSegmentNzNc]
1857 * [uriReference]->[pctEncoded][mustBeSegmentNzNc]
1858 * [uriReference]->[subDelims][mustBeSegmentNzNc]
1859 * [uriReference]->[uriTail] // can take <NULL>
1860 * [uriReference]-><.>[mustBeSegmentNzNc]
1861 * [uriReference]-></>[partHelperTwo][uriTail]
1862 * [uriReference]-><@>[mustBeSegmentNzNc]
1863 * [uriReference]-><_>[mustBeSegmentNzNc]
1864 * [uriReference]-><~>[mustBeSegmentNzNc]
1865 * [uriReference]-><->[mustBeSegmentNzNc]
1866 */
1867static const URI_CHAR * URI_FUNC(ParseUriReference)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1868 if (first >= afterLast) {
1869 return afterLast;
1870 }
1871
1872 switch (*first) {
1873 case URI_SET_ALPHA:
1874 state->uri->scheme.first = first; /* SCHEME BEGIN */
1875 return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast);
1876
1877 case URI_SET_DIGIT:
1878 case _UT('!'):
1879 case _UT('$'):
1880 case _UT('&'):
1881 case _UT('('):
1882 case _UT(')'):
1883 case _UT('*'):
1884 case _UT(','):
1885 case _UT(';'):
1886 case _UT('\''):
1887 case _UT('+'):
1888 case _UT('='):
1889 case _UT('.'):
1890 case _UT('_'):
1891 case _UT('~'):
1892 case _UT('-'):
1893 case _UT('@'):
1894 state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */
1895 return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast);
1896
1897 case _UT('%'):
1898 {
1899 const URI_CHAR * const afterPctEncoded
1900 = URI_FUNC(ParsePctEncoded)(state, first, afterLast);
1901 if (afterPctEncoded == NULL) {
1902 return NULL;
1903 }
1904 state->uri->scheme.first = first; /* SEGMENT BEGIN, ABUSE SCHEME POINTER */
1905 return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast);
1906 }
1907
1908 case _UT('/'):
1909 {
1910 const URI_CHAR * const afterPartHelperTwo
1911 = URI_FUNC(ParsePartHelperTwo)(state, first + 1, afterLast);
1912 if (afterPartHelperTwo == NULL) {
1913 return NULL;
1914 }
1915 return URI_FUNC(ParseUriTail)(state, afterPartHelperTwo, afterLast);
1916 }
1917
1918 default:
1919 return URI_FUNC(ParseUriTail)(state, first, afterLast);
1920 }
1921}
1922
1923
1924
1925/*
1926 * [uriTail]-><#>[queryFrag]
1927 * [uriTail]-><?>[queryFrag][uriTailTwo]
1928 * [uriTail]-><NULL>
1929 */
1930static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTail)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1931 if (first >= afterLast) {
1932 return afterLast;
1933 }
1934
1935 switch (*first) {
1936 case _UT('#'):
1937 {
1938 const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
1939 if (afterQueryFrag == NULL) {
1940 return NULL;
1941 }
1942 state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */
1943 state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */
1944 return afterQueryFrag;
1945 }
1946
1947 case _UT('?'):
1948 {
1949 const URI_CHAR * const afterQueryFrag
1950 = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
1951 if (afterQueryFrag == NULL) {
1952 return NULL;
1953 }
1954 state->uri->query.first = first + 1; /* QUERY BEGIN */
1955 state->uri->query.afterLast = afterQueryFrag; /* QUERY END */
1956 return URI_FUNC(ParseUriTailTwo)(state, afterQueryFrag, afterLast);
1957 }
1958
1959 default:
1960 return first;
1961 }
1962}
1963
1964
1965
1966/*
1967 * [uriTailTwo]-><#>[queryFrag]
1968 * [uriTailTwo]-><NULL>
1969 */
1970static URI_INLINE const URI_CHAR * URI_FUNC(ParseUriTailTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1971 if (first >= afterLast) {
1972 return afterLast;
1973 }
1974
1975 switch (*first) {
1976 case _UT('#'):
1977 {
1978 const URI_CHAR * const afterQueryFrag = URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast);
1979 if (afterQueryFrag == NULL) {
1980 return NULL;
1981 }
1982 state->uri->fragment.first = first + 1; /* FRAGMENT BEGIN */
1983 state->uri->fragment.afterLast = afterQueryFrag; /* FRAGMENT END */
1984 return afterQueryFrag;
1985 }
1986
1987 default:
1988 return first;
1989 }
1990}
1991
1992
1993
1994/*
1995 * [zeroMoreSlashSegs]-></>[segment][zeroMoreSlashSegs]
1996 * [zeroMoreSlashSegs]-><NULL>
1997 */
1998static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
1999 if (first >= afterLast) {
2000 return afterLast;
2001 }
2002
2003 switch (*first) {
2004 case _UT('/'):
2005 {
2006 const URI_CHAR * const afterSegment
2007 = URI_FUNC(ParseSegment)(state, first + 1, afterLast);
2008 if (afterSegment == NULL) {
2009 return NULL;
2010 }
2011 if (!URI_FUNC(PushPathSegment)(state, first + 1, afterSegment)) { /* SEGMENT BOTH */
2012 URI_FUNC(StopMalloc)(state);
2013 return NULL;
2014 }
2015 return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast);
2016 }
2017
2018 default:
2019 return first;
2020 }
2021}
2022
2023
2024
2025static URI_INLINE void URI_FUNC(ResetParserState)(URI_TYPE(ParserState) * state) {
2026 URI_TYPE(Uri) * const uriBackup = state->uri;
2027 memset(state, 0, sizeof(URI_TYPE(ParserState)));
2028 state->uri = uriBackup;
2029}
2030
2031
2032
2033static URI_INLINE UriBool URI_FUNC(PushPathSegment)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
2034 URI_TYPE(PathSegment) * segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
2035 if (segment == NULL) {
2036 return URI_FALSE; /* Raises malloc error */
2037 }
2038 memset(segment, 0, sizeof(URI_TYPE(PathSegment)));
2039 if (first == afterLast) {
2040 segment->text.first = URI_FUNC(SafeToPointTo);
2041 segment->text.afterLast = URI_FUNC(SafeToPointTo);
2042 } else {
2043 segment->text.first = first;
2044 segment->text.afterLast = afterLast;
2045 }
2046
2047 /* First segment ever? */
2048 if (state->uri->pathHead == NULL) {
2049 /* First segment ever, set head and tail */
2050 state->uri->pathHead = segment;
2051 state->uri->pathTail = segment;
2052 } else {
2053 /* Append, update tail */
2054 state->uri->pathTail->next = segment;
2055 state->uri->pathTail = segment;
2056 }
2057
2058 return URI_TRUE; /* Success */
2059}
2060
2061
2062
2063int URI_FUNC(ParseUriEx)(URI_TYPE(ParserState) * state, const URI_CHAR * first, const URI_CHAR * afterLast) {
2064 const URI_CHAR * afterUriReference;
2065 URI_TYPE(Uri) * uri;
2066
2067 /* Check params */
2068 if ((state == NULL) || (first == NULL) || (afterLast == NULL)) {
2069 return URI_ERROR_NULL;
2070 }
2071 uri = state->uri;
2072
2073 /* Init parser */
2074 URI_FUNC(ResetParserState)(state);
2075 URI_FUNC(ResetUri)(uri);
2076
2077 /* Parse */
2078 afterUriReference = URI_FUNC(ParseUriReference)(state, first, afterLast);
2079 if (afterUriReference == NULL) {
2080 return state->errorCode;
2081 }
2082 if (afterUriReference != afterLast) {
2083 URI_FUNC(StopSyntax)(state, afterUriReference);
2084 return state->errorCode;
2085 }
2086 return URI_SUCCESS;
2087}
2088
2089
2090
2091int URI_FUNC(ParseUri)(URI_TYPE(ParserState) * state, const URI_CHAR * text) {
2092 if ((state == NULL) || (text == NULL)) {
2093 return URI_ERROR_NULL;
2094 }
2095 return URI_FUNC(ParseUriEx)(state, text, text + URI_STRLEN(text));
2096}
2097
2098
2099
2100void URI_FUNC(FreeUriMembers)(URI_TYPE(Uri) * uri) {
2101 if (uri == NULL) {
2102 return;
2103 }
2104
2105 if (uri->owner) {
2106 /* Scheme */
2107 if (uri->scheme.first != NULL) {
2108 if (uri->scheme.first != uri->scheme.afterLast) {
2109 free((URI_CHAR *)uri->scheme.first);
2110 }
2111 uri->scheme.first = NULL;
2112 uri->scheme.afterLast = NULL;
2113 }
2114
2115 /* User info */
2116 if (uri->userInfo.first != NULL) {
2117 if (uri->userInfo.first != uri->userInfo.afterLast) {
2118 free((URI_CHAR *)uri->userInfo.first);
2119 }
2120 uri->userInfo.first = NULL;
2121 uri->userInfo.afterLast = NULL;
2122 }
2123
2124 /* Host data - IPvFuture */
2125 if (uri->hostData.ipFuture.first != NULL) {
2126 if (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast) {
2127 free((URI_CHAR *)uri->hostData.ipFuture.first);
2128 }
2129 uri->hostData.ipFuture.first = NULL;
2130 uri->hostData.ipFuture.afterLast = NULL;
2131 uri->hostText.first = NULL;
2132 uri->hostText.afterLast = NULL;
2133 }
2134
2135 /* Host text (if regname, after IPvFuture!) */
2136 if ((uri->hostText.first != NULL)
2137 && (uri->hostData.ip4 == NULL)
2138 && (uri->hostData.ip6 == NULL)) {
2139 /* Real regname */
2140 if (uri->hostText.first != uri->hostText.afterLast) {
2141 free((URI_CHAR *)uri->hostText.first);
2142 }
2143 uri->hostText.first = NULL;
2144 uri->hostText.afterLast = NULL;
2145 }
2146 }
2147
2148 /* Host data - IPv4 */
2149 if (uri->hostData.ip4 != NULL) {
2150 free(uri->hostData.ip4);
2151 uri->hostData.ip4 = NULL;
2152 }
2153
2154 /* Host data - IPv6 */
2155 if (uri->hostData.ip6 != NULL) {
2156 free(uri->hostData.ip6);
2157 uri->hostData.ip6 = NULL;
2158 }
2159
2160 /* Port text */
2161 if (uri->owner && (uri->portText.first != NULL)) {
2162 if (uri->portText.first != uri->portText.afterLast) {
2163 free((URI_CHAR *)uri->portText.first);
2164 }
2165 uri->portText.first = NULL;
2166 uri->portText.afterLast = NULL;
2167 }
2168
2169 /* Path */
2170 if (uri->pathHead != NULL) {
2171 URI_TYPE(PathSegment) * segWalk = uri->pathHead;
2172 while (segWalk != NULL) {
2173 URI_TYPE(PathSegment) * const next = segWalk->next;
2174 if (uri->owner && (segWalk->text.first != NULL)
2175 && (segWalk->text.first < segWalk->text.afterLast)) {
2176 free((URI_CHAR *)segWalk->text.first);
2177 }
2178 free(segWalk);
2179 segWalk = next;
2180 }
2181 uri->pathHead = NULL;
2182 uri->pathTail = NULL;
2183 }
2184
2185 if (uri->owner) {
2186 /* Query */
2187 if (uri->query.first != NULL) {
2188 if (uri->query.first != uri->query.afterLast) {
2189 free((URI_CHAR *)uri->query.first);
2190 }
2191 uri->query.first = NULL;
2192 uri->query.afterLast = NULL;
2193 }
2194
2195 /* Fragment */
2196 if (uri->fragment.first != NULL) {
2197 if (uri->fragment.first != uri->fragment.afterLast) {
2198 free((URI_CHAR *)uri->fragment.first);
2199 }
2200 uri->fragment.first = NULL;
2201 uri->fragment.afterLast = NULL;
2202 }
2203 }
2204}
2205
2206
2207
2208UriBool URI_FUNC(_TESTING_ONLY_ParseIpSix)(const URI_CHAR * text) {
2209 URI_TYPE(Uri) uri;
2210 URI_TYPE(ParserState) parser;
2211 const URI_CHAR * const afterIpSix = text + URI_STRLEN(text);
2212 const URI_CHAR * res;
2213
2214 URI_FUNC(ResetParserState)(&parser);
2215 URI_FUNC(ResetUri)(&uri);
2216 parser.uri = &uri;
2217 parser.uri->hostData.ip6 = malloc(1 * sizeof(UriIp6));
2218 res = URI_FUNC(ParseIPv6address2)(&parser, text, afterIpSix);
2219 URI_FUNC(FreeUriMembers)(&uri);
2220 return res == afterIpSix ? URI_TRUE : URI_FALSE;
2221}
2222
2223
2224
2225UriBool URI_FUNC(_TESTING_ONLY_ParseIpFour)(const URI_CHAR * text) {
2226 unsigned char octets[4];
2227 int res = URI_FUNC(ParseIpFourAddress)(octets, text, text + URI_STRLEN(text));
2228 return (res == URI_SUCCESS) ? URI_TRUE : URI_FALSE;
2229}
2230
2231
2232
2233#undef URI_SET_DIGIT
2234#undef URI_SET_HEX_LETTER_UPPER
2235#undef URI_SET_HEX_LETTER_LOWER
2236#undef URI_SET_HEXDIG
2237#undef URI_SET_ALPHA
2238
2239
2240
2241#endif
diff --git a/plugins/uriparser/UriParseBase.c b/plugins/uriparser/UriParseBase.c
new file mode 100644
index 00000000..75e47613
--- /dev/null
+++ b/plugins/uriparser/UriParseBase.c
@@ -0,0 +1,90 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef URI_DOXYGEN
41# include "UriParseBase.h"
42#endif
43
44
45
46void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount, unsigned char * output) {
47 switch (digitCount) {
48 case 1:
49 /* 0x___? -> \x00 \x0? */
50 output[0] = 0;
51 output[1] = hexDigits[0];
52 break;
53
54 case 2:
55 /* 0x__?? -> \0xx \x?? */
56 output[0] = 0;
57 output[1] = 16 * hexDigits[0] + hexDigits[1];
58 break;
59
60 case 3:
61 /* 0x_??? -> \0x? \x?? */
62 output[0] = hexDigits[0];
63 output[1] = 16 * hexDigits[1] + hexDigits[2];
64 break;
65
66 case 4:
67 /* 0x???? -> \0?? \x?? */
68 output[0] = 16 * hexDigits[0] + hexDigits[1];
69 output[1] = 16 * hexDigits[2] + hexDigits[3];
70 break;
71
72 }
73}
74
75
76
77unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount) {
78 switch (digitCount) {
79 case 1:
80 return digits[0];
81
82 case 2:
83 return 10 * digits[0] + digits[1];
84
85 case 3:
86 default:
87 return 100 * digits[0] + 10 * digits[1] + digits[2];
88
89 }
90}
diff --git a/plugins/uriparser/UriParseBase.h b/plugins/uriparser/UriParseBase.h
new file mode 100644
index 00000000..dea5c81c
--- /dev/null
+++ b/plugins/uriparser/UriParseBase.h
@@ -0,0 +1,55 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#ifndef URI_PARSE_BASE_H
41#define URI_PARSE_BASE_H 1
42
43
44
45#include <uriparser/UriBase.h>
46
47
48
49void uriWriteQuadToDoubleByte(const unsigned char * hexDigits, int digitCount,
50 unsigned char * output);
51unsigned char uriGetOctetValue(const unsigned char * digits, int digitCount);
52
53
54
55#endif /* URI_PARSE_BASE_H */
diff --git a/plugins/uriparser/UriQuery.c b/plugins/uriparser/UriQuery.c
new file mode 100644
index 00000000..7adb0736
--- /dev/null
+++ b/plugins/uriparser/UriQuery.c
@@ -0,0 +1,460 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriQuery.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriQuery.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71static int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest,
72 const URI_TYPE(QueryList) * queryList,
73 int maxChars, int * charsWritten, int * charsRequired,
74 UriBool spaceToPlus, UriBool normalizeBreaks);
75
76static UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext,
77 int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter,
78 const URI_CHAR * valueFirst, const URI_CHAR * valueAfter,
79 UriBool plusToSpace, UriBreakConversion breakConversion);
80
81
82
83int URI_FUNC(ComposeQueryCharsRequired)(const URI_TYPE(QueryList) * queryList,
84 int * charsRequired) {
85 const UriBool spaceToPlus = URI_TRUE;
86 const UriBool normalizeBreaks = URI_TRUE;
87
88 return URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, charsRequired,
89 spaceToPlus, normalizeBreaks);
90}
91
92
93
94int URI_FUNC(ComposeQueryCharsRequiredEx)(const URI_TYPE(QueryList) * queryList,
95 int * charsRequired, UriBool spaceToPlus, UriBool normalizeBreaks) {
96 if ((queryList == NULL) || (charsRequired == NULL)) {
97 return URI_ERROR_NULL;
98 }
99
100 return URI_FUNC(ComposeQueryEngine)(NULL, queryList, 0, NULL,
101 charsRequired, spaceToPlus, normalizeBreaks);
102}
103
104
105
106int URI_FUNC(ComposeQuery)(URI_CHAR * dest,
107 const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten) {
108 const UriBool spaceToPlus = URI_TRUE;
109 const UriBool normalizeBreaks = URI_TRUE;
110
111 return URI_FUNC(ComposeQueryEx)(dest, queryList, maxChars, charsWritten,
112 spaceToPlus, normalizeBreaks);
113}
114
115
116
117int URI_FUNC(ComposeQueryEx)(URI_CHAR * dest,
118 const URI_TYPE(QueryList) * queryList, int maxChars, int * charsWritten,
119 UriBool spaceToPlus, UriBool normalizeBreaks) {
120 if ((dest == NULL) || (queryList == NULL)) {
121 return URI_ERROR_NULL;
122 }
123
124 if (maxChars < 1) {
125 return URI_ERROR_OUTPUT_TOO_LARGE;
126 }
127
128 return URI_FUNC(ComposeQueryEngine)(dest, queryList, maxChars,
129 charsWritten, NULL, spaceToPlus, normalizeBreaks);
130}
131
132
133
134int URI_FUNC(ComposeQueryMalloc)(URI_CHAR ** dest,
135 const URI_TYPE(QueryList) * queryList) {
136 const UriBool spaceToPlus = URI_TRUE;
137 const UriBool normalizeBreaks = URI_TRUE;
138
139 return URI_FUNC(ComposeQueryMallocEx)(dest, queryList,
140 spaceToPlus, normalizeBreaks);
141}
142
143
144
145int URI_FUNC(ComposeQueryMallocEx)(URI_CHAR ** dest,
146 const URI_TYPE(QueryList) * queryList,
147 UriBool spaceToPlus, UriBool normalizeBreaks) {
148 int charsRequired;
149 int res;
150 URI_CHAR * queryString;
151
152 if (dest == NULL) {
153 return URI_ERROR_NULL;
154 }
155
156 /* Calculate space */
157 res = URI_FUNC(ComposeQueryCharsRequiredEx)(queryList, &charsRequired,
158 spaceToPlus, normalizeBreaks);
159 if (res != URI_SUCCESS) {
160 return res;
161 }
162 charsRequired++;
163
164 /* Allocate space */
165 queryString = malloc(charsRequired * sizeof(URI_CHAR));
166 if (queryString == NULL) {
167 return URI_ERROR_MALLOC;
168 }
169
170 /* Put query in */
171 res = URI_FUNC(ComposeQueryEx)(queryString, queryList, charsRequired,
172 NULL, spaceToPlus, normalizeBreaks);
173 if (res != URI_SUCCESS) {
174 free(queryString);
175 return res;
176 }
177
178 *dest = queryString;
179 return URI_SUCCESS;
180}
181
182
183
184int URI_FUNC(ComposeQueryEngine)(URI_CHAR * dest,
185 const URI_TYPE(QueryList) * queryList,
186 int maxChars, int * charsWritten, int * charsRequired,
187 UriBool spaceToPlus, UriBool normalizeBreaks) {
188 UriBool firstItem = URI_TRUE;
189 int ampersandLen = 0;
190 URI_CHAR * write = dest;
191
192 /* Subtract terminator */
193 if (dest == NULL) {
194 *charsRequired = 0;
195 } else {
196 maxChars--;
197 }
198
199 while (queryList != NULL) {
200 const URI_CHAR * const key = queryList->key;
201 const URI_CHAR * const value = queryList->value;
202 const int worstCase = (normalizeBreaks == URI_TRUE ? 6 : 3);
203 const int keyLen = (key == NULL) ? 0 : (int)URI_STRLEN(key);
204 const int keyRequiredChars = worstCase * keyLen;
205 const int valueLen = (value == NULL) ? 0 : (int)URI_STRLEN(value);
206 const int valueRequiredChars = worstCase * valueLen;
207
208 if (dest == NULL) {
209 if (firstItem == URI_TRUE) {
210 ampersandLen = 1;
211 firstItem = URI_FALSE;
212 }
213
214 (*charsRequired) += ampersandLen + keyRequiredChars + ((value == NULL)
215 ? 0
216 : 1 + valueRequiredChars);
217 } else {
218 URI_CHAR * afterKey;
219
220 if ((write - dest) + ampersandLen + keyRequiredChars > maxChars) {
221 return URI_ERROR_OUTPUT_TOO_LARGE;
222 }
223
224 /* Copy key */
225 if (firstItem == URI_TRUE) {
226 firstItem = URI_FALSE;
227 } else {
228 write[0] = _UT('&');
229 write++;
230 }
231 afterKey = URI_FUNC(EscapeEx)(key, key + keyLen,
232 write, spaceToPlus, normalizeBreaks);
233 write += (afterKey - write);
234
235 if (value != NULL) {
236 URI_CHAR * afterValue;
237
238 if ((write - dest) + 1 + valueRequiredChars > maxChars) {
239 return URI_ERROR_OUTPUT_TOO_LARGE;
240 }
241
242 /* Copy value */
243 write[0] = _UT('=');
244 write++;
245 afterValue = URI_FUNC(EscapeEx)(value, value + valueLen,
246 write, spaceToPlus, normalizeBreaks);
247 write += (afterValue - write);
248 }
249 }
250
251 queryList = queryList->next;
252 }
253
254 if (dest != NULL) {
255 write[0] = _UT('\0');
256 if (charsWritten != NULL) {
257 *charsWritten = (int)(write - dest) + 1; /* .. for terminator */
258 }
259 }
260
261 return URI_SUCCESS;
262}
263
264
265
266UriBool URI_FUNC(AppendQueryItem)(URI_TYPE(QueryList) ** prevNext,
267 int * itemCount, const URI_CHAR * keyFirst, const URI_CHAR * keyAfter,
268 const URI_CHAR * valueFirst, const URI_CHAR * valueAfter,
269 UriBool plusToSpace, UriBreakConversion breakConversion) {
270 const int keyLen = (int)(keyAfter - keyFirst);
271 const int valueLen = (int)(valueAfter - valueFirst);
272 URI_CHAR * key;
273 URI_CHAR * value;
274
275 if ((prevNext == NULL) || (itemCount == NULL)
276 || (keyFirst == NULL) || (keyAfter == NULL)
277 || (keyFirst > keyAfter) || (valueFirst > valueAfter)
278 || ((keyFirst == keyAfter)
279 && (valueFirst == NULL) && (valueAfter == NULL))) {
280 return URI_TRUE;
281 }
282
283 /* Append new empty item */
284 *prevNext = malloc(1 * sizeof(URI_TYPE(QueryList)));
285 if (*prevNext == NULL) {
286 return URI_FALSE; /* Raises malloc error */
287 }
288 (*prevNext)->next = NULL;
289
290
291 /* Fill key */
292 key = malloc((keyLen + 1) * sizeof(URI_CHAR));
293 if (key == NULL) {
294 free(*prevNext);
295 *prevNext = NULL;
296 return URI_FALSE; /* Raises malloc error */
297 }
298
299 key[keyLen] = _UT('\0');
300 if (keyLen > 0) {
301 /* Copy 1:1 */
302 memcpy(key, keyFirst, keyLen * sizeof(URI_CHAR));
303
304 /* Unescape */
305 URI_FUNC(UnescapeInPlaceEx)(key, plusToSpace, breakConversion);
306 }
307 (*prevNext)->key = key;
308
309
310 /* Fill value */
311 if (valueFirst != NULL) {
312 value = malloc((valueLen + 1) * sizeof(URI_CHAR));
313 if (value == NULL) {
314 free(key);
315 free(*prevNext);
316 *prevNext = NULL;
317 return URI_FALSE; /* Raises malloc error */
318 }
319
320 value[valueLen] = _UT('\0');
321 if (valueLen > 0) {
322 /* Copy 1:1 */
323 memcpy(value, valueFirst, valueLen * sizeof(URI_CHAR));
324
325 /* Unescape */
326 URI_FUNC(UnescapeInPlaceEx)(value, plusToSpace, breakConversion);
327 }
328 (*prevNext)->value = value;
329 } else {
330 value = NULL;
331 }
332 (*prevNext)->value = value;
333
334 (*itemCount)++;
335 return URI_TRUE;
336}
337
338
339
340void URI_FUNC(FreeQueryList)(URI_TYPE(QueryList) * queryList) {
341 while (queryList != NULL) {
342 URI_TYPE(QueryList) * nextBackup = queryList->next;
343 free((URI_CHAR *)queryList->key); /* const cast */
344 free((URI_CHAR *)queryList->value); /* const cast */
345 free(queryList);
346 queryList = nextBackup;
347 }
348}
349
350
351
352int URI_FUNC(DissectQueryMalloc)(URI_TYPE(QueryList) ** dest, int * itemCount,
353 const URI_CHAR * first, const URI_CHAR * afterLast) {
354 const UriBool plusToSpace = URI_TRUE;
355 const UriBreakConversion breakConversion = URI_BR_DONT_TOUCH;
356
357 return URI_FUNC(DissectQueryMallocEx)(dest, itemCount, first, afterLast,
358 plusToSpace, breakConversion);
359}
360
361
362
363int URI_FUNC(DissectQueryMallocEx)(URI_TYPE(QueryList) ** dest, int * itemCount,
364 const URI_CHAR * first, const URI_CHAR * afterLast,
365 UriBool plusToSpace, UriBreakConversion breakConversion) {
366 const URI_CHAR * walk = first;
367 const URI_CHAR * keyFirst = first;
368 const URI_CHAR * keyAfter = NULL;
369 const URI_CHAR * valueFirst = NULL;
370 const URI_CHAR * valueAfter = NULL;
371 URI_TYPE(QueryList) ** prevNext = dest;
372 int nullCounter;
373 int * itemsAppended = (itemCount == NULL) ? &nullCounter : itemCount;
374
375 if ((dest == NULL) || (first == NULL) || (afterLast == NULL)) {
376 return URI_ERROR_NULL;
377 }
378
379 if (first > afterLast) {
380 return URI_ERROR_RANGE_INVALID;
381 }
382
383 *dest = NULL;
384 *itemsAppended = 0;
385
386 /* Parse query string */
387 for (; walk < afterLast; walk++) {
388 switch (*walk) {
389 case _UT('&'):
390 if (valueFirst != NULL) {
391 valueAfter = walk;
392 } else {
393 keyAfter = walk;
394 }
395
396 if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended,
397 keyFirst, keyAfter, valueFirst, valueAfter,
398 plusToSpace, breakConversion)
399 == URI_FALSE) {
400 /* Free list we built */
401 *itemsAppended = 0;
402 URI_FUNC(FreeQueryList)(*dest);
403 return URI_ERROR_MALLOC;
404 }
405
406 /* Make future items children of the current */
407 if ((prevNext != NULL) && (*prevNext != NULL)) {
408 prevNext = &((*prevNext)->next);
409 }
410
411 if (walk + 1 < afterLast) {
412 keyFirst = walk + 1;
413 } else {
414 keyFirst = NULL;
415 }
416 keyAfter = NULL;
417 valueFirst = NULL;
418 valueAfter = NULL;
419 break;
420
421 case _UT('='):
422 /* NOTE: WE treat the first '=' as a separator, */
423 /* all following go into the value part */
424 if (keyAfter == NULL) {
425 keyAfter = walk;
426 if (walk + 1 <= afterLast) {
427 valueFirst = walk + 1;
428 valueAfter = walk + 1;
429 }
430 }
431 break;
432
433 default:
434 break;
435 }
436 }
437
438 if (valueFirst != NULL) {
439 /* Must be key/value pair */
440 valueAfter = walk;
441 } else {
442 /* Must be key only */
443 keyAfter = walk;
444 }
445
446 if (URI_FUNC(AppendQueryItem)(prevNext, itemsAppended, keyFirst, keyAfter,
447 valueFirst, valueAfter, plusToSpace, breakConversion)
448 == URI_FALSE) {
449 /* Free list we built */
450 *itemsAppended = 0;
451 URI_FUNC(FreeQueryList)(*dest);
452 return URI_ERROR_MALLOC;
453 }
454
455 return URI_SUCCESS;
456}
457
458
459
460#endif
diff --git a/plugins/uriparser/UriRecompose.c b/plugins/uriparser/UriRecompose.c
new file mode 100644
index 00000000..09daee07
--- /dev/null
+++ b/plugins/uriparser/UriRecompose.c
@@ -0,0 +1,577 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriRecompose.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriRecompose.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71static int URI_FUNC(ToStringEngine)(URI_CHAR * dest, const URI_TYPE(Uri) * uri,
72 int maxChars, int * charsWritten, int * charsRequired);
73
74
75
76int URI_FUNC(ToStringCharsRequired)(const URI_TYPE(Uri) * uri,
77 int * charsRequired) {
78 const int MAX_CHARS = ((unsigned int)-1) >> 1;
79 return URI_FUNC(ToStringEngine)(NULL, uri, MAX_CHARS, NULL, charsRequired);
80}
81
82
83
84int URI_FUNC(ToString)(URI_CHAR * dest, const URI_TYPE(Uri) * uri,
85 int maxChars, int * charsWritten) {
86 return URI_FUNC(ToStringEngine)(dest, uri, maxChars, charsWritten, NULL);
87}
88
89
90
91static URI_INLINE int URI_FUNC(ToStringEngine)(URI_CHAR * dest,
92 const URI_TYPE(Uri) * uri, int maxChars, int * charsWritten,
93 int * charsRequired) {
94 int written = 0;
95 if ((uri == NULL) || ((dest == NULL) && (charsRequired == NULL))) {
96 if (charsWritten != NULL) {
97 *charsWritten = 0;
98 }
99 return URI_ERROR_NULL;
100 }
101
102 if (maxChars < 1) {
103 if (charsWritten != NULL) {
104 *charsWritten = 0;
105 }
106 return URI_ERROR_TOSTRING_TOO_LONG;
107 }
108 maxChars--; /* So we don't have to substract 1 for '\0' all the time */
109
110 /* [01/19] result = "" */
111 if (dest != NULL) {
112 dest[0] = _UT('\0');
113 } else {
114 (*charsRequired) = 0;
115 }
116 /* [02/19] if defined(scheme) then */
117 if (uri->scheme.first != NULL) {
118 /* [03/19] append scheme to result; */
119 const int charsToWrite
120 = (int)(uri->scheme.afterLast - uri->scheme.first);
121 if (dest != NULL) {
122 if (written + charsToWrite <= maxChars) {
123 memcpy(dest + written, uri->scheme.first,
124 charsToWrite * sizeof(URI_CHAR));
125 written += charsToWrite;
126 } else {
127 dest[0] = _UT('\0');
128 if (charsWritten != NULL) {
129 *charsWritten = 0;
130 }
131 return URI_ERROR_TOSTRING_TOO_LONG;
132 }
133 } else {
134 (*charsRequired) += charsToWrite;
135 }
136 /* [04/19] append ":" to result; */
137 if (dest != NULL) {
138 if (written + 1 <= maxChars) {
139 memcpy(dest + written, _UT(":"),
140 1 * sizeof(URI_CHAR));
141 written += 1;
142 } else {
143 dest[0] = _UT('\0');
144 if (charsWritten != NULL) {
145 *charsWritten = 0;
146 }
147 return URI_ERROR_TOSTRING_TOO_LONG;
148 }
149 } else {
150 (*charsRequired) += 1;
151 }
152 /* [05/19] endif; */
153 }
154 /* [06/19] if defined(authority) then */
155 if (URI_FUNC(IsHostSet)(uri)) {
156 /* [07/19] append "//" to result; */
157 if (dest != NULL) {
158 if (written + 2 <= maxChars) {
159 memcpy(dest + written, _UT("//"),
160 2 * sizeof(URI_CHAR));
161 written += 2;
162 } else {
163 dest[0] = _UT('\0');
164 if (charsWritten != NULL) {
165 *charsWritten = 0;
166 }
167 return URI_ERROR_TOSTRING_TOO_LONG;
168 }
169 } else {
170 (*charsRequired) += 2;
171 }
172 /* [08/19] append authority to result; */
173 /* UserInfo */
174 if (uri->userInfo.first != NULL) {
175 const int charsToWrite = (int)(uri->userInfo.afterLast - uri->userInfo.first);
176 if (dest != NULL) {
177 if (written + charsToWrite <= maxChars) {
178 memcpy(dest + written, uri->userInfo.first,
179 charsToWrite * sizeof(URI_CHAR));
180 written += charsToWrite;
181 } else {
182 dest[0] = _UT('\0');
183 if (charsWritten != NULL) {
184 *charsWritten = 0;
185 }
186 return URI_ERROR_TOSTRING_TOO_LONG;
187 }
188
189 if (written + 1 <= maxChars) {
190 memcpy(dest + written, _UT("@"),
191 1 * sizeof(URI_CHAR));
192 written += 1;
193 } else {
194 dest[0] = _UT('\0');
195 if (charsWritten != NULL) {
196 *charsWritten = 0;
197 }
198 return URI_ERROR_TOSTRING_TOO_LONG;
199 }
200 } else {
201 (*charsRequired) += charsToWrite + 1;
202 }
203 }
204
205 /* Host */
206 if (uri->hostData.ip4 != NULL) {
207 /* IPv4 */
208 int i = 0;
209 for (; i < 4; i++) {
210 const unsigned char value = uri->hostData.ip4->data[i];
211 const int charsToWrite = (value > 99) ? 3 : ((value > 9) ? 2 : 1);
212 if (dest != NULL) {
213 if (written + charsToWrite <= maxChars) {
214 URI_CHAR text[4];
215 if (value > 99) {
216 text[0] = _UT('0') + (value / 100);
217 text[1] = _UT('0') + ((value % 100) / 10);
218 text[2] = _UT('0') + (value % 10);
219 } else if (value > 9) {
220 text[0] = _UT('0') + (value / 10);
221 text[1] = _UT('0') + (value % 10);
222 } else {
223 text[0] = _UT('0') + value;
224 }
225 text[charsToWrite] = _UT('\0');
226 memcpy(dest + written, text, charsToWrite * sizeof(URI_CHAR));
227 written += charsToWrite;
228 } else {
229 dest[0] = _UT('\0');
230 if (charsWritten != NULL) {
231 *charsWritten = 0;
232 }
233 return URI_ERROR_TOSTRING_TOO_LONG;
234 }
235 if (i < 3) {
236 if (written + 1 <= maxChars) {
237 memcpy(dest + written, _UT("."),
238 1 * sizeof(URI_CHAR));
239 written += 1;
240 } else {
241 dest[0] = _UT('\0');
242 if (charsWritten != NULL) {
243 *charsWritten = 0;
244 }
245 return URI_ERROR_TOSTRING_TOO_LONG;
246 }
247 }
248 } else {
249 (*charsRequired) += charsToWrite + 1;
250 }
251 }
252 } else if (uri->hostData.ip6 != NULL) {
253 /* IPv6 */
254 int i = 0;
255 if (dest != NULL) {
256 if (written + 1 <= maxChars) {
257 memcpy(dest + written, _UT("["),
258 1 * sizeof(URI_CHAR));
259 written += 1;
260 } else {
261 dest[0] = _UT('\0');
262 if (charsWritten != NULL) {
263 *charsWritten = 0;
264 }
265 return URI_ERROR_TOSTRING_TOO_LONG;
266 }
267 } else {
268 (*charsRequired) += 1;
269 }
270
271 for (; i < 16; i++) {
272 const unsigned char value = uri->hostData.ip6->data[i];
273 if (dest != NULL) {
274 if (written + 2 <= maxChars) {
275 URI_CHAR text[3];
276 text[0] = URI_FUNC(HexToLetterEx)(value / 16, URI_FALSE);
277 text[1] = URI_FUNC(HexToLetterEx)(value % 16, URI_FALSE);
278 text[2] = _UT('\0');
279 memcpy(dest + written, text, 2 * sizeof(URI_CHAR));
280 written += 2;
281 } else {
282 dest[0] = _UT('\0');
283 if (charsWritten != NULL) {
284 *charsWritten = 0;
285 }
286 return URI_ERROR_TOSTRING_TOO_LONG;
287 }
288 } else {
289 (*charsRequired) += 2;
290 }
291 if (((i & 1) == 1) && (i < 15)) {
292 if (dest != NULL) {
293 if (written + 1 <= maxChars) {
294 memcpy(dest + written, _UT(":"),
295 1 * sizeof(URI_CHAR));
296 written += 1;
297 } else {
298 dest[0] = _UT('\0');
299 if (charsWritten != NULL) {
300 *charsWritten = 0;
301 }
302 return URI_ERROR_TOSTRING_TOO_LONG;
303 }
304 } else {
305 (*charsRequired) += 1;
306 }
307 }
308 }
309
310 if (dest != NULL) {
311 if (written + 1 <= maxChars) {
312 memcpy(dest + written, _UT("]"),
313 1 * sizeof(URI_CHAR));
314 written += 1;
315 } else {
316 dest[0] = _UT('\0');
317 if (charsWritten != NULL) {
318 *charsWritten = 0;
319 }
320 return URI_ERROR_TOSTRING_TOO_LONG;
321 }
322 } else {
323 (*charsRequired) += 1;
324 }
325 } else if (uri->hostData.ipFuture.first != NULL) {
326 /* IPvFuture */
327 const int charsToWrite = (int)(uri->hostData.ipFuture.afterLast
328 - uri->hostData.ipFuture.first);
329 if (dest != NULL) {
330 if (written + 1 <= maxChars) {
331 memcpy(dest + written, _UT("["),
332 1 * sizeof(URI_CHAR));
333 written += 1;
334 } else {
335 dest[0] = _UT('\0');
336 if (charsWritten != NULL) {
337 *charsWritten = 0;
338 }
339 return URI_ERROR_TOSTRING_TOO_LONG;
340 }
341
342 if (written + charsToWrite <= maxChars) {
343 memcpy(dest + written, uri->hostData.ipFuture.first, charsToWrite * sizeof(URI_CHAR));
344 written += charsToWrite;
345 } else {
346 dest[0] = _UT('\0');
347 if (charsWritten != NULL) {
348 *charsWritten = 0;
349 }
350 return URI_ERROR_TOSTRING_TOO_LONG;
351 }
352
353 if (written + 1 <= maxChars) {
354 memcpy(dest + written, _UT("]"),
355 1 * sizeof(URI_CHAR));
356 written += 1;
357 } else {
358 dest[0] = _UT('\0');
359 if (charsWritten != NULL) {
360 *charsWritten = 0;
361 }
362 return URI_ERROR_TOSTRING_TOO_LONG;
363 }
364 } else {
365 (*charsRequired) += 1 + charsToWrite + 1;
366 }
367 } else if (uri->hostText.first != NULL) {
368 /* Regname */
369 const int charsToWrite = (int)(uri->hostText.afterLast - uri->hostText.first);
370 if (dest != NULL) {
371 if (written + charsToWrite <= maxChars) {
372 memcpy(dest + written, uri->hostText.first,
373 charsToWrite * sizeof(URI_CHAR));
374 written += charsToWrite;
375 } else {
376 dest[0] = _UT('\0');
377 if (charsWritten != NULL) {
378 *charsWritten = 0;
379 }
380 return URI_ERROR_TOSTRING_TOO_LONG;
381 }
382 } else {
383 (*charsRequired) += charsToWrite;
384 }
385 }
386
387 /* Port */
388 if (uri->portText.first != NULL) {
389 const int charsToWrite = (int)(uri->portText.afterLast - uri->portText.first);
390 if (dest != NULL) {
391 /* Leading ':' */
392 if (written + 1 <= maxChars) {
393 memcpy(dest + written, _UT(":"),
394 1 * sizeof(URI_CHAR));
395 written += 1;
396 } else {
397 dest[0] = _UT('\0');
398 if (charsWritten != NULL) {
399 *charsWritten = 0;
400 }
401 return URI_ERROR_TOSTRING_TOO_LONG;
402 }
403
404 /* Port number */
405 if (written + charsToWrite <= maxChars) {
406 memcpy(dest + written, uri->portText.first,
407 charsToWrite * sizeof(URI_CHAR));
408 written += charsToWrite;
409 } else {
410 dest[0] = _UT('\0');
411 if (charsWritten != NULL) {
412 *charsWritten = 0;
413 }
414 return URI_ERROR_TOSTRING_TOO_LONG;
415 }
416 } else {
417 (*charsRequired) += 1 + charsToWrite;
418 }
419 }
420 /* [09/19] endif; */
421 }
422 /* [10/19] append path to result; */
423 /* Slash needed here? */
424 if (uri->absolutePath || ((uri->pathHead != NULL)
425 && URI_FUNC(IsHostSet)(uri))) {
426 if (dest != NULL) {
427 if (written + 1 <= maxChars) {
428 memcpy(dest + written, _UT("/"),
429 1 * sizeof(URI_CHAR));
430 written += 1;
431 } else {
432 dest[0] = _UT('\0');
433 if (charsWritten != NULL) {
434 *charsWritten = 0;
435 }
436 return URI_ERROR_TOSTRING_TOO_LONG;
437 }
438 } else {
439 (*charsRequired) += 1;
440 }
441 }
442
443 if (uri->pathHead != NULL) {
444 URI_TYPE(PathSegment) * walker = uri->pathHead;
445 do {
446 const int charsToWrite = (int)(walker->text.afterLast - walker->text.first);
447 if (dest != NULL) {
448 if (written + charsToWrite <= maxChars) {
449 memcpy(dest + written, walker->text.first,
450 charsToWrite * sizeof(URI_CHAR));
451 written += charsToWrite;
452 } else {
453 dest[0] = _UT('\0');
454 if (charsWritten != NULL) {
455 *charsWritten = 0;
456 }
457 return URI_ERROR_TOSTRING_TOO_LONG;
458 }
459 } else {
460 (*charsRequired) += charsToWrite;
461 }
462
463 /* Not last segment -> append slash */
464 if (walker->next != NULL) {
465 if (dest != NULL) {
466 if (written + 1 <= maxChars) {
467 memcpy(dest + written, _UT("/"),
468 1 * sizeof(URI_CHAR));
469 written += 1;
470 } else {
471 dest[0] = _UT('\0');
472 if (charsWritten != NULL) {
473 *charsWritten = 0;
474 }
475 return URI_ERROR_TOSTRING_TOO_LONG;
476 }
477 } else {
478 (*charsRequired) += 1;
479 }
480 }
481
482 walker = walker->next;
483 } while (walker != NULL);
484 }
485 /* [11/19] if defined(query) then */
486 if (uri->query.first != NULL) {
487 /* [12/19] append "?" to result; */
488 if (dest != NULL) {
489 if (written + 1 <= maxChars) {
490 memcpy(dest + written, _UT("?"),
491 1 * sizeof(URI_CHAR));
492 written += 1;
493 } else {
494 dest[0] = _UT('\0');
495 if (charsWritten != NULL) {
496 *charsWritten = 0;
497 }
498 return URI_ERROR_TOSTRING_TOO_LONG;
499 }
500 } else {
501 (*charsRequired) += 1;
502 }
503 /* [13/19] append query to result; */
504 {
505 const int charsToWrite
506 = (int)(uri->query.afterLast - uri->query.first);
507 if (dest != NULL) {
508 if (written + charsToWrite <= maxChars) {
509 memcpy(dest + written, uri->query.first,
510 charsToWrite * sizeof(URI_CHAR));
511 written += charsToWrite;
512 } else {
513 dest[0] = _UT('\0');
514 if (charsWritten != NULL) {
515 *charsWritten = 0;
516 }
517 return URI_ERROR_TOSTRING_TOO_LONG;
518 }
519 } else {
520 (*charsRequired) += charsToWrite;
521 }
522 }
523 /* [14/19] endif; */
524 }
525 /* [15/19] if defined(fragment) then */
526 if (uri->fragment.first != NULL) {
527 /* [16/19] append "#" to result; */
528 if (dest != NULL) {
529 if (written + 1 <= maxChars) {
530 memcpy(dest + written, _UT("#"),
531 1 * sizeof(URI_CHAR));
532 written += 1;
533 } else {
534 dest[0] = _UT('\0');
535 if (charsWritten != NULL) {
536 *charsWritten = 0;
537 }
538 return URI_ERROR_TOSTRING_TOO_LONG;
539 }
540 } else {
541 (*charsRequired) += 1;
542 }
543 /* [17/19] append fragment to result; */
544 {
545 const int charsToWrite
546 = (int)(uri->fragment.afterLast - uri->fragment.first);
547 if (dest != NULL) {
548 if (written + charsToWrite <= maxChars) {
549 memcpy(dest + written, uri->fragment.first,
550 charsToWrite * sizeof(URI_CHAR));
551 written += charsToWrite;
552 } else {
553 dest[0] = _UT('\0');
554 if (charsWritten != NULL) {
555 *charsWritten = 0;
556 }
557 return URI_ERROR_TOSTRING_TOO_LONG;
558 }
559 } else {
560 (*charsRequired) += charsToWrite;
561 }
562 }
563 /* [18/19] endif; */
564 }
565 /* [19/19] return result; */
566 if (dest != NULL) {
567 dest[written++] = _UT('\0');
568 if (charsWritten != NULL) {
569 *charsWritten = written;
570 }
571 }
572 return URI_SUCCESS;
573}
574
575
576
577#endif
diff --git a/plugins/uriparser/UriResolve.c b/plugins/uriparser/UriResolve.c
new file mode 100644
index 00000000..3660b6b2
--- /dev/null
+++ b/plugins/uriparser/UriResolve.c
@@ -0,0 +1,316 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriResolve.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriResolve.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71/* Appends a relative URI to an absolute. The last path segment of
72 * the absolute URI is replaced. */
73static URI_INLINE UriBool URI_FUNC(MergePath)(URI_TYPE(Uri) * absWork,
74 const URI_TYPE(Uri) * relAppend) {
75 URI_TYPE(PathSegment) * sourceWalker;
76 URI_TYPE(PathSegment) * destPrev;
77 if (relAppend->pathHead == NULL) {
78 return URI_TRUE;
79 }
80
81 /* Replace last segment ("" if trailing slash) with first of append chain */
82 if (absWork->pathHead == NULL) {
83 URI_TYPE(PathSegment) * const dup = malloc(sizeof(URI_TYPE(PathSegment)));
84 if (dup == NULL) {
85 return URI_FALSE; /* Raises malloc error */
86 }
87 dup->next = NULL;
88 absWork->pathHead = dup;
89 absWork->pathTail = dup;
90 }
91 absWork->pathTail->text.first = relAppend->pathHead->text.first;
92 absWork->pathTail->text.afterLast = relAppend->pathHead->text.afterLast;
93
94 /* Append all the others */
95 sourceWalker = relAppend->pathHead->next;
96 if (sourceWalker == NULL) {
97 return URI_TRUE;
98 }
99 destPrev = absWork->pathTail;
100
101 for (;;) {
102 URI_TYPE(PathSegment) * const dup = malloc(sizeof(URI_TYPE(PathSegment)));
103 if (dup == NULL) {
104 destPrev->next = NULL;
105 absWork->pathTail = destPrev;
106 return URI_FALSE; /* Raises malloc error */
107 }
108 dup->text = sourceWalker->text;
109 destPrev->next = dup;
110
111 if (sourceWalker->next == NULL) {
112 absWork->pathTail = dup;
113 absWork->pathTail->next = NULL;
114 break;
115 }
116 destPrev = dup;
117 sourceWalker = sourceWalker->next;
118 }
119
120 return URI_TRUE;
121}
122
123
124static int URI_FUNC(ResolveAbsolutePathFlag)(URI_TYPE(Uri) * absWork) {
125 if (absWork == NULL) {
126 return URI_ERROR_NULL;
127 }
128
129 if (URI_FUNC(IsHostSet)(absWork) && absWork->absolutePath) {
130 /* Empty segment needed, instead? */
131 if (absWork->pathHead == NULL) {
132 URI_TYPE(PathSegment) * const segment = malloc(sizeof(URI_TYPE(PathSegment)));
133 if (segment == NULL) {
134 return URI_ERROR_MALLOC;
135 }
136 segment->text.first = URI_FUNC(SafeToPointTo);
137 segment->text.afterLast = URI_FUNC(SafeToPointTo);
138 segment->next = NULL;
139
140 absWork->pathHead = segment;
141 absWork->pathTail = segment;
142 }
143
144 absWork->absolutePath = URI_FALSE;
145 }
146
147 return URI_SUCCESS;
148}
149
150
151static int URI_FUNC(AddBaseUriImpl)(URI_TYPE(Uri) * absDest,
152 const URI_TYPE(Uri) * relSource,
153 const URI_TYPE(Uri) * absBase,
154 UriResolutionOptions options) {
155 if (absDest == NULL) {
156 return URI_ERROR_NULL;
157 }
158 URI_FUNC(ResetUri)(absDest);
159
160 if ((relSource == NULL) || (absBase == NULL)) {
161 return URI_ERROR_NULL;
162 }
163
164 /* absBase absolute? */
165 if (absBase->scheme.first == NULL) {
166 return URI_ERROR_ADDBASE_REL_BASE;
167 }
168
169 /* [00/32] -- A non-strict parser may ignore a scheme in the reference */
170 /* [00/32] -- if it is identical to the base URI's scheme. */
171 /* [00/32] if ((not strict) and (R.scheme == Base.scheme)) then */
172 UriBool relSourceHasScheme = (relSource->scheme.first != NULL) ? URI_TRUE : URI_FALSE;
173 if ((options & URI_RESOLVE_IDENTICAL_SCHEME_COMPAT)
174 && (absBase->scheme.first != NULL)
175 && (relSource->scheme.first != NULL)
176 && (0 == URI_FUNC(CompareRange)(&(absBase->scheme), &(relSource->scheme)))) {
177 /* [00/32] undefine(R.scheme); */
178 relSourceHasScheme = URI_FALSE;
179 /* [00/32] endif; */
180 }
181
182 /* [01/32] if defined(R.scheme) then */
183 if (relSourceHasScheme) {
184 /* [02/32] T.scheme = R.scheme; */
185 absDest->scheme = relSource->scheme;
186 /* [03/32] T.authority = R.authority; */
187 if (!URI_FUNC(CopyAuthority)(absDest, relSource)) {
188 return URI_ERROR_MALLOC;
189 }
190 /* [04/32] T.path = remove_dot_segments(R.path); */
191 if (!URI_FUNC(CopyPath)(absDest, relSource)) {
192 return URI_ERROR_MALLOC;
193 }
194 if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
195 return URI_ERROR_MALLOC;
196 }
197 /* [05/32] T.query = R.query; */
198 absDest->query = relSource->query;
199 /* [06/32] else */
200 } else {
201 /* [07/32] if defined(R.authority) then */
202 if (URI_FUNC(IsHostSet)(relSource)) {
203 /* [08/32] T.authority = R.authority; */
204 if (!URI_FUNC(CopyAuthority)(absDest, relSource)) {
205 return URI_ERROR_MALLOC;
206 }
207 /* [09/32] T.path = remove_dot_segments(R.path); */
208 if (!URI_FUNC(CopyPath)(absDest, relSource)) {
209 return URI_ERROR_MALLOC;
210 }
211 if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
212 return URI_ERROR_MALLOC;
213 }
214 /* [10/32] T.query = R.query; */
215 absDest->query = relSource->query;
216 /* [11/32] else */
217 } else {
218 /* [28/32] T.authority = Base.authority; */
219 if (!URI_FUNC(CopyAuthority)(absDest, absBase)) {
220 return URI_ERROR_MALLOC;
221 }
222 /* [12/32] if (R.path == "") then */
223 if (relSource->pathHead == NULL && !relSource->absolutePath) {
224 /* [13/32] T.path = Base.path; */
225 if (!URI_FUNC(CopyPath)(absDest, absBase)) {
226 return URI_ERROR_MALLOC;
227 }
228 /* [14/32] if defined(R.query) then */
229 if (relSource->query.first != NULL) {
230 /* [15/32] T.query = R.query; */
231 absDest->query = relSource->query;
232 /* [16/32] else */
233 } else {
234 /* [17/32] T.query = Base.query; */
235 absDest->query = absBase->query;
236 /* [18/32] endif; */
237 }
238 /* [19/32] else */
239 } else {
240 /* [20/32] if (R.path starts-with "/") then */
241 if (relSource->absolutePath) {
242 int res;
243 /* [21/32] T.path = remove_dot_segments(R.path); */
244 if (!URI_FUNC(CopyPath)(absDest, relSource)) {
245 return URI_ERROR_MALLOC;
246 }
247 res = URI_FUNC(ResolveAbsolutePathFlag)(absDest);
248 if (res != URI_SUCCESS) {
249 return res;
250 }
251 if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
252 return URI_ERROR_MALLOC;
253 }
254 /* [22/32] else */
255 } else {
256 /* [23/32] T.path = merge(Base.path, R.path); */
257 if (!URI_FUNC(CopyPath)(absDest, absBase)) {
258 return URI_ERROR_MALLOC;
259 }
260 if (!URI_FUNC(MergePath)(absDest, relSource)) {
261 return URI_ERROR_MALLOC;
262 }
263 /* [24/32] T.path = remove_dot_segments(T.path); */
264 if (!URI_FUNC(RemoveDotSegmentsAbsolute)(absDest)) {
265 return URI_ERROR_MALLOC;
266 }
267
268 if (!URI_FUNC(FixAmbiguity)(absDest)) {
269 return URI_ERROR_MALLOC;
270 }
271 /* [25/32] endif; */
272 }
273 /* [26/32] T.query = R.query; */
274 absDest->query = relSource->query;
275 /* [27/32] endif; */
276 }
277 URI_FUNC(FixEmptyTrailSegment)(absDest);
278 /* [29/32] endif; */
279 }
280 /* [30/32] T.scheme = Base.scheme; */
281 absDest->scheme = absBase->scheme;
282 /* [31/32] endif; */
283 }
284 /* [32/32] T.fragment = R.fragment; */
285 absDest->fragment = relSource->fragment;
286
287 return URI_SUCCESS;
288
289}
290
291
292
293int URI_FUNC(AddBaseUri)(URI_TYPE(Uri) * absDest,
294 const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase) {
295 const int res = URI_FUNC(AddBaseUriImpl)(absDest, relSource, absBase, URI_RESOLVE_STRICTLY);
296 if ((res != URI_SUCCESS) && (absDest != NULL)) {
297 URI_FUNC(FreeUriMembers)(absDest);
298 }
299 return res;
300}
301
302
303
304int URI_FUNC(AddBaseUriEx)(URI_TYPE(Uri) * absDest,
305 const URI_TYPE(Uri) * relSource, const URI_TYPE(Uri) * absBase,
306 UriResolutionOptions options) {
307 const int res = URI_FUNC(AddBaseUriImpl)(absDest, relSource, absBase, options);
308 if ((res != URI_SUCCESS) && (absDest != NULL)) {
309 URI_FUNC(FreeUriMembers)(absDest);
310 }
311 return res;
312}
313
314
315
316#endif
diff --git a/plugins/uriparser/UriShorten.c b/plugins/uriparser/UriShorten.c
new file mode 100644
index 00000000..c839ae53
--- /dev/null
+++ b/plugins/uriparser/UriShorten.c
@@ -0,0 +1,320 @@
1/*
2 * uriparser - RFC 3986 URI parsing library
3 *
4 * Copyright (C) 2007, Weijia Song <songweijia@gmail.com>
5 * Copyright (C) 2007, Sebastian Pipping <webmaster@hartwork.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer.
15 *
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials
19 * provided with the distribution.
20 *
21 * * Neither the name of the <ORGANIZATION> nor the names of its
22 * contributors may be used to endorse or promote products
23 * derived from this software without specific prior written
24 * permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39
40/* What encodings are enabled? */
41#include <uriparser/UriDefsConfig.h>
42#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE))
43/* Include SELF twice */
44# ifdef URI_ENABLE_ANSI
45# define URI_PASS_ANSI 1
46# include "UriShorten.c"
47# undef URI_PASS_ANSI
48# endif
49# ifdef URI_ENABLE_UNICODE
50# define URI_PASS_UNICODE 1
51# include "UriShorten.c"
52# undef URI_PASS_UNICODE
53# endif
54#else
55# ifdef URI_PASS_ANSI
56# include <uriparser/UriDefsAnsi.h>
57# else
58# include <uriparser/UriDefsUnicode.h>
59# include <wchar.h>
60# endif
61
62
63
64#ifndef URI_DOXYGEN
65# include <uriparser/Uri.h>
66# include "UriCommon.h"
67#endif
68
69
70
71static URI_INLINE UriBool URI_FUNC(AppendSegment)(URI_TYPE(Uri) * uri,
72 const URI_CHAR * first, const URI_CHAR * afterLast) {
73 /* Create segment */
74 URI_TYPE(PathSegment) * segment = malloc(1 * sizeof(URI_TYPE(PathSegment)));
75 if (segment == NULL) {
76 return URI_FALSE; /* Raises malloc error */
77 }
78 segment->next = NULL;
79 segment->text.first = first;
80 segment->text.afterLast = afterLast;
81
82 /* Put into chain */
83 if (uri->pathTail == NULL) {
84 uri->pathHead = segment;
85 } else {
86 uri->pathTail->next = segment;
87 }
88 uri->pathTail = segment;
89
90 return URI_TRUE;
91}
92
93
94
95static URI_INLINE UriBool URI_FUNC(EqualsAuthority)(const URI_TYPE(Uri) * first,
96 const URI_TYPE(Uri) * second) {
97 /* IPv4 */
98 if (first->hostData.ip4 != NULL) {
99 return ((second->hostData.ip4 != NULL)
100 && !memcmp(first->hostData.ip4->data,
101 second->hostData.ip4->data, 4)) ? URI_TRUE : URI_FALSE;
102 }
103
104 /* IPv6 */
105 if (first->hostData.ip6 != NULL) {
106 return ((second->hostData.ip6 != NULL)
107 && !memcmp(first->hostData.ip6->data,
108 second->hostData.ip6->data, 16)) ? URI_TRUE : URI_FALSE;
109 }
110
111 /* IPvFuture */
112 if (first->hostData.ipFuture.first != NULL) {
113 return ((second->hostData.ipFuture.first != NULL)
114 && !URI_STRNCMP(first->hostData.ipFuture.first,
115 second->hostData.ipFuture.first,
116 first->hostData.ipFuture.afterLast
117 - first->hostData.ipFuture.first))
118 ? URI_TRUE : URI_FALSE;
119 }
120
121 if (first->hostText.first != NULL) {
122 return ((second->hostText.first != NULL)
123 && !URI_STRNCMP(first->hostText.first,
124 second->hostText.first,
125 first->hostText.afterLast
126 - first->hostText.first)) ? URI_TRUE : URI_FALSE;
127 }
128
129 return (second->hostText.first == NULL);
130}
131
132
133
134int URI_FUNC(RemoveBaseUriImpl)(URI_TYPE(Uri) * dest,
135 const URI_TYPE(Uri) * absSource,
136 const URI_TYPE(Uri) * absBase,
137 UriBool domainRootMode) {
138 if (dest == NULL) {
139 return URI_ERROR_NULL;
140 }
141 URI_FUNC(ResetUri)(dest);
142
143 if ((absSource == NULL) || (absBase == NULL)) {
144 return URI_ERROR_NULL;
145 }
146
147 /* absBase absolute? */
148 if (absBase->scheme.first == NULL) {
149 return URI_ERROR_REMOVEBASE_REL_BASE;
150 }
151
152 /* absSource absolute? */
153 if (absSource->scheme.first == NULL) {
154 return URI_ERROR_REMOVEBASE_REL_SOURCE;
155 }
156
157 /* [01/50] if (A.scheme != Base.scheme) then */
158 if (URI_STRNCMP(absSource->scheme.first, absBase->scheme.first,
159 absSource->scheme.afterLast - absSource->scheme.first)) {
160 /* [02/50] T.scheme = A.scheme; */
161 dest->scheme = absSource->scheme;
162 /* [03/50] T.authority = A.authority; */
163 if (!URI_FUNC(CopyAuthority)(dest, absSource)) {
164 return URI_ERROR_MALLOC;
165 }
166 /* [04/50] T.path = A.path; */
167 if (!URI_FUNC(CopyPath)(dest, absSource)) {
168 return URI_ERROR_MALLOC;
169 }
170 /* [05/50] else */
171 } else {
172 /* [06/50] undef(T.scheme); */
173 /* NOOP */
174 /* [07/50] if (A.authority != Base.authority) then */
175 if (!URI_FUNC(EqualsAuthority)(absSource, absBase)) {
176 /* [08/50] T.authority = A.authority; */
177 if (!URI_FUNC(CopyAuthority)(dest, absSource)) {
178 return URI_ERROR_MALLOC;
179 }
180 /* [09/50] T.path = A.path; */
181 if (!URI_FUNC(CopyPath)(dest, absSource)) {
182 return URI_ERROR_MALLOC;
183 }
184 /* [10/50] else */
185 } else {
186 /* [11/50] if domainRootMode then */
187 if (domainRootMode == URI_TRUE) {
188 /* [12/50] undef(T.authority); */
189 /* NOOP */
190 /* [13/50] if (first(A.path) == "") then */
191 /* GROUPED */
192 /* [14/50] T.path = "/." + A.path; */
193 /* GROUPED */
194 /* [15/50] else */
195 /* GROUPED */
196 /* [16/50] T.path = A.path; */
197 /* GROUPED */
198 /* [17/50] endif; */
199 if (!URI_FUNC(CopyPath)(dest, absSource)) {
200 return URI_ERROR_MALLOC;
201 }
202 dest->absolutePath = URI_TRUE;
203
204 if (!URI_FUNC(FixAmbiguity)(dest)) {
205 return URI_ERROR_MALLOC;
206 }
207 /* [18/50] else */
208 } else {
209 const URI_TYPE(PathSegment) * sourceSeg = absSource->pathHead;
210 const URI_TYPE(PathSegment) * baseSeg = absBase->pathHead;
211 /* [19/50] bool pathNaked = true; */
212 UriBool pathNaked = URI_TRUE;
213 /* [20/50] undef(last(Base.path)); */
214 /* NOOP */
215 /* [21/50] T.path = ""; */
216 dest->absolutePath = URI_FALSE;
217 /* [22/50] while (first(A.path) == first(Base.path)) do */
218 while ((sourceSeg != NULL) && (baseSeg != NULL)
219 && !URI_STRNCMP(sourceSeg->text.first, baseSeg->text.first,
220 sourceSeg->text.afterLast - sourceSeg->text.first)
221 && !((sourceSeg->text.first == sourceSeg->text.afterLast)
222 && ((sourceSeg->next == NULL) != (baseSeg->next == NULL)))) {
223 /* [23/50] A.path++; */
224 sourceSeg = sourceSeg->next;
225 /* [24/50] Base.path++; */
226 baseSeg = baseSeg->next;
227 /* [25/50] endwhile; */
228 }
229 /* [26/50] while defined(first(Base.path)) do */
230 while ((baseSeg != NULL) && (baseSeg->next != NULL)) {
231 /* [27/50] Base.path++; */
232 baseSeg = baseSeg->next;
233 /* [28/50] T.path += "../"; */
234 if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstParent),
235 URI_FUNC(ConstParent) + 2)) {
236 return URI_ERROR_MALLOC;
237 }
238 /* [29/50] pathNaked = false; */
239 pathNaked = URI_FALSE;
240 /* [30/50] endwhile; */
241 }
242 /* [31/50] while defined(first(A.path)) do */
243 while (sourceSeg != NULL) {
244 /* [32/50] if pathNaked then */
245 if (pathNaked == URI_TRUE) {
246 /* [33/50] if (first(A.path) contains ":") then */
247 UriBool containsColon = URI_FALSE;
248 const URI_CHAR * ch = sourceSeg->text.first;
249 for (; ch < sourceSeg->text.afterLast; ch++) {
250 if (*ch == _UT(':')) {
251 containsColon = URI_TRUE;
252 break;
253 }
254 }
255
256 if (containsColon) {
257 /* [34/50] T.path += "./"; */
258 if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd),
259 URI_FUNC(ConstPwd) + 1)) {
260 return URI_ERROR_MALLOC;
261 }
262 /* [35/50] elseif (first(A.path) == "") then */
263 } else if (sourceSeg->text.first == sourceSeg->text.afterLast) {
264 /* [36/50] T.path += "/."; */
265 if (!URI_FUNC(AppendSegment)(dest, URI_FUNC(ConstPwd),
266 URI_FUNC(ConstPwd) + 1)) {
267 return URI_ERROR_MALLOC;
268 }
269 /* [37/50] endif; */
270 }
271 /* [38/50] endif; */
272 }
273 /* [39/50] T.path += first(A.path); */
274 if (!URI_FUNC(AppendSegment)(dest, sourceSeg->text.first,
275 sourceSeg->text.afterLast)) {
276 return URI_ERROR_MALLOC;
277 }
278 /* [40/50] pathNaked = false; */
279 pathNaked = URI_FALSE;
280 /* [41/50] A.path++; */
281 sourceSeg = sourceSeg->next;
282 /* [42/50] if defined(first(A.path)) then */
283 /* NOOP */
284 /* [43/50] T.path += + "/"; */
285 /* NOOP */
286 /* [44/50] endif; */
287 /* NOOP */
288 /* [45/50] endwhile; */
289 }
290 /* [46/50] endif; */
291 }
292 /* [47/50] endif; */
293 }
294 /* [48/50] endif; */
295 }
296 /* [49/50] T.query = A.query; */
297 dest->query = absSource->query;
298 /* [50/50] T.fragment = A.fragment; */
299 dest->fragment = absSource->fragment;
300
301 return URI_SUCCESS;
302}
303
304
305
306int URI_FUNC(RemoveBaseUri)(URI_TYPE(Uri) * dest,
307 const URI_TYPE(Uri) * absSource,
308 const URI_TYPE(Uri) * absBase,
309 UriBool domainRootMode) {
310 const int res = URI_FUNC(RemoveBaseUriImpl)(dest, absSource,
311 absBase, domainRootMode);
312 if ((res != URI_SUCCESS) && (dest != NULL)) {
313 URI_FUNC(FreeUriMembers)(dest);
314 }
315 return res;
316}
317
318
319
320#endif
diff --git a/po/de.po b/po/de.po
index 72694e27..919fae32 100644
--- a/po/de.po
+++ b/po/de.po
@@ -13,10 +13,10 @@ msgstr ""
13"PO-Revision-Date: 2004-12-23 17:46+0100\n" 13"PO-Revision-Date: 2004-12-23 17:46+0100\n"
14"Last-Translator: <>\n" 14"Last-Translator: <>\n"
15"Language-Team: English <en@li.org>\n" 15"Language-Team: English <en@li.org>\n"
16"Language: en\n"
17"MIME-Version: 1.0\n" 16"MIME-Version: 1.0\n"
18"Content-Type: text/plain; charset=iso-8859-1\n" 17"Content-Type: text/plain; charset=iso-8859-1\n"
19"Content-Transfer-Encoding: 8bit\n" 18"Content-Transfer-Encoding: 8bit\n"
19"Language: en\n"
20"Plural-Forms: nplurals=2; plural=(n > 1);X-Generator: KBabel 1.3.1\n" 20"Plural-Forms: nplurals=2; plural=(n > 1);X-Generator: KBabel 1.3.1\n"
21 21
22#: plugins/check_by_ssh.c:86 plugins/check_cluster.c:76 plugins/check_dig.c:88 22#: plugins/check_by_ssh.c:86 plugins/check_cluster.c:76 plugins/check_dig.c:88
@@ -5438,8 +5438,8 @@ msgstr ""
5438 5438
5439#: plugins/negate.c:174 5439#: plugins/negate.c:174
5440msgid "" 5440msgid ""
5441"Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer " 5441"Ok must be a valid state name (OK, WARNING, CRITICAL, UNKNOWN) or integer (0-"
5442"(0-3)." 5442"3)."
5443msgstr "" 5443msgstr ""
5444 5444
5445#: plugins/negate.c:180 5445#: plugins/negate.c:180