liboauth2-2.4.0/0000775000175000017500000000000015250557206014167 5ustar00hzandbelthzandbeltliboauth2-2.4.0/Makefile.am0000664000175000017500000002013715250557177016235 0ustar00hzandbelthzandbeltACLOCAL_AMFLAGS=-I m4 EXTRA_DIST = autogen.sh ChangeLog README.md LICENSE # # generic # AM_CPPFLAGS = -Wall -Werror -Wno-error=deprecated-declarations -I${srcdir}/include -I${srcdir}/src @JANSSON_CFLAGS@ @OPENSSL_CFLAGS@ AM_CPPFLAGS += $(CODE_COVERAGE_CPPFLAGS) $(CODE_COVERAGE_CFLAGS) AM_LDFLAGS = if CODE_COVERAGE_ENABLED AM_LDFLAGS += --coverage endif LDADD = @JANSSON_LIBS@ @OPENSSL_LIBS@ LDADD += $(CODE_COVERAGE_LIBS) # # headers # includesubdir = $(includedir)/oauth2 includesub_HEADERS = \ include/oauth2/cache.h \ include/oauth2/http.h \ include/oauth2/ipc.h \ include/oauth2/jose.h \ include/oauth2/log.h \ include/oauth2/mem.h \ include/oauth2/proto.h \ include/oauth2/oauth2.h \ include/oauth2/openidc.h \ include/oauth2/session.h \ include/oauth2/cfg.h \ include/oauth2/util.h \ include/oauth2/version.h if HAVE_LIBJQ includesub_HEADERS += \ include/oauth2/jq.h endif # # liboauth # lib_LTLIBRARIES = liboauth2.la liboauth2_la_pkgconfigdir = $(libdir)/pkgconfig liboauth2_la_pkgconfig_DATA = liboauth2.pc liboauth2_la_CFLAGS = @CURL_CFLAGS@ @CJOSE_CFLAGS@ @PCRE2_CFLAGS@ liboauth2_la_LIBADD = @CURL_LIBS@ @CJOSE_LIBS@ @PCRE2_LIBS@ liboauth2_la_LDFLAGS = @LIBTOOL_EXTRA_FLAGS@ liboauth2_la_SOURCES = \ src/version.c \ src/mem.c \ src/log.c \ src/util_int.h \ src/util.c \ src/cfg_int.h \ src/cfg/cfg.c \ src/cfg/auth.c \ src/cfg/source.c \ src/cfg/cache_cfg.c \ src/cfg/verify.c \ src/cfg/target.c \ src/cfg/proto_cfg.c \ src/cfg/openidc_cfg.c \ src/cfg/session_cfg.c \ src/ipc.c \ src/cache_int.h \ src/cache/shm.c \ src/cache/file.c \ src/jose_int.h \ src/jose.c \ src/http.c \ src/proto.c \ src/oauth2_int.h \ src/oauth2.c \ src/dpop.c \ src/session.c \ src/openidc_int.h \ src/openidc/provider.c \ src/openidc/resolver.c \ src/openidc/client.c \ src/openidc/state.c \ src/openidc/openidc.c if HAVE_LIBJQ AM_CPPFLAGS += -DHAVE_LIBJQ liboauth2_la_CFLAGS += @JQ_CFLAGS@ liboauth2_la_LIBADD += @JQ_LIBS@ liboauth2_la_SOURCES += src/jq.c endif # # cache # noinst_LTLIBRARIES = liboauth2_cache.la liboauth2_la_LIBADD += liboauth2_cache.la liboauth2_cache_la_SOURCES = src/cache.c liboauth2_cache_la_CPPFLAGS = $(AM_CPPFLAGS) liboauth2_cache_la_CFLAGS = @CURL_CFLAGS@ @CJOSE_CFLAGS@ @PCRE2_CFLAGS@ if HAVE_LIBMEMCACHE liboauth2_cache_la_CPPFLAGS += -DHAVE_LIBMEMCACHE liboauth2_la_LIBADD += liboauth2_memcache.la noinst_LTLIBRARIES += liboauth2_memcache.la liboauth2_memcache_la_SOURCES = src/cache/memcache.c liboauth2_memcache_la_CFLAGS = @MEMCACHE_CFLAGS@ liboauth2_memcache_la_LIBADD = @MEMCACHE_LIBS@ endif if HAVE_LIBHIREDIS liboauth2_cache_la_CPPFLAGS += -DHAVE_LIBHIREDIS liboauth2_la_LIBADD += liboauth2_redis.la noinst_LTLIBRARIES += liboauth2_redis.la liboauth2_redis_la_SOURCES = src/cache/redis.c liboauth2_redis_la_CFLAGS = @HIREDIS_CFLAGS@ liboauth2_redis_la_LIBADD = @HIREDIS_LIBS@ endif # # liboauth2_apache # if HAVE_APACHE lib_LTLIBRARIES += liboauth2_apache.la liboauth2_apache_la_pkgconfigdir = $(libdir)/pkgconfig liboauth2_apache_la_pkgconfig_DATA = liboauth2_apache.pc liboauth2_apache_la_CPPFLAGS = $(AM_CPPFLAGS) -DHAVE_APACHE liboauth2_apache_la_CFLAGS = @APACHE_CFLAGS@ @PCRE2_CFLAGS@ liboauth2_apache_la_LIBADD = liboauth2.la @APR_LIBS@ @JANSSON_LIBS@ liboauth2_apache_la_LDFLAGS = @LIBTOOL_EXTRA_FLAGS@ includesub_HEADERS += \ include/oauth2/apache.h liboauth2_apache_la_SOURCES = \ src/server/apache.c endif # # liboauth2_nginx # if HAVE_NGINX lib_LTLIBRARIES += liboauth2_nginx.la liboauth2_nginx_la_pkgconfigdir = $(libdir)/pkgconfig liboauth2_nginx_la_pkgconfig_DATA = liboauth2_nginx.pc liboauth2_nginx_la_CPPFLAGS = $(AM_CPPFLAGS) -DHAVE_NGINX liboauth2_nginx_la_CFLAGS = @NGINX_CFLAGS@ @PCRE2_CFLAGS@ liboauth2_nginx_la_LIBADD = liboauth2.la @NGINX_LIBS@ @JANSSON_LIBS@ liboauth2_nginx_la_LDFLAGS = @LIBTOOL_EXTRA_FLAGS@ includesub_HEADERS += \ include/oauth2/nginx.h liboauth2_nginx_la_SOURCES = \ src/server/nginx.c endif # # check # if HAVE_CHECK TESTS = check_liboauth2 check_PROGRAMS = $(TESTS) check_liboauth2_CPPFLAGS = $(liboauth2_cache_la_CPPFLAGS) check_liboauth2_CFLAGS = @OPENSSL_CFLAGS@ @CURL_CFLAGS@ @CJOSE_CFLAGS@ @PCRE2_CFLAGS@ @JQ_CFLAGS@ @CHECK_CFLAGS@ check_liboauth2_LDADD = liboauth2.la if HAVE_APACHE check_liboauth2_CPPFLAGS += $(liboauth2_apache_la_CPPFLAGS) check_liboauth2_CFLAGS += $(liboauth2_apache_la_CFLAGS) check_liboauth2_LDADD += liboauth2_apache.la ${liboauth2_apache_la_LIBADD} endif if HAVE_NGINX check_liboauth2_CPPFLAGS += $(liboauth2_nginx_la_CPPFLAGS) check_liboauth2_CFLAGS += $(liboauth2_nginx_la_CFLAGS) check_liboauth2_LDADD += liboauth2_nginx.la endif check_liboauth2_LDADD += @OPENSSL_LIBS@ @CURL_LIBS@ @CJOSE_LIBS@ @PCRE2_LIBS@ @CHECK_LIBS@ check_liboauth2_CFLAGS += -pthread # keep AM_LDFLAGS (carries --coverage when code coverage is enabled) and add -pthread check_liboauth2_LDFLAGS = $(AM_LDFLAGS) -pthread check_liboauth2_SOURCES = \ test/check_liboauth2.h \ test/check_liboauth2.c \ test/http_server.h \ test/http_server.c \ test/check_version.c \ test/check_mem.c \ test/check_log.c \ test/check_cfg.c \ test/check_util.c \ test/check_ipc.c \ test/check_cache.c \ test/check_jose.c \ test/check_http.c \ test/check_proto.c \ test/check_oauth2.c \ test/check_openidc.c \ test/check_session.c \ test/server_stubs.c \ test/provider.json \ test/client.json if HAVE_LIBJQ check_liboauth2_SOURCES += \ test/check_jq.c endif if HAVE_APACHE check_liboauth2_SOURCES += \ test/check_apache.c endif if HAVE_NGINX check_liboauth2_SOURCES += \ test/check_nginx.c endif endif #@CODE_COVERAGE_RULES@ # only the project's own sources: a bare "find ." would descend into any # other source trees checked out alongside and reformat those too clang-format: clang-format -style=file -i `find include src test -name '*.[ch]'` doxygen: cd $(top_srcdir) && \ (cat Doxyfile; echo "PROJECT_NUMBER = $(VERSION)") | doxygen - test: check # run the registered suites concurrently (one process per suite) instead of the # single serial check_liboauth2 run that "make check" performs; optional suites # that were not built simply run no tests. fails if any suite fails. CHECK_SUITES = version mem log cfg util ipc cache cache_file cache_memcache \ cache_redis jose http proto oauth2 openidc session jq apache nginx check-parallel: check_liboauth2$(EXEEXT) @echo "running $(words $(CHECK_SUITES)) suites in parallel ..."; \ failed=""; \ for s in $(CHECK_SUITES); do \ CK_RUN_SUITE=$$s ./check_liboauth2$(EXEEXT) \ >check-parallel-$$s.log 2>&1 & \ eval "pid_$$s=$$!"; \ done; \ for s in $(CHECK_SUITES); do \ eval "p=\$$pid_$$s"; \ if wait $$p; then echo " PASS $$s"; \ else echo " FAIL $$s"; failed="$$failed $$s"; fi; \ done; \ for s in $$failed; do echo "=== $$s ==="; cat check-parallel-$$s.log; done; \ rm -f check-parallel-*.log; \ if test -n "$$failed"; then echo "FAILED:$$failed"; exit 1; fi; \ echo "all suites passed" TAG=liboauth2/test docker: clean docker build --progress plain -f test/Dockerfile . -t $(TAG) docker-check: docker docker run -it --rm $(TAG):latest /bin/bash -c "./start.sh && make check" docker-coverage: clean docker build --build-arg CONFIGURE_ARGS="--enable-code-coverage" -f test/Dockerfile . -t $(TAG) docker run -it --rm $(TAG):latest /bin/bash -c "./start.sh && make check-code-coverage" docker-valgrind: docker docker run -it --rm -e CK_FORK=no $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/valgrind --leak-check=full --show-leak-kinds=definite --read-inline-info=yes --keep-debuginfo=yes .libs/check_liboauth2" docker-valgrind-%: docker docker run -it --rm -e CK_FORK=no -e CK_RUN_SUITE=${subst docker-valgrind-,,$@} $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/valgrind --leak-check=full --show-leak-kinds=definite --read-inline-info=yes --keep-debuginfo=yes .libs/check_liboauth2" docker-gdb: docker docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it --rm -e CK_FORK=no $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/gdb .libs/check_liboauth2" docker-gdb-%: docker docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -it --rm -e CK_FORK=no -e CK_RUN_SUITE=${subst docker-gdb-,,$@} $(TAG):latest /bin/bash -c "./start.sh && /usr/bin/gdb .libs/check_liboauth2" liboauth2-2.4.0/LICENSE0000664000175000017500000002367615112755670015214 0ustar00hzandbelthzandbelt Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS liboauth2-2.4.0/liboauth2_nginx.pc.in0000664000175000017500000000054015127103575020212 0ustar00hzandbelthzandbelt# # liboauth2_nginx - NGINX C bindings for liboauth2 # prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: liboauth2_nginx URL: https://github.com/OpenIDC/liboauth2 Description: NGINX C bindings for liboauth2 Version: @VERSION@ Requires: liboauth2 >= 2.2.0 Cflags: -I${includedir} Libs: -L${libdir} -loauth2_nginx liboauth2-2.4.0/AUTHORS0000664000175000017500000000126215112755670015242 0ustar00hzandbelthzandbeltThe primary author of liboauth2 is: Hans Zandbelt Thanks to the following people for contributing to liboauth2 by reporting bugs, providing fixes, suggesting useful features or other: Nicolas Mora Phil Brown Alexander Bokovoy Niebardzo Mikael Broadfoot Robert Title may-day Pavel Anpin smanolache pladen Drew liboauth2-2.4.0/autogen.sh0000775000175000017500000000007615112755670016175 0ustar00hzandbelthzandbelt#!/bin/sh autoreconf --force --install rm -rf autom4te.cache/ liboauth2-2.4.0/install-sh0000755000175000017500000003612215250556146016177 0ustar00hzandbelthzandbelt#!/bin/sh # install - install a program, script, or datafile scriptversion=2025-06-18.21; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Report bugs to . GNU Automake home page: . General help using GNU software: ." while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 (GNU Automake) $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibility with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp nil t) # time-stamp-start: "scriptversion=" # time-stamp-format: "%Y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: liboauth2-2.4.0/liboauth2.pc.in0000664000175000017500000000074015112755670017014 0ustar00hzandbelthzandbelt# # liboauth2 - OAuth 2.0 / OpenID Connect implementation for C # prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: liboauth2 URL: https://github.com/OpenIDC/liboauth2 Description: OAuth 2.0 / OpenID Connect implementation for C Version: @VERSION@ Requires: cjose >= 0.5.1, jansson >= 2.3, libcurl, libpcre2-8, libssl, libcrypto >= 1.0.1@MEMCACHE_PC@@HIREDIS_PC@@JQ_LIBS_PC@ Cflags: -I${includedir}@JQ_CFLAGS_PC@ Libs: -L${libdir} -loauth2 liboauth2-2.4.0/include/0000775000175000017500000000000015250557206015612 5ustar00hzandbelthzandbeltliboauth2-2.4.0/include/oauth2/0000775000175000017500000000000015250557206017014 5ustar00hzandbelthzandbeltliboauth2-2.4.0/include/oauth2/jose.h0000664000175000017500000003377015250553546020142 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_JOSE_H_ #define _OAUTH2_JOSE_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file jose.h * @brief JWT, JWK and JWKS handling on top of cjose. * * The JOSE layer of the library: hashing and symmetric key derivation * (OpenSSL), authenticated encryption of strings and JSON objects * under a shared secret (JWS inside JWE, used for the state cookies of * the OpenID Connect RP flow, "cookie" type sessions and encrypted * cache entries), signed JWT creation (client assertions for the * private_key_jwt and client_secret_jwt endpoint authentication * methods in cfg.h) and signed JWT verification against a set of keys * (the "jwk", "jwks_uri", "eckey_uri", "aws_alb" etc. verification * methods of oauth2_token_verify() in oauth2.h, id_token verification * in openidc.h and DPoP proofs in src/dpop.c). * * The cjose allocator is wired to the oauth2_mem_* functions (mem.h), * so any string returned here is released with oauth2_mem_free(). */ #include #include #include "oauth2/cfg.h" #include "oauth2/log.h" #include "oauth2/util.h" /** * @name Digest algorithm names * OpenSSL digest names accepted by the hashing functions and by the * hash_algo parameter of oauth2_jose_jwk_create_symmetric(); any name * EVP_get_digestbyname() knows is accepted, these are the ones the * library uses itself. * @{ */ #define OAUTH2_JOSE_OPENSSL_ALG_SHA1 "sha1" #define OAUTH2_JOSE_OPENSSL_ALG_SHA256 "sha256" /** @} */ /** * @name JOSE header and JWT claim names * The "typ" header (RFC 7515 section 4.1.9) and the registered claim * names of RFC 7519 section 4.1 as used in JWT verification. * @{ */ #define OAUTH2_JOSE_HDR_TYP "typ" #define OAUTH2_JOSE_HDR_TYP_JWT "JWT" #define OAUTH2_JOSE_JWT_ISS "iss" #define OAUTH2_JOSE_JWT_IAT "iat" #define OAUTH2_JOSE_JWT_EXP "exp" #define OAUTH2_JOSE_JWT_NBF "nbf" #define OAUTH2_JOSE_JWT_SUB "sub" #define OAUTH2_JOSE_JWT_AUD "aud" /** @} */ /** * @name Keys * A JWK as the library handles it: a cjose_jwk_t together with its * "kid", and a linked list of those as resolved from a JWKS. Lists are * created and consumed by the JWT verification machinery; the only * public operations are releasing them. * @{ */ typedef struct oauth2_jose_jwk_t oauth2_jose_jwk_t; /** * @brief Release a key, including the cjose_jwk_t it wraps. * * @param jwk the key to release, obtained from * oauth2_jose_jwk_create_symmetric() */ void oauth2_jose_jwk_release(oauth2_jose_jwk_t *jwk); typedef struct oauth2_jose_jwk_list_t oauth2_jose_jwk_list_t; /** * @brief Release a list of keys and every key in it. * * @param log the log handle to use * @param keys the head of the list; NULL is accepted */ void oauth2_jose_jwk_list_free(oauth2_log_t *log, oauth2_jose_jwk_list_t *keys); /** @} */ /** * @name Hashing * @{ */ /** * @brief Hash a byte string with an OpenSSL digest. * * @param log the log handle to use * @param digest the OpenSSL digest name, e.g. * OAUTH2_JOSE_OPENSSL_ALG_SHA256 * @param src the bytes to hash; hashing an empty input fails * @param src_len the number of bytes in src * @param dst set to the raw (binary, not encoded) digest as a * newly allocated buffer, to be released with * oauth2_mem_free() * @param dst_len set to the number of bytes in dst * @return true on success, false when the digest is unknown, the * input is empty or hashing failed */ bool oauth2_jose_hash_bytes(oauth2_log_t *log, const char *digest, const unsigned char *src, unsigned int src_len, unsigned char **dst, unsigned int *dst_len); /** * @brief Hash a string and return the digest as a hex string. * * Hashes src with oauth2_jose_hash_bytes() and lowercase-hex encodes * the result, e.g. 64 characters for "sha256"; used to derive cache * keys and cache encryption keys from their plaintext. * * @param log the log handle to use * @param digest the OpenSSL digest name * @param src the NUL-terminated string to hash, must not be NULL * @param dst set to the hex-encoded digest as a newly allocated * string, to be released with oauth2_mem_free() * @return true on success, false on error */ bool oauth2_jose_hash2s(oauth2_log_t *log, const char *digest, const char *src, char **dst); /** @} */ /** * @name Symmetric keys * @{ */ /** * @brief Derive a symmetric ("oct") JWK from a secret. * * @param log the log handle to use * @param client_secret the secret to derive the key from * @param hash_algo the OpenSSL digest to hash the secret with, the * digest becoming the key material (the OpenID * Connect Core 1.0 section 10.1 way of turning a * client_secret into a key); NULL uses the bytes * of the secret as the key as-is * @param jwk set to the new key, to be released with * oauth2_jose_jwk_release() * @return true on success, false on error */ bool oauth2_jose_jwk_create_symmetric(oauth2_log_t *log, const char *client_secret, const char *hash_algo, oauth2_jose_jwk_t **jwk); /** @} */ /** * @name Encryption under a shared secret * Authenticated encryption of a payload under a passphrase: the key is * the SHA-256 digest of the secret (oauth2_jose_jwk_create_symmetric() * with OAUTH2_JOSE_OPENSSL_ALG_SHA256), the payload is first signed * into a compact JWS with HS256 and that JWS is then encrypted into a * compact JWE with "alg":"dir" and "enc":"A256GCM". Decryption * reverses both steps and fails when either the JWE cannot be * decrypted or the inner signature does not verify under the same * secret. * @{ */ /** * @brief Encrypt a string under a shared secret. * * @param log the log handle to use * @param secret the shared secret * @param s_sig_payload the string to sign and encrypt * @param cser set to the compact serialized JWE as a newly * allocated string, to be released with * oauth2_mem_free() * @return true on success, false on error */ bool oauth2_jose_encrypt(oauth2_log_t *log, const char *secret, const char *s_sig_payload, char **cser); /** * @brief Encrypt a JSON object under a shared secret. * * The compact JSON serialization of payload is encrypted with * oauth2_jose_encrypt(). * * @param log the log handle to use * @param secret the shared secret * @param payload the JSON object to sign and encrypt * @param cser set to the compact serialized JWE as a newly * allocated string, to be released with * oauth2_mem_free() * @return true on success, false on error */ bool oauth2_jose_jwt_encrypt(oauth2_log_t *log, const char *secret, json_t *payload, char **cser); /** * @brief Decrypt a string encrypted with oauth2_jose_encrypt(). * * @param log the log handle to use * @param secret the shared secret the string was encrypted under * @param cser the compact serialized JWE * @param result set to the plaintext as a newly allocated * NUL-terminated string, to be released with * oauth2_mem_free() * @return true on success, false when decryption or the inner * signature verification failed */ bool oauth2_jose_decrypt(oauth2_log_t *log, const char *secret, const char *cser, char **result); /** * @brief Decrypt a JSON object encrypted with oauth2_jose_jwt_encrypt(). * * @param log the log handle to use * @param secret the shared secret the object was encrypted under * @param cser the compact serialized JWE * @param result set to the decrypted payload parsed into a new JSON * value, to be released with json_decref() * @return true on success, false when decryption, signature * verification or JSON parsing failed */ bool oauth2_jose_jwt_decrypt(oauth2_log_t *log, const char *secret, const char *cser, json_t **result); /** @} */ /** * @name JWT verification * @{ */ /** * @brief The keys and claim validation settings a JWT is verified with. * * Created and owned by the "jwk", "jwks_uri", "eckey_uri", "aws_alb" * etc. verification methods of a token verification chain, i.e. * populated from the options passed to * oauth2_cfg_token_verify_add_options() in cfg.h: the key source, the * expected "iss" and "aud" values, the "verify.iss", "verify.aud", * "verify.exp", "verify.nbf" and "verify.iat" strictness options * (each "required", "optional" or "skip") and the * "verify.iat.slack_before" and "verify.iat.slack_after" allowances * in seconds. */ typedef struct oauth2_jose_jwt_verify_ctx_t oauth2_jose_jwt_verify_ctx_t; /** * @brief Verify the signature and claims of a compact serialized JWS. * * Resolves the verification keys from the context's key source * (re-resolving once from a remote source when no key verified the * signature and the source allows a refresh), tries every key whose * "kid" matches the JWS header - keys without a "kid" and tokens * without one are tried against everything - and, once a key verified * the signature, parses the payload as a JSON object and validates its * "iss", "aud", "exp", "nbf" and "iat" claims per the context's * settings: a "required" claim must be present and valid, an * "optional" one is validated only when present, a "skip"ped one is * ignored; "iss"/"aud" are compared to the expected values ("aud" may * be a string or an array of strings), "exp" and "nbf" against the * current time and "iat" against the slack settings. * * @param log the log handle to use * @param jwt_verify_ctx the keys and claim validation settings; NULL * skips the signature verification and the * claim validation altogether and only extracts * the payload, so a NULL context must never be * used to accept a token * @param token the compact serialized JWS * @param json_payload set to the verified payload as a new JSON * object, to be released with json_decref(); * must not be NULL * @param s_payload set to the verified payload as a newly * allocated string, to be released with * oauth2_mem_free(); must not be NULL * @return true when the signature verified and the claims validated, * false otherwise (both outputs are then left unset) */ bool oauth2_jose_jwt_verify(oauth2_log_t *log, oauth2_jose_jwt_verify_ctx_t *jwt_verify_ctx, const char *token, json_t **json_payload, char **s_payload); /** @} */ /** * @name JWK thumbprint * @{ */ /** * @brief Compute the SHA-256 JWK thumbprint of a key (RFC 7638). * * Builds the canonical JSON representation of the required members of * the key ("crv", "kty", "x", "y" for EC, "e", "kty", "n" for RSA, * "k", "kty" for oct keys, in lexicographic order, no whitespace) and * hashes it with SHA-256; a DPoP "jkt" confirmation claim is the * base64url encoding of the result. * * @param log the log handle to use * @param jwk the key to compute the thumbprint of * @param hash_bytes set to the raw 32-byte digest as a newly * allocated buffer, to be released with * oauth2_mem_free() * @param hash_bytes_len set to the number of bytes in hash_bytes * @return true on success, false when the key type is not EC, RSA or * oct or hashing failed */ bool oauth2_jose_jwk_thumbprint(oauth2_log_t *log, const cjose_jwk_t *jwk, unsigned char **hash_bytes, unsigned int *hash_bytes_len); /** @} */ /** * @name JWT creation * @{ */ /** * @brief Create a signed JWT. * * Signs a JSON payload into a compact serialized JWS with the header * {"alg":alg,"typ":"JWT"}. The payload starts as a deep copy of * json_payload (or an empty object) into which the claims given as * parameters are set, overriding same-named members of json_payload. * This is what the private_key_jwt and client_secret_jwt endpoint * authentication methods use to create their client assertion. * * @param log the log handle to use * @param jwk the cjose key to sign with; must match alg * @param alg the JWS "alg" to sign with, e.g. "RS256" or * "HS256" * @param iss the "iss" claim, or NULL to leave it out * @param sub the "sub" claim, or NULL to leave it out * @param client_id unused * @param aud the "aud" claim, or NULL to leave it out * @param exp the lifetime of the JWT in seconds, the "exp" * claim being set to that many seconds from now; * 0 leaves "exp" out * @param include_iat set the "iat" claim to the current time * @param include_jti set the "jti" claim to a random string * @param json_payload additional claims to start from, or NULL * @return the compact serialized JWS as a newly allocated string, to * be released with oauth2_mem_free(), or NULL on error */ char *oauth2_jwt_create(oauth2_log_t *log, cjose_jwk_t *jwk, const char *alg, const char *iss, const char *sub, const char *client_id, const char *aud, oauth2_uint_t exp, bool include_iat, bool include_jti, const json_t *json_payload); /** @} */ #endif /* _OAUTH2_JOSE_H_ */ liboauth2-2.4.0/include/oauth2/util.h0000664000175000017500000006420115250553546020150 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_UTIL_H #define _OAUTH2_UTIL_H /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file util.h * @brief Basic types, the object declaration macros, name/value lists * and generic helpers. * * Everything else in the library builds on what is declared here: the * scalar types and their printf formats, the OAUTH2_TYPE_DECLARE * family of macros that generate the init/clone/free functions and * the member accessors of every public object type, the * oauth2_nv_list_t name/value list that carries options, parameters * and headers, and helpers for string handling, encoding, JSON, * random data, time and option parsing. Memory returned by any of * these functions is allocated with oauth2_mem_alloc() (mem.h) and * must be released with oauth2_mem_free(). */ #include #include #include #include "oauth2/log.h" #include /** * @name Preprocessor helpers * @{ */ /** @brief Turn a token into a string literal, without expanding it. */ #define OAUTH2_STRINGIFY(x) #x /** @brief Turn a macro's expansion into a string literal. */ #define OAUTH2_TOSTRING(x) OAUTH2_STRINGIFY(x) /** @} */ /** * @name Basic types * The scalar types used throughout the API and their printf formats. * @{ */ /** * @brief A tri-state boolean setting: 0 (off), 1 (on) or * OAUTH2_CFG_FLAG_UNSET (cfg.h) when not configured. */ typedef char oauth2_flag_t; /** @brief An unsigned integer setting, e.g. a count, size or status. */ typedef unsigned int oauth2_uint_t; /** * @brief A point in time as seconds since the epoch, or a duration in * seconds; 64-bit on every platform. */ typedef uint64_t oauth2_time_t; /** @brief printf conversion specifier for an oauth2_uint_t. */ #define OAUTH2_UINT_FORMAT "%u" /** @brief printf conversion specifier for an oauth2_time_t. */ #ifdef _WIN32 // LLP64: long is 32 bits there, so a 64-bit oauth2_time_t needs long long #define OAUTH2_TIME_T_FORMAT "%llu" #else #define OAUTH2_TIME_T_FORMAT "%lu" #endif /** @brief Milliseconds in a second. */ #define OAUTH2_MSEC_PER_SEC 1000 /** @brief Microseconds in a millisecond. */ #define OAUTH2_USEC_PER_MSEC 1000 /** @} */ /** * @name Object type declaration macros * The public object types are declared with these macros rather than * by hand: OAUTH2_TYPE_DECLARE(module, object) names the opaque type * oauth2___t and declares its lifecycle functions, * and the OAUTH2_TYPE_DECLARE_MEMBER_* macros declare typed accessors * for the individual members of such an object. The * OAUTH2_MEMBER_LIST_DECLARE_* macros declare accessors for a member * that is itself an oauth2_nv_list_t (e.g. the headers of an HTTP * request), and the OAUTH2_LIST_DECLARE_* macros declare the * name/value interface of an object that is a list itself. * * The implementations live with the module that defines the type; * every generated function takes an oauth2_log_t as its first * argument, as the rest of the API does. * @{ */ /** * @brief Declare an object type and its lifecycle functions. * * Generates the opaque typedef oauth2___t and three * lifecycle functions carrying the same prefix: * - init(log): allocate a new, zeroed object with its defaults * applied; NULL on allocation failure * - clone(log, src): a deep copy of src, to be released like src * itself; NULL when src is NULL or on failure * - free(log, obj): release the object and everything it owns; a * NULL obj is ignored */ #define OAUTH2_TYPE_DECLARE(module, object) \ typedef struct oauth2_##module##_##object##_t \ oauth2_##module##_##object##_t; \ oauth2_##module##_##object##_t *oauth2_##module##_##object##_init( \ oauth2_log_t *); \ oauth2_##module##_##object##_t *oauth2_##module##_##object##_clone( \ oauth2_log_t *, const oauth2_##module##_##object##_t *); \ void oauth2_##module##_##object##_free( \ oauth2_log_t *, oauth2_##module##_##object##_t *); /** * @brief Declare a setter oauth2____set(log, * obj, value) for a member of an object type. * * A string (char *) member is set to a copy of the value, releasing * the previously set string; NULL is rejected. Scalar members (bool, * oauth2_uint_t, oauth2_time_t, ...) are assigned; pointer members * store the pointer as-is, so the caller keeps ownership of the * pointed-to object. Returns true on success, false when obj is NULL * or the copy failed. */ #define OAUTH2_TYPE_DECLARE_MEMBER_SET(module, object, member, type) \ bool oauth2_##module##_##object##_##member##_set( \ oauth2_log_t *, oauth2_##module##_##object##_t *, const type); /** * @brief Declare a getter oauth2____get(log, * obj) for a member of an object type. * * Returns the member's value as stored: a borrowed pointer for string * and pointer members, owned by the object and valid until the object * is modified or freed. */ #define OAUTH2_TYPE_DECLARE_MEMBER_GET(module, object, member, type) \ type oauth2_##module##_##object##_##member##_get( \ oauth2_log_t *, const oauth2_##module##_##object##_t *); /** @brief Declare both the setter and the getter for a member. */ #define OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(module, object, member, type) \ OAUTH2_TYPE_DECLARE_MEMBER_SET(module, object, member, type) \ OAUTH2_TYPE_DECLARE_MEMBER_GET(module, object, member, type) /** * @brief Declare oauth2____set(log, obj, name, * value) for an oauth2_nv_list_t member, with the semantics of * oauth2_nv_list_set(). */ #define OAUTH2_MEMBER_LIST_DECLARE_SET(module, object, member) \ bool oauth2_##module##_##object##_##member##_set( \ oauth2_log_t *, oauth2_##module##_##object##_t *, const char *, \ const char *); /** * @brief Declare oauth2____unset(log, obj, * name) for an oauth2_nv_list_t member, with the semantics of * oauth2_nv_list_unset(). */ #define OAUTH2_MEMBER_LIST_DECLARE_UNSET(module, object, member) \ bool oauth2_##module##_##object##_##member##_unset( \ oauth2_log_t *, oauth2_##module##_##object##_t *, const char *); /** * @brief Declare oauth2____add(log, obj, name, * value) for an oauth2_nv_list_t member, with the semantics of * oauth2_nv_list_add(). */ #define OAUTH2_MEMBER_LIST_DECLARE_ADD(module, object, member) \ bool oauth2_##module##_##object##_##member##_add( \ oauth2_log_t *, oauth2_##module##_##object##_t *, const char *, \ const char *); /** * @brief Declare oauth2____get(log, obj, name) * for an oauth2_nv_list_t member, with the semantics of * oauth2_nv_list_get(). */ #define OAUTH2_MEMBER_LIST_DECLARE_GET(module, object, member) \ const char *oauth2_##module##_##object##_##member##_get( \ oauth2_log_t *, const oauth2_##module##_##object##_t *, \ const char *); /** * @brief Declare the set, unset, add and get accessors for an * oauth2_nv_list_t member. */ #define OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(module, object, member) \ OAUTH2_MEMBER_LIST_DECLARE_SET(module, object, member) \ OAUTH2_MEMBER_LIST_DECLARE_UNSET(module, object, member) \ OAUTH2_MEMBER_LIST_DECLARE_ADD(module, object, member) \ OAUTH2_MEMBER_LIST_DECLARE_GET(module, object, member) /** * @brief Declare oauth2___set(log, list, name, value) * for a list type; see oauth2_nv_list_set(). */ #define OAUTH2_LIST_DECLARE_SET(module, type) \ bool oauth2_##module##_##type##_set(oauth2_log_t *, \ oauth2_##module##_##type##_t *, \ const char *, const char *); /** * @brief Declare oauth2___unset(log, list, name) for a * list type; see oauth2_nv_list_unset(). */ #define OAUTH2_LIST_DECLARE_UNSET(module, type) \ bool oauth2_##module##_##type##_unset( \ oauth2_log_t *, oauth2_##module##_##type##_t *, const char *); /** * @brief Declare oauth2___add(log, list, name, value) * for a list type; see oauth2_nv_list_add(). */ #define OAUTH2_LIST_DECLARE_ADD(module, type) \ bool oauth2_##module##_##type##_add(oauth2_log_t *, \ oauth2_##module##_##type##_t *, \ const char *, const char *); /** * @brief Declare oauth2___get(log, list, name) for a * list type; see oauth2_nv_list_get(). */ #define OAUTH2_LIST_DECLARE_GET(module, type) \ const char *oauth2_##module##_##type##_get( \ oauth2_log_t *, const oauth2_##module##_##type##_t *, \ const char *); /** * @brief Declare the set, unset, add and get functions of a list * type. */ #define OAUTH2_LIST_DECLARE_SET_UNSET_ADD_GET(module, type) \ OAUTH2_LIST_DECLARE_SET(module, type) \ OAUTH2_LIST_DECLARE_UNSET(module, type) \ OAUTH2_LIST_DECLARE_ADD(module, type) \ OAUTH2_LIST_DECLARE_GET(module, type) /** @} */ /** * @name Name/value lists * oauth2_nv_list_t is an ordered list of name/value string pairs that * may hold the same name more than once; it carries configuration * options, request parameters, headers and cookies throughout the * API. Names are matched case-sensitively unless case_sensitive is * set to false; the list copies the names and values it is given and * owns them. Create a list with oauth2_nv_list_init(), copy it with * oauth2_nv_list_clone() and release it with oauth2_nv_list_free(). * * The set, unset, add and get functions declared here: * - oauth2_nv_list_set(log, list, name, value): replace the value of * the first entry with that name, or append a new entry when there * is none; a NULL value is stored as such * - oauth2_nv_list_unset(log, list, name): remove the first entry * with that name; succeeds when there is none * - oauth2_nv_list_add(log, list, name, value): append an entry, * even when the name is already present (e.g. repeated query * parameters or headers) * - oauth2_nv_list_get(log, list, name): the value of the first entry * with that name, borrowed from the list, or NULL when absent or * stored as NULL * * All of them return false, or NULL, when the list or name is NULL. * @{ */ OAUTH2_TYPE_DECLARE(nv, list) OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(nv, list, case_sensitive, bool) OAUTH2_LIST_DECLARE_SET_UNSET_ADD_GET(nv, list) /** * @brief Callback invoked by oauth2_nv_list_loop() for each entry. * * @param log the log handle to use * @param rec the caller's context passed to oauth2_nv_list_loop() * @param key the entry's name, borrowed from the list * @param value the entry's value, borrowed from the list; may be NULL * @return true to continue with the next entry, false to stop the * iteration */ typedef bool(oauth2_nv_list_loop_cb_t)(oauth2_log_t *log, void *rec, const char *key, const char *value); /** * @brief Iterate over the entries of a list, in insertion order, * until the callback returns false. */ void oauth2_nv_list_loop(oauth2_log_t *log, const oauth2_nv_list_t *list, oauth2_nv_list_loop_cb_t *callback, void *rec); /** * @brief Render a list for logging purposes. * * @return a newly allocated string in the form "[ name=value ... ]", * to be released with oauth2_mem_free() */ char *oauth2_nv_list2s(oauth2_log_t *log, const oauth2_nv_list_t *list); /** * @brief Append copies of all entries of source to target. * * Entries are added, not set, so names already present in target are * repeated rather than overwritten; a NULL source is ignored. */ void oauth2_nv_list_merge_into(oauth2_log_t *log, const oauth2_nv_list_t *source, oauth2_nv_list_t *target); /** @} */ /** * @name Library initialization * @{ */ /** * @brief Initialize the library. * * Call once, before any other function, from the main thread: this * initializes OpenSSL and libcurl (curl_global_init) and creates the * global log handle with oauth2_log_init() (log.h). To use a custom * allocator, call oauth2_mem_set_alloc_funcs() (mem.h) before this. * * @param level the log level of the initial sink * @param sink the log sink to send messages to, e.g. * oauth2_log_sink_stderr, or NULL for no output * @return the log handle to pass to the other functions and to * oauth2_shutdown() when done */ oauth2_log_t *oauth2_init(oauth2_log_level_t level, oauth2_log_sink_t *sink); /** * @brief Shut the library down. * * Releases the global cache and session state and the libcurl and * OpenSSL resources, and frees the log handle returned by * oauth2_init(); no library function may be called afterwards. */ void oauth2_shutdown(oauth2_log_t *); /** @} */ /** * @name String helpers * All strings returned by these functions are newly allocated and to * be released with oauth2_mem_free(). * @{ */ /** * @brief snprintf() that tolerates a NULL destination or format. * * @return the value snprintf() returns, or 0 when dst or fmt is NULL */ int oauth2_snprintf(char *dst, size_t len, const char *fmt, ...); /** @brief Copy a string; NULL for a NULL src or on allocation failure. */ char *oauth2_strdup(const char *src); /** * @brief Copy the first len bytes of a string, NUL-terminating the * result; NULL for a NULL src or on allocation failure. */ char *oauth2_strndup(const char *src, size_t len); /** * @brief Concatenate strings. * * @param src the string to append to, which this function takes * ownership of and releases; NULL starts from an empty * string * @param add1 the first string to append, or NULL for none * @param add2 the second string to append, or NULL for none * @param add3 the third string to append, or NULL for none * @return a new string holding the concatenation, replacing src, or * NULL on allocation failure */ char *oauth2_stradd(char *src, const char *add1, const char *add2, const char *add3); /** * @brief Concatenate strings and the decimal representation of an * integer, with the ownership semantics of oauth2_stradd(). */ char *oauth2_intadd(char *src, const char *add1, const char *add2, int i); /** * @brief Split off the next word of a string. * * @param line the position to read from; advanced past the word and * past any run of stop characters that follows it * @param stop the separator character * @return a copy of the characters up to the separator or the end of * the string, possibly empty */ char *oauth2_getword(const char **line, char stop); /** @} */ /** * @name Encoding and escaping * @{ */ /** * @brief Base64url-encode (RFC 4648 section 5, without padding) a * byte buffer. * * @param log the log handle to use * @param src the bytes to encode * @param src_len the number of bytes to encode * @param dst set to the NUL-terminated encoded string, to be * released with oauth2_mem_free(); NULL on error * @return the length of the encoded string, 0 on error */ size_t oauth2_base64url_encode(oauth2_log_t *log, const uint8_t *src, const size_t src_len, char **dst); /** * @brief Decode a base64url-encoded string. * * @param log the log handle to use * @param src the string to decode * @param dst set to the decoded bytes, to be released with * oauth2_mem_free(); NULL on error * @param dst_len set to the number of decoded bytes * @return true on success, false on error */ bool oauth2_base64url_decode(oauth2_log_t *log, const char *src, uint8_t **dst, size_t *dst_len); /** @brief As oauth2_base64url_encode(), for standard padded base64. */ size_t oauth2_base64_encode(oauth2_log_t *log, const uint8_t *src, const size_t src_len, char **dst); /** @brief As oauth2_base64url_decode(), for standard padded base64. */ bool oauth2_base64_decode(oauth2_log_t *log, const char *src, uint8_t **dst, size_t *dst_len); /** * @brief Percent-encode a string for use in a URL or form body. * * @return the encoded string, to be released with oauth2_mem_free(); * NULL for a NULL str or on error */ char *oauth2_url_encode(oauth2_log_t *log, const char *str); /** * @brief Decode a percent-encoded string, turning "+" into a space * as in form-encoded data. * * @return the decoded string, to be released with oauth2_mem_free(); * NULL for a NULL str or on error */ char *oauth2_url_decode(oauth2_log_t *log, const char *str); /** * @brief Escape the HTML special characters &, ', ", < and > in a * string. * * @return the escaped string, to be released with oauth2_mem_free(); * NULL for a NULL src */ char *oauth2_html_escape(oauth2_log_t *log, const char *src); /** * @brief Parse a form-encoded (application/x-www-form-urlencoded) * string, e.g. a query string or POST body, into a list. * * @param log the log handle to use * @param data the "name=value&name=value" string to parse; a NULL * data succeeds without touching params * @param params set to a newly allocated list of the decoded * parameters, in order and with repeated names kept, * to be released with oauth2_nv_list_free() * @return true on success, false on error */ bool oauth2_parse_form_encoded_params(oauth2_log_t *log, const char *data, oauth2_nv_list_t **params); /** @} */ /** * @name JSON helpers * Conveniences on top of jansson for the JSON documents exchanged in * the protocols: parsing and typed member lookups with defaults. * @{ */ /** * @brief Parse a JSON object and check that it is not an OAuth 2.x * error response. * * As oauth2_json_decode_object(), but a document containing an * "error" member (RFC 6749 section 5.2) is logged, together with its * "error_description", and rejected. * * @param log the log handle to use * @param str the JSON text to parse * @param json set to the parsed object, to be released with * json_decref(); NULL on failure * @return true on success, false when the text does not parse to an * object or contains an "error" member */ bool oauth2_json_decode_check_error(oauth2_log_t *log, const char *str, json_t **json); /** * @brief Parse a JSON text that must be a JSON object. * * @param log the log handle to use * @param payload the JSON text to parse * @param json set to the parsed object, to be released with * json_decref(); NULL on failure * @return true on success, false when the text does not parse or is * not an object */ bool oauth2_json_decode_object(oauth2_log_t *log, const char *payload, json_t **json); /** * @brief Get an object-valued member of a JSON object. * * @param log the log handle to use * @param json the object to look in * @param name the member name * @param value set to a new reference to the member, to be released * with json_decref(); left untouched when the member is * absent or null * @return true when the member is an object or null, false when it * is absent, of another type or an argument is NULL */ bool oauth2_json_object_get(oauth2_log_t *log, const json_t *json, const char *name, json_t **value); /** * @brief Get a string-valued member of a JSON object. * * @param log the log handle to use * @param json the object to look in * @param name the member name * @param value set to a copy of the member's string, or of * default_value when the member is absent and a * default was given, to be released with * oauth2_mem_free(); left untouched for a null * member or when there is no default * @param default_value the value to return when the member is * absent, or NULL for none * @return true when the member is a string, null or absent, false * when it is of another type or an argument is NULL */ bool oauth2_json_string_get(oauth2_log_t *log, const json_t *json, const char *name, char **value, const char *default_value); /** * @brief Get a number-valued member of a JSON object. * * @param log the log handle to use * @param json the object to look in * @param name the member name * @param number set to the member's integer value, or to * default_value when the member is absent or * null * @param default_value the value to return when the member is absent * @return true when the member is a number, null or absent, false * when it is of another type or an argument is NULL */ bool oauth2_json_number_get(oauth2_log_t *log, const json_t *json, const char *name, json_int_t *number, const json_int_t default_value); /** @} */ /** * @name Random data, time and option parsing * @{ */ /** * @brief Generate a random string, e.g. for a nonce, state value or * cookie name. * * @param log the log handle to use * @param len the number of characters to generate * @return a string of len lowercase hexadecimal characters drawn from * the OpenSSL random generator, to be released with * oauth2_mem_free(); NULL when len is 0 or on error */ char *oauth2_rand_str(oauth2_log_t *log, size_t len); /** @brief The current time in seconds since the epoch. */ oauth2_time_t oauth2_time_now_sec(); /** * @brief Parse a decimal number of seconds. * * @param log the log handle to use * @param seconds the string to parse, or NULL * @param default_value the value to return for a NULL seconds * @return the parsed value; 0 (as strtol() yields) for a string that * is not a number */ oauth2_time_t oauth2_parse_time_sec(oauth2_log_t *log, const char *seconds, oauth2_time_t default_value); /** * @brief Parse a boolean option. * * @param log the log handle to use * @param value the string to parse, or NULL * @param default_value the value to return for a NULL value * @return true when value equals "true", case-insensitively, false * for any other string */ bool oauth2_parse_bool(oauth2_log_t *log, const char *value, bool default_value); /** * @brief Parse a decimal unsigned integer. * * @param log the log handle to use * @param int_value the string to parse, or NULL * @param default_value the value to return for a NULL int_value * @return the parsed value; 0 (as strtol() yields) for a string that * is not a number */ oauth2_uint_t oauth2_parse_uint(oauth2_log_t *log, const char *int_value, oauth2_uint_t default_value); /** @} */ /** * @name Header name and value helpers * @{ */ /** * @brief Compare two strings as environment variable names. * * Both strings are compared after normalizing each character the way * a header name is turned into an environment variable name: letters * and digits uppercased, anything else mapped to an underscore, so * that e.g. "Content-Type" and "CONTENT_TYPE" compare equal. * * @param a the first string * @param b the second string * @param len the number of characters to compare, or a negative value * to compare the whole strings * @return 0 when equal, negative when a sorts before b, positive * otherwise, as strcmp() */ int oauth2_strnenvcmp(const char *a, const char *b, int len); /** * @brief Serialize a JSON value. * * @param log the log handle to use * @param json the value to serialize * @param flags jansson json_dumps() flags, e.g. JSON_COMPACT or * JSON_ENCODE_ANY for a value that is not an object or * array * @return the JSON text, to be released with oauth2_mem_free(); NULL * when serialization fails */ char *oauth2_json_encode(oauth2_log_t *log, json_t *json, size_t flags); /** * @brief Make a string usable as an HTTP header name. * * Replaces every character that is not allowed in a header field * name (control characters and the separators of RFC 2616 section * 2.2, including space) with a dash. * * @return the normalized name, to be released with oauth2_mem_free() */ char *oauth2_normalize_header_name(const char *str); /** * @brief Convert a UTF-8 string to ISO-8859-1, for use as an HTTP * header value. * * Code points above U+00FF, which have no Latin-1 representation, are * replaced with a question mark. * * @return the converted string, to be released with oauth2_mem_free(); * NULL for a NULL str */ char *oauth2_utf8_to_latin1(const char *str); /** @} */ /** * @brief Read a whole file into memory. * * @param log the log handle to use * @param filename the path of the file to read * @return the file contents as a NUL-terminated string, to be released * with oauth2_mem_free(); NULL, having logged the reason, when * the file cannot be read */ char *oauth_read_file(oauth2_log_t *log, const char *filename); #endif /* _OAUTH2_UTIL_H */ liboauth2-2.4.0/include/oauth2/http.h0000664000175000017500000007066415250553546020164 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_HTTP_H_ #define _OAUTH2_HTTP_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file http.h * @brief HTTP request/response abstraction and outgoing HTTP client. * * This is the seam between the server bindings and the core library: * a binding translates the hosting server's native request object * (Apache's request_rec, NGINX's ngx_http_request_t) into an * oauth2_http_request_t and turns the resulting * oauth2_http_response_t back into a native response; everything below * the bindings operates on these abstract types only. This header also * provides the libcurl-based HTTP client used for outgoing calls to * OAuth 2.x endpoints. */ #include "oauth2/cfg.h" #include "oauth2/util.h" #include /** * @name HTTP header names and values * The headers the library reads from incoming requests and writes to * outgoing calls and responses, and the values it recognizes in them. * @{ */ // TODO: can these be http.c internal with the set and get functions available? /** @brief The scheme a reverse proxy received the request on. */ #define OAUTH2_HTTP_HDR_X_FORWARDED_PROTO "X-Forwarded-Proto" /** @brief The port a reverse proxy received the request on. */ #define OAUTH2_HTTP_HDR_X_FORWARDED_PORT "X-Forwarded-Port" /** @brief The host, with optional port, a reverse proxy received the * request for. */ #define OAUTH2_HTTP_HDR_X_FORWARDED_HOST "X-Forwarded-Host" /** @brief The host, with optional port, the user agent addressed. */ #define OAUTH2_HTTP_HDR_HOST "Host" /** @brief The cookies sent by the user agent. */ #define OAUTH2_HTTP_HDR_COOKIE "Cookie" /** @brief The media type of a request or response body. */ #define OAUTH2_HTTP_HDR_CONTENT_TYPE "Content-Type" /** @brief The size of a request or response body in bytes. */ #define OAUTH2_HTTP_HDR_CONTENT_LENGTH "Content-Length" /** @brief The credentials a client presents. */ #define OAUTH2_HTTP_HDR_AUTHORIZATION "Authorization" /** @brief Set by JavaScript frameworks on the requests they issue. */ #define OAUTH2_HTTP_HDR_X_REQUESTED_WITH "X-Requested-With" /** @brief The media types the user agent accepts in a response. */ #define OAUTH2_HTTP_HDR_ACCEPT "Accept" /** @brief The target of a redirect response. */ #define OAUTH2_HTTP_HDR_LOCATION "Location" /** @brief A cookie a response sets on the user agent. */ #define OAUTH2_HTTP_HDR_SET_COOKIE "Set-Cookie" /** @brief The Authorization scheme carrying a bearer token (RFC 6750). */ #define OAUTH2_HTTP_HDR_BEARER "Bearer" /** @brief The Authorization scheme carrying a username and password * (RFC 7617). */ #define OAUTH2_HTTP_HDR_BASIC "Basic" /** @brief The realm attribute of a WWW-Authenticate challenge. */ #define OAUTH2_HTTP_HDR_REALM "realm" /** @brief The challenge a 401 response carries. */ #define OAUTH2_HTTP_HDR_WWW_AUTHENTICATE "WWW-Authenticate" /** @brief The X-Requested-With value that identifies an AJAX request. */ #define OAUTH2_HTTP_HDR_XML_HTTP_REQUEST "XMLHttpRequest" /** * @brief The request context key (oauth2_http_request_context_set()) * under which a server binding stores the PEM-encoded TLS * client certificate of the connection, for the mTLS-bound * token check in oauth2_token_verify() (oauth2.h). */ #define OAUTH2_TLS_CERT_VAR_NAME "SSL_CLIENT_CERT" /** @} */ /** * @name Content types * @{ */ /** @brief HTML form data: token endpoint requests, POSTed tokens. */ #define OAUTH2_CONTENT_TYPE_FORM_ENCODED "application/x-www-form-urlencoded" /** @brief JSON documents: endpoint responses, JSON POSTs. */ #define OAUTH2_CONTENT_TYPE_JSON "application/json" /** @brief HTML pages; a user agent not accepting them is treated as a * script, see oauth2_http_request_is_xml_http_request(). */ #define OAUTH2_CONTENT_TYPE_TEXT_HTML "text/html" /** @brief XHTML pages, accepted like text/html. */ #define OAUTH2_CONTENT_TYPE_APP_XHTML_XML "application/xhtml+xml" /** @brief The Accept wildcard matching any media type. */ #define OAUTH2_CONTENT_TYPE_ANY "*/*" /** @} */ /** * @name Protocol constants * @{ */ /** @brief The URL scheme of plain HTTP. */ #define OAUTH2_HTTP_SCHEME_HTTP "http" /** @brief The URL scheme of HTTP over TLS. */ #define OAUTH2_HTTP_SCHEME_HTTPS "https" /** @brief The method of an incoming HTTP request. */ typedef enum { OAUTH2_HTTP_METHOD_UNKNOWN, /**< not set, or none of the below */ OAUTH2_HTTP_METHOD_GET, /**< GET */ OAUTH2_HTTP_METHOD_PUT, /**< PUT */ OAUTH2_HTTP_METHOD_POST, /**< POST */ OAUTH2_HTTP_METHOD_DELETE, /**< DELETE */ OAUTH2_HTTP_METHOD_CONNECT, /**< CONNECT */ OAUTH2_HTTP_METHOD_OPTIONS /**< OPTIONS */ } oauth2_http_method_t; /** @brief An HTTP status code, e.g. 200, 302 or 401. */ typedef oauth2_uint_t oauth2_http_status_code_t; /** @} */ /** * @name Incoming HTTP request * oauth2_http_request_t represents an incoming HTTP request, * independent of the hosting server. A server binding creates one per * native request and populates: the native URL scheme on which the * request was received (i.e. without taking forwarding headers into * account), the configured server hostname, the native port, the path, * the HTTP method, the query string, and each incoming header. The * oauth2_http_request_url_..._get() functions derive the externally * visible URL from these, taking the X-Forwarded-Proto, * X-Forwarded-Host, X-Forwarded-Port and Host headers into account * when present. The request context is a name/value list for * additional per-request data provided by the binding, such as the * TLS client certificate under OAUTH2_TLS_CERT_VAR_NAME. * * The member setters copy the value and reject NULL; the generated * getters return the member as stored and abort on a NULL request, * whereas the hand-written scheme and hostname getters derive the * externally visible values, see below. * @{ */ /** * @brief Opaque incoming request with its init and free functions; the * _clone function the declaration macro names is not * implemented. */ OAUTH2_TYPE_DECLARE(http, request) /** * @brief The native URL scheme, "http" or "https", the request was * received on. * * The getter is hand-written: it returns the left-most value of the * X-Forwarded-Proto header when present and the native scheme * otherwise, falling back to "https" with a warning when the result is * neither "http" nor "https", as a newly allocated string to be * released with oauth2_mem_free(), or NULL when the request is NULL. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, scheme, char *) /** * @brief The configured hostname of the server. * * The getter is hand-written: it prefers the left-most value of the * X-Forwarded-Host header, then the Host header, both with any ":port" * suffix removed, over the configured hostname, and returns a newly * allocated string to be released with oauth2_mem_free(), or NULL when * none of them is set. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, hostname, char *) /** @brief The path of the request URL, without the query string. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, path, char *) /** @brief The HTTP method; OAUTH2_HTTP_METHOD_UNKNOWN until set. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, method, oauth2_http_method_t) /** * @brief The raw query string, without the leading "?"; the * ..._query_param_...() functions parse and rewrite it. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, request, query, char *) /** * @brief Get the request method as its uppercase name, e.g. "GET". * @return a static string, or NULL for OAUTH2_HTTP_METHOD_UNKNOWN */ const char *oauth2_http_request_method_get_str(oauth2_log_t *, oauth2_http_request_t *); /** * @brief Store a value in the request context. * * The context is a case-sensitive name/value list, separate from the * headers, in which a server binding passes per-request data that has * no place in the HTTP request itself, such as the TLS client * certificate under OAUTH2_TLS_CERT_VAR_NAME. * * @param log the log handle to use * @param request the request to store into * @param name the name to store the value under, replacing an * existing value of that name * @param value the value, copied into the request; may be NULL * @return true when stored, false when request or name is NULL */ bool oauth2_http_request_context_set(oauth2_log_t *log, oauth2_http_request_t *request, const char *name, const char *value); /** * @brief Look up a value in the request context. * @return a borrowed pointer to the value, or NULL when not set */ const char *oauth2_http_request_context_get( oauth2_log_t *log, const oauth2_http_request_t *request, const char *name); /** @} */ /** * @name Outgoing HTTP response * oauth2_http_response_t represents the HTTP response that the core * hands back to the server binding to be delivered to the user agent: * a status code plus headers (e.g. Location and Set-Cookie); the * binding translates it into its native response object. * @{ */ /** * @brief Opaque outgoing response with its init, clone and free * functions. */ OAUTH2_TYPE_DECLARE(http, response) /** * @brief The response headers as a name/value list owned by the * response. The setter is not implemented and returns false: * use oauth2_http_response_header_set() and * oauth2_http_response_cookie_set() instead. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, response, headers, oauth2_nv_list_t *) /** @brief The HTTP status code to respond with; 0 until set. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(http, response, status_code, oauth2_http_status_code_t) /** * @brief Set a response header, replacing an existing header of that * name (matched case-sensitively) or adding it when absent. * @return true when set, false when the name is NULL */ bool oauth2_http_response_header_set(oauth2_log_t *log, oauth2_http_response_t *response, const char *name, const char *value); /** * @brief Get a response header. * @return a borrowed pointer into the response's header list, or NULL * when not set */ const char * oauth2_http_response_header_get(oauth2_log_t *log, const oauth2_http_response_t *response, const char *name); /** * @brief Find the first Set-Cookie header whose value starts with a * prefix, typically "name=" to find the cookie of that name. * @return a borrowed pointer to the complete Set-Cookie value, or NULL * when none matches */ const char *oauth2_http_response_header_set_cookie_prefix_get( oauth2_log_t *log, oauth2_http_response_t *response, const char *prefix); /** * @brief Add a Set-Cookie header to the response. * * Each cookie set on a response gets its own Set-Cookie header; the * name and value are URL-encoded. * * @param log the log handle to use * @param response the response to add the header to * @param name the name of the cookie * @param value the value of the cookie, or NULL to clear the * cookie on the user agent with an empty value that * expires on the epoch with Max-Age=0 * @param path the Path attribute, or NULL to omit it * @param is_secure when true, the HttpOnly, Secure and SameSite=None * attributes are added * @param max_age the Max-Age attribute in seconds, or * OAUTH2_CFG_TIME_UNSET (cfg.h) to omit it; not used * when clearing * @return true when added, false on error */ bool oauth2_http_response_cookie_set(oauth2_log_t *log, oauth2_http_response_t *response, const char *name, const char *value, const char *path, const bool is_secure, oauth2_time_t max_age); /** * @brief Iterate over the response headers in the order they were set. * @param log the log handle to use * @param response the response whose headers to iterate over * @param callback called for each header with its name and value; * returning false stops the iteration * @param rec passed to the callback unchanged */ void oauth2_http_response_headers_loop(oauth2_log_t *log, const oauth2_http_response_t *response, oauth2_nv_list_loop_cb_t *callback, void *rec); /** @} */ // typedef bool (*oauth2_http_read_post_callback_t)(oauth2_log_t *log, // oauth2_http_request_t *request, char **data); /** * @name Request URL * The ..._url_..._get() functions return the externally visible URL of * the request - scheme://host[:port], that plus the path, and the full * URL including the query string - as derived from the populated * request members and the forwarding headers. All return a newly * allocated string, to be released with oauth2_mem_free(), or NULL on * error. * @{ */ /** * @brief Set the native port the request was received on; 0 means * that there is none, e.g. for a Unix domain socket. * @return true, false when the request is NULL */ bool oauth2_http_request_port_set(oauth2_log_t *log, oauth2_http_request_t *r, unsigned long port); /** * @brief Get the port of the externally visible URL. * * Takes, in this order, the left-most X-Forwarded-Port value, the * ":port" suffix of the left-most X-Forwarded-Host value, the ":port" * suffix of the Host header, no port at all when an X-Forwarded-Proto * header is present (the forwarded scheme's default port applies), and * otherwise the native port unless it is the default port of the * scheme (443 for https, 80 for http). * * @return the port as a newly allocated string, to be released with * oauth2_mem_free(), or NULL when the URL needs no explicit * port */ char *oauth2_http_request_port_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** @brief Get "scheme://host[:port]" of the request. */ char *oauth2_http_request_url_base_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** @brief Get "scheme://host[:port]/path" of the request. */ char *oauth2_http_request_url_path_get(oauth2_log_t *log, const oauth2_http_request_t *request); /** * @brief Get the full request URL, "?query" included when a query * string is set. */ char *oauth2_http_request_url_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** @} */ /** * @name Request headers * Set/unset/add/get incoming request headers, iterate over them, and * convenience getters for common headers. Header names are matched * case-insensitively; the _get functions return a pointer into the * request's own header list, or NULL when the header is absent. * @{ */ /** * @brief The incoming headers: oauth2_http_request_header_set() * replaces the first header of that name, adding it when * absent, _add() appends another one, _unset() removes the * first one and _get() returns the value of the first one. The * set and add functions replace every line feed in the value by * a space, so a value cannot inject headers when the request is * passed on, and return false when the request or the name is * NULL. */ OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, request, header) /** * @brief Iterate over the request headers in the order they were set. * @param log the log handle to use * @param request the request whose headers to iterate over * @param callback called for each header with its name and value; * returning false stops the iteration * @param rec passed to the callback unchanged */ void oauth2_http_request_headers_loop(oauth2_log_t *log, oauth2_http_request_t *request, oauth2_nv_list_loop_cb_t *callback, void *rec); /** @brief Get the Content-Type header. */ const char * oauth2_http_request_header_content_type_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** * @brief Get the raw Cookie header; see oauth2_http_request_cookie_get() * for a single cookie. */ const char * oauth2_http_request_header_cookie_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** @brief Get the Content-Length header. */ const char * oauth2_http_request_header_content_length_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** * @brief Set the Content-Length header from a byte count, e.g. after * rewriting the request body. */ bool oauth2_http_request_header_content_length_set(oauth2_log_t *log, oauth2_http_request_t *r, size_t len); /** @brief Get the X-Requested-With header. */ const char * oauth2_http_request_header_x_requested_with_get(oauth2_log_t *log, const oauth2_http_request_t *r); /** @brief Get the Accept header. */ const char * oauth2_http_request_header_accept_get(oauth2_log_t *log, const oauth2_http_request_t *request); /** * @brief Check whether a script rather than a navigating user issued * the request, in which case a redirect to the authorization * server is pointless. * * True when the X-Requested-With header equals "XMLHttpRequest" * (case-insensitively), or when the Accept header lists neither * text/html, application/xhtml+xml nor the any-type wildcard * (OAUTH2_CONTENT_TYPE_ANY). */ bool oauth2_http_request_is_xml_http_request( oauth2_log_t *log, const oauth2_http_request_t *request); /** * @brief Check whether the request was received over https, taking the * X-Forwarded-Proto header into account like * oauth2_http_request_scheme_get() does; false for a NULL * request. */ bool oauth2_http_request_is_secure(oauth2_log_t *log, const oauth2_http_request_t *request); /** @} */ /** * @name Query and form parameters * Encode name/value lists into query strings or form-encoded data and * access/modify the query parameters of an incoming request. The * request's query string is parsed on first access - split on "&" and * "=", trimmed and URL-decoded - and re-encoded into the query member * after a modification. * @{ */ /** * @brief Append URL-encoded parameters to a URL. * * @param log the log handle to use * @param url the URL to append to, "?" or "&" is inserted depending * on whether it already carries a query string; NULL to * get the encoded parameters alone * @param args the parameters, names and values URL-encoded and joined * with "&"; may be NULL or empty * @return the result as a newly allocated string, to be released with * oauth2_mem_free(), or NULL on error */ char *oauth2_http_url_query_encode(oauth2_log_t *log, const char *url, const oauth2_nv_list_t *args); /** * @brief Encode parameters as application/x-www-form-urlencoded data. * @return the encoded data as a newly allocated string, to be released * with oauth2_mem_free(), or NULL when args is NULL or empty */ char *oauth2_http_url_form_encode(oauth2_log_t *log, const oauth2_nv_list_t *args); /** * @brief Add a query parameter to the request, keeping existing ones * of the same name. * @return true when the query string was updated, false on error */ bool oauth2_http_request_query_param_add(oauth2_log_t *log, oauth2_http_request_t *request, const char *name, const char *value); /** * @brief Get a query parameter of the request. * @return a borrowed pointer to the URL-decoded value of the first * parameter of that name, valid until the query is modified, * or NULL when absent */ const char *oauth2_http_request_query_param_get(oauth2_log_t *log, oauth2_http_request_t *request, const char *name); /** * @brief Remove the first query parameter of a name from the request, * e.g. to strip an access token before passing the request on. * @return true when the query string was updated (also when there was * no such parameter), false on error */ bool oauth2_http_request_query_param_unset(oauth2_log_t *log, oauth2_http_request_t *request, const char *name); /** @} */ /** * @name Outgoing call context * oauth2_http_call_ctx_t holds the per-call settings for an outgoing * HTTP request: a bearer token, content type, outgoing proxy, CA * bundle, TLS client certificate/key, timeout and retry behaviour, * TLS server certificate verification, plus cookies and headers to * send and basic authentication credentials. A new context has a * timeout of 15 seconds, 1 retry after an interval of 300 * milliseconds and TLS verification enabled. * @{ */ /** * @brief Opaque per-call settings with their init and free functions; * the _clone function the declaration macro names is not * implemented. */ OAUTH2_TYPE_DECLARE(http, call_ctx) /** * @brief Send the token in an "Authorization: Bearer" header; NULL is * rejected. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, bearer_token, char *) /** @brief The Content-Type header to send along with a POSTed body. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, content_type, char *) /** @brief The proxy to route the call through, as a libcurl proxy * URL. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, outgoing_proxy, char *) /** * @brief The CA bundle file to verify the server certificate against; * libcurl's default bundle when unset. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ca_info, char *) /** @brief The TLS client certificate file to present. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_cert, char *) /** @brief The private key file belonging to the client certificate. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_key, char *) /** @brief The total time allowed for one attempt, in seconds. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, timeout, int) /** * @brief How often to retry an attempt that failed for a reason other * than a timeout. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, retries, int) /** @brief The time to wait before a retry, in milliseconds. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, retry_interval, int) /** @brief Whether to verify the server's TLS certificate and hostname. */ OAUTH2_TYPE_DECLARE_MEMBER_SET(http, call_ctx, ssl_verify, bool) /** * @brief Cookies to send with the call in a Cookie header, names and * values URL-encoded and joined with "; ". */ OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, cookie) /** * @brief Headers to send with the call, names matched * case-insensitively; the bearer token and content type are * stored here as well. */ OAUTH2_MEMBER_LIST_DECLARE_SET_UNSET_ADD_GET(http, call_ctx, hdr) /** * @brief Authenticate the call with HTTP Basic credentials. * * Credentials set earlier on the context are released and replaced. * * @param log the log handle to use * @param ctx the call context to set the credentials on * @param username the username * @param password the password * @param url_encode when true, both are URL-encoded before use, as * RFC 6749 section 2.3.1 prescribes for a client_id * and client_secret * @return true, false when ctx is NULL */ bool oauth2_http_call_ctx_basic_auth_set(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx, const char *username, const char *password, bool url_encode); /** @} */ /** * @name Outgoing calls * Execute an outgoing HTTP call with libcurl, following up to 5 * redirects and accepting a response body of up to 1 MB (a larger one * fails the call, without a retry). On success * the response body is returned in @p response as a newly allocated * NUL-terminated string, to be released with oauth2_mem_free(), and * the HTTP status code in @p status_code; @p response must not be * NULL, @p status_code may be. Any status code received, 4xx and 5xx * included, makes a successful call; a call fails, returning false, * on a transport error once the configured retries are exhausted, or * right away on a timeout, which reports status code 504. * oauth2_http_call() is the generic form: it POSTs @p data when * non-NULL (with the content type set on the call context) and * performs a plain GET otherwise; the _get/_post_form/_post_json * variants encode a parameter list or JSON object accordingly. A NULL * @p ctx makes the call with libcurl's defaults. * @{ */ /** * @brief Perform a GET or POST call. * * @param log the log handle to use * @param url the URL to call; must not be NULL * @param data the body to POST, or NULL to GET * @param ctx the call settings, or NULL for the defaults * @param response set to the response body; must not be NULL * @param status_code set to the HTTP status code when non-NULL, also * when the call failed (0 when nothing was * received) * @return true when a response was received, false otherwise */ bool oauth2_http_call(oauth2_log_t *log, const char *url, const char *data, oauth2_http_call_ctx_t *ctx, char **response, oauth2_http_status_code_t *status_code); /** @brief GET a URL with @p params appended to its query string. */ bool oauth2_http_get(oauth2_log_t *log, const char *url, const oauth2_nv_list_t *params, oauth2_http_call_ctx_t *ctx, char **response, oauth2_http_status_code_t *status_code); /** * @brief POST @p params as form-encoded data, setting the content type * on @p ctx accordingly. */ bool oauth2_http_post_form(oauth2_log_t *log, const char *url, const oauth2_nv_list_t *params, oauth2_http_call_ctx_t *ctx, char **response, oauth2_http_status_code_t *status_code); /** * @brief POST @p json serialized compactly, setting the content type on * @p ctx to application/json. */ bool oauth2_http_post_json(oauth2_log_t *log, const char *url, const json_t *json, oauth2_http_call_ctx_t *ctx, char **response, oauth2_http_status_code_t *status_code); /** @} */ /** * @name Request cookies * The Cookie header is parsed on first access - split on ";" and "=", * trimmed and URL-decoded - and rewritten from the parsed cookies, * URL-encoded again, after a modification. * @{ */ /** * @brief Get a cookie value from an incoming request. * * @param log the log handle to use * @param r the incoming HTTP request * @param name the name of the cookie * @param strip when true, the cookie is removed from the request's * Cookie header so it is not passed on to the target * application * @return the cookie value as a newly allocated string, to be released * with oauth2_mem_free(), or NULL when not found */ char *oauth2_http_request_cookie_get(oauth2_log_t *log, oauth2_http_request_t *r, const char *name, bool strip); /** * @brief Set a cookie in the request's Cookie header, replacing an * existing cookie of that name, e.g. to pass a value on to the * target application. * @return true when the header was updated, false on error */ bool oauth2_http_request_cookie_set(oauth2_log_t *log, oauth2_http_request_t *r, const char *name, const char *value); /** @} */ /** * @name Outgoing call authentication * Set a TLS client certificate/key or basic authentication credentials * on an outgoing call context. * @{ */ /** * @brief Present a TLS client certificate on the call. * * @param log the log handle to use * @param ssl_cert the certificate file; must not be NULL * @param ssl_key the private key file; must not be NULL * @param ctx the call context to set them on * @return true when set, false when either file is NULL or setting it * failed */ bool oauth2_http_auth_client_cert(oauth2_log_t *log, const char *ssl_cert, const char *ssl_key, oauth2_http_call_ctx_t *ctx); /** * @brief Authenticate the call with HTTP Basic credentials used as-is; * see oauth2_http_call_ctx_basic_auth_set() for URL-encoding * them. * @return always true */ bool oauth2_http_auth_basic(oauth2_log_t *log, const char *username, const char *passwd, oauth2_http_call_ctx_t *ctx); /** @} */ #endif /* _OAUTH2_HTTP_H_ */ liboauth2-2.4.0/include/oauth2/nginx.h0000664000175000017500000005456215250553546020327 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_NGINX_H_ #define _OAUTH2_NGINX_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file nginx.h * @brief The NGINX binding of the library (liboauth2_nginx). * * The glue that NGINX modules built on this library - ngx_oauth2_module, * ngx_sts_module - use to plug the server-agnostic core into NGINX. It * maps an ngx_http_request_t onto the oauth2_http_request_t / * oauth2_http_response_t abstraction (http.h) and the NGINX error log * onto an oauth2_log_sink_t (log.h), publishes the claims of a * verified token as NGINX variables and evaluates "require" * expressions against them, and provides macros that generate the * module table, the directive handlers and the ngx_command_t entries * a module needs. * * The macros assume the module's naming scheme: for a module named * "foo" the NGINX module object is `ngx_foo_module` and the directive * handlers are `ngx_foo_set_`; the per-location * configuration struct they operate on must have an "oauth2_log_t *log" * member. */ #include #include #include #include #include #include #include #include #include /** * @name Module table * @{ */ /** * @brief Generate the module table of a dynamic module. * * Defines the ngx_modules, ngx_module_names and ngx_module_order arrays * that NGINX's build system would otherwise generate in * `objs/ngx__module_modules.c`, listing the single module * `ngx__module` (declared extern by the macro), so that the * module can be built as a shared object outside of the NGINX build * tree. Use it in a source file of its own, e.g. * OAUTH2_NGINX_MODULE(oauth2) in ngx_oauth2_module_modules.c. */ #define OAUTH2_NGINX_MODULE(module) \ extern ngx_module_t ngx_##module##_module; \ \ ngx_module_t *ngx_modules[] = {&ngx_##module##_module, NULL}; \ \ char *ngx_module_names[] = {OAUTH2_TOSTRING(ngx_##module##_module), \ NULL}; \ \ char *ngx_module_order[] = {NULL}; /** @} */ /** * @name Directive handlers * Macros generating the ngx_command_t "set" handler * `ngx__set_()` of a directive - with the * (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) signature - * referenced by name from OAUTH2_NGINX_CMD(). The ARGS variants * forward the directive's * arguments to a config setter of the library with the "return NULL on * success, an error string on failure" convention (cfg.h), the RET1 * variant to a setter operating on a member of the location config. * @{ */ /** * @brief Open a hand-written directive handler. * * Starts the definition of `ngx__set_()` and declares * three locals for the body that follows: "rv" (const char *, the * error string to return, initially NULL), "cfg" (the location config * conf cast to type *) and "value" (the ngx_str_t array of the * directive's arguments, value[0] being the directive name and * cf->args->nelts its length). Close the handler with * OAUTH2_NGINX_CFG_FUNC_END(). */ #define OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ static char *ngx_##module##_set_##primitive( \ ngx_conf_t *cf, ngx_command_t *cmd, void *conf) \ { \ const char *rv = NULL; \ type *cfg = (type *)conf; \ ngx_str_t *value = cf->args->elts; // fprintf(stderr, " ## %s: %p (log=%p)\n", __FUNCTION__, cfg, cf->log); /** * @brief Close a directive handler opened with * OAUTH2_NGINX_CFG_FUNC_START(). * * Logs rv at NGX_LOG_ERR to the configuration log when it is set and * returns NGX_CONF_ERROR then, NGX_CONF_OK otherwise. */ #define OAUTH2_NGINX_CFG_FUNC_END(cf, rv) \ if (rv) \ ngx_log_error(NGX_LOG_ERR, cf->log, 0, rv); \ return rv ? NGX_CONF_ERROR : NGX_CONF_OK; \ } /** * @brief Generate a directive handler calling func(cf, &cfg->member). * * For setters that parse the directive's arguments from the ngx_conf_t * themselves and store into a member of the location config, e.g. * nginx_oauth2_set_require() with an ngx_array_t * member. */ #define OAUTH2_NGINX_CFG_FUNC_RET1(module, type, primitive, func, member) \ OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ (void)value; \ rv = func(cf, &cfg->member); \ OAUTH2_NGINX_CFG_FUNC_END(cf, rv) /** * @brief Generate a directive handler calling func(cfg->log, member, * v1) with the directive's first argument. * * The argument is copied into a NUL-terminated string that is released * after the call; an absent argument is passed as NULL. "member" is any * expression evaluated inside the handler, typically the address of * the config member to set or NULL for global setters such as * oauth2_crypto_passphrase_set(). */ #define OAUTH2_NGINX_CFG_FUNC_ARGS1(module, type, primitive, func, member) \ OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ char *v1 = cf->args->nelts > 1 \ ? oauth2_strndup((const char *)value[1].data, \ (size_t)value[1].len) \ : NULL; \ rv = func(cfg->log, member, v1); \ oauth2_mem_free(v1); \ OAUTH2_NGINX_CFG_FUNC_END(cf, rv) /** * @brief Generate a directive handler calling func(cfg->log, member, * v1, v2) with the directive's first two arguments. * * See OAUTH2_NGINX_CFG_FUNC_ARGS1(); e.g. oauth2_cfg_set_cache() for a * " " cache directive. */ #define OAUTH2_NGINX_CFG_FUNC_ARGS2(module, type, primitive, func, member) \ OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ char *v1 = cf->args->nelts > 1 \ ? oauth2_strndup((const char *)value[1].data, \ (size_t)value[1].len) \ : NULL; \ char *v2 = cf->args->nelts > 2 \ ? oauth2_strndup((const char *)value[2].data, \ (size_t)value[2].len) \ : NULL; \ rv = func(cfg->log, member, v1, v2); \ oauth2_mem_free(v2); \ oauth2_mem_free(v1); \ OAUTH2_NGINX_CFG_FUNC_END(cf, rv) /** * @brief Generate a directive handler calling func(cfg->log, member, * v1, v2, v3) with the directive's first three arguments. * * See OAUTH2_NGINX_CFG_FUNC_ARGS1(). */ #define OAUTH2_NGINX_CFG_FUNC_ARGS3(module, type, primitive, func, member) \ OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ char *v1 = cf->args->nelts > 1 \ ? oauth2_strndup((const char *)value[1].data, \ (size_t)value[1].len) \ : NULL; \ char *v2 = cf->args->nelts > 2 \ ? oauth2_strndup((const char *)value[2].data, \ (size_t)value[2].len) \ : NULL; \ char *v3 = cf->args->nelts > 3 \ ? oauth2_strndup((const char *)value[3].data, \ (size_t)value[3].len) \ : NULL; \ rv = func(cfg->log, member, v1, v2, v3); \ oauth2_mem_free(v3); \ oauth2_mem_free(v2); \ oauth2_mem_free(v1); \ OAUTH2_NGINX_CFG_FUNC_END(cf, rv) /** * @brief Generate a directive handler calling func(cfg->log, member, * v1, v2, v3, v4) with the directive's first four arguments. * * See OAUTH2_NGINX_CFG_FUNC_ARGS1(). */ #define OAUTH2_NGINX_CFG_FUNC_ARGS4(module, type, primitive, func, member) \ OAUTH2_NGINX_CFG_FUNC_START(module, type, primitive) \ char *v1 = cf->args->nelts > 1 \ ? oauth2_strndup((const char *)value[1].data, \ (size_t)value[1].len) \ : NULL; \ char *v2 = cf->args->nelts > 2 \ ? oauth2_strndup((const char *)value[2].data, \ (size_t)value[2].len) \ : NULL; \ char *v3 = cf->args->nelts > 3 \ ? oauth2_strndup((const char *)value[3].data, \ (size_t)value[3].len) \ : NULL; \ char *v4 = cf->args->nelts > 4 \ ? oauth2_strndup((const char *)value[4].data, \ (size_t)value[4].len) \ : NULL; \ rv = func(cfg->log, member, v1, v2, v3, v4); \ oauth2_mem_free(v4); \ oauth2_mem_free(v3); \ oauth2_mem_free(v2); \ oauth2_mem_free(v1); \ OAUTH2_NGINX_CFG_FUNC_END(cf, rv) /** @} */ /** * @name Commands * @{ */ /** * @brief Generate an ngx_command_t entry for a directive. * * Declares the directive as allowed in location and if-in-location * context, handled by `ngx__set_()` (generated with * the OAUTH2_NGINX_CFG_FUNC_* macros or OAUTH2_NGINX_CMD_SET_IMPL()) * on the module's location config. "take" is the digits appended to * NGX_CONF_TAKE to express the accepted argument counts - 1, 12, 123, * ... - and may be OR-ed with further NGX_CONF_TAKE flags for * counts beyond 3, e.g. OAUTH2_NGINX_CMD(3 | NGX_CONF_TAKE4, oauth2, * "OAuth2TokenVerify", token_verify). */ // clang-format off #define OAUTH2_NGINX_CMD(take, module, directive, primitive) \ { \ ngx_string(directive), \ NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | \ NGX_CONF_TAKE##take, \ ngx_##module##_set_##primitive, NGX_HTTP_LOC_CONF_OFFSET, \ 0, NULL \ } // clang-format on /** @} */ /** * @name Logging * @{ */ /** * @brief The log sink callback writing to the NGINX error log. * * An oauth2_log_function_t for oauth2_log_sink_create() whose sink * context is the ngx_log_t to write to; the request context created by * oauth2_nginx_request_context_init() installs it on the request's * connection log. Messages are written as "# : " at the * NGINX level the library level maps to: error, warn, notice, info and * debug map to their namesakes, trace1 to debug as well and trace2 to * stderr. */ void oauth2_nginx_log(oauth2_log_sink_t *sink, const char *filename, unsigned long line, const char *function, oauth2_log_level_t level, const char *msg); /** @} */ /** * @name Request context * @{ */ /** * @brief The per-request state of a module: the log handle, the NGINX * request and its translation into the library's request type. */ typedef struct oauth2_nginx_request_context_t { oauth2_log_t *log; ngx_http_request_t *r; oauth2_http_request_t *request; } oauth2_nginx_request_context_t; /** * @brief Create the request context for an NGINX request. * * Creates a log handle at trace1 level with an oauth2_nginx_log() sink * on the request's connection log, and an oauth2_http_request_t * populated from the request: the scheme ("https" when the connection * is TLS), the hostname and port of the local address (0 for a Unix * domain socket), the path, query string and method (GET, POST, PUT, * DELETE, CONNECT or OPTIONS, anything else is "unknown") and all * request headers. When the $ssl_client_cert variable is set, its * value is stored in the request's context under * OAUTH2_TLS_CERT_VAR_NAME so that mTLS-bound tokens can be verified. * * @param r the NGINX request * @return the new request context, to be released by the caller with * oauth2_nginx_request_context_free() when the request has * been handled */ oauth2_nginx_request_context_t * oauth2_nginx_request_context_init(ngx_http_request_t *r); /** * @brief Release a request context: its request translation, its log * handle and sink, and the context itself. * * @param rec the oauth2_nginx_request_context_t to release, typed as * void * so that it can serve as a pool cleanup handler; * NULL is accepted */ void oauth2_nginx_request_context_free(void *rec); /** * @brief Apply a library response to an NGINX request. * * Appends every header of the response - Set-Cookie headers from * oauth2_http_response_cookie_set() included - to the request's * headers_out and sets its status; the headers are not sent and the * response body is not applied, that is left to the caller. * * @param log the log handle to use * @param response the response to apply * @param r the NGINX request to apply it to * @return the ngx_int_t for the caller's handler to return: NGX_OK for * a 200 status, NGX_HTTP_MOVED_TEMPORARILY for 302, * NGX_HTTP_UNAUTHORIZED for 401 and the status code itself for * anything else; NGX_ERROR when response or r is NULL */ ngx_int_t oauth2_nginx_http_response_set(oauth2_log_t *log, oauth2_http_response_t *response, ngx_http_request_t *r); /** @} */ /** * @name Claims as variables * A module publishes the claims of a verified token as NGINX variables * in two steps: at configuration time a " * $" directive (handled by oauth2_nginx_set_claim(), e.g. * "OAuth2Claim sub $oauth2_sub") registers the variable with * oauth2_nginx_claim_variable() as its get handler, and at request * time the module stores the token's claims with * oauth2_nginx_set_target_variables() after which the variables * resolve to them - for use in proxy_set_header, map, if, etc. The * claims are kept in a hash in the request pool, registered as the * module's request context (ngx_http_set_ctx()). * @{ */ /** * @brief Copy an ngx_str_t into a NUL-terminated string. * * @param p the pool to allocate from * @param str the string to copy * @return the copy, or NULL when the allocation failed */ char *oauth2_nginx_str2chr(ngx_pool_t *p, const ngx_str_t *str); /** * @brief Generate the variable get handler and directive handler of a * claims-to-variables directive for a module. * * Defines `ngx___variable()` and * `ngx__set_()` forwarding to * `oauth2_nginx__variable()` resp. * `oauth2_nginx_set_()` with the module's * `ngx__module` (which must be declared before the macro is * used) filled in; with "claim" as the primitive those are * oauth2_nginx_claim_variable() and oauth2_nginx_set_claim(). * Pair it with `OAUTH2_NGINX_CMD(2, , "", claim)`. */ #define OAUTH2_NGINX_CMD_SET_IMPL(module, primitive) \ static ngx_int_t ngx_##module##_##primitive##_variable( \ ngx_http_request_t *r, ngx_http_variable_value_t *v, \ uintptr_t data) \ { \ return oauth2_nginx_##primitive##_variable( \ ngx_##module##_module, r, v, data); \ } \ \ static char *ngx_##module##_set_##primitive( \ ngx_conf_t *cf, ngx_command_t *cmd, void *conf) \ { \ return oauth2_nginx_set_##primitive( \ ngx_##module##_module, \ ngx_##module##_##primitive##_variable, cf, cmd, conf); \ } /** * @brief The get handler of a claim variable. * * Looks the claim up in the claims hash that * oauth2_nginx_set_target_variables() stored as the module's request * context; the variable is marked not found when no claims were stored * on this request or the claim is absent, and not cacheable otherwise. * * @param module the module owning the request context * @param r the NGINX request * @param v the variable value to fill in * @param data the claim name, as set by oauth2_nginx_set_claim() * @return always NGX_OK */ ngx_int_t oauth2_nginx_claim_variable(ngx_module_t module, ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); /** * @brief Handle a " $" directive. * * Registers the variable (the second argument, which must start with * a "$") as a changeable NGINX variable with handler as its get handler * and the claim name (the first argument) as its data. * * @param module the module the directive belongs to (unused) * @param handler the get handler to register, the one generated by * OAUTH2_NGINX_CMD_SET_IMPL() * @param cf the configuration being parsed * @param cmd the directive being handled * @param conf the location config (unused) * @return NGX_CONF_OK, or an error message allocated from the * configuration pool (NGX_CONF_ERROR when even that fails) * when the variable name is invalid or registering it failed */ char *oauth2_nginx_set_claim(ngx_module_t module, ngx_http_get_variable_pt handler, ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /** * @brief Store the claims of a token for the claim variables. * * Builds a hash of the members of json_token in the request pool - * string values as-is, any other value as its compact JSON encoding - * and registers it as the module's request context, so that the * variables registered with oauth2_nginx_set_claim() resolve for the * remainder of the request. Every key must be stored once: adding a * key that is already in the hash fails. * * @param module the module to register the claims under * @param ctx the request context * @param json_token the JSON object with the claims * @return NGX_OK on success, NGX_ERROR or the ngx_hash error code on * failure (which is logged) */ ngx_int_t oauth2_nginx_set_target_variables(ngx_module_t module, oauth2_nginx_request_context_t *ctx, json_t *json_token); /** @} */ /** * @name Authorization requirements * Authorization expressed in NGINX terms: a "require" directive lists * expressions with variables, typically claim variables or the result * of a "map" over them, and access is granted when each of them * evaluates to "1" on the request. E.g.: * * map $oauth2_sub $valid_sub { default 0; "~^user" 1; } * ... * OAuth2Claim sub $oauth2_sub; * OAuth2Require $valid_sub; * @{ */ /** * @brief Handle a " ..." require directive. * * Compiles each argument as an NGINX complex value (a string with * variables, ngx_http_compile_complex_value()) and appends it to the * requirements array, created in the configuration pool on first use. * Meant as the func of OAUTH2_NGINX_CFG_FUNC_RET1() with an * "ngx_array_t *" member of the location config. * * @param cf the configuration being parsed * @param requirements the array to append to, created when *requirements * is NULL * @return NGX_CONF_OK, or an error message allocated from the * configuration pool (NGX_CONF_ERROR when even that fails) * when an expression does not compile */ char *nginx_oauth2_set_require(ngx_conf_t *cf, ngx_array_t **requirements); /** * @brief Evaluate the requirements on a request. * * Evaluates the expressions in order and stops at the first one that * does not evaluate to exactly "1". * * @param ctx the request context * @param requirements the array built by nginx_oauth2_set_require(), * NULL when no requirements were configured * @return NGX_OK when every requirement is satisfied (or there are * none), NGX_HTTP_UNAUTHORIZED when one is not, NGX_ERROR when * an expression could not be evaluated */ ngx_int_t nginx_oauth2_check_requirements(oauth2_nginx_request_context_t *ctx, ngx_array_t *requirements); /** @} */ #endif /* _OAUTH2_NGINX_H_ */ liboauth2-2.4.0/include/oauth2/log.h0000664000175000017500000002605115250553546017755 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_LOG_H_ #define _OAUTH2_LOG_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file log.h * @brief Logging: log contexts, sinks and the level-checked log macros. * * Every non-trivial function in the library takes an oauth2_log_t as * its first parameter and logs through the oauth2_error() ... * oauth2_trace2() macros, which capture the source file, line and * function of the call site. A log context is a list of sinks: each * sink carries its own level, a callback that emits one formatted * message and an opaque context for that callback - the server_rec or * request_rec to log against in the Apache binding, the ngx_log_t in * the NGINX binding. A message is delivered to every sink whose level * is at or above the level of the message. The oauth2_log_sink_stderr * and oauth2_log_sink_stdout built-in sinks print to the standard * streams. * * A log context is normally obtained from oauth2_init() (util.h), * which also initializes the global state of the library, and * released with oauth2_shutdown(); oauth2_log_init() and * oauth2_log_free() create and free a bare context, as the server * bindings do for each request. */ /** * @brief Storage class for the data symbols that cross the library * boundary. * * A Windows DLL exports data only when its declaration says so, and a * consumer resolves that data only through __declspec(dllimport); * functions need neither, the linker binds them through the import * library. OAUTH2_EXPORTS is defined when compiling liboauth2 itself, * OAUTH2_STATIC when its objects are linked in directly. Everywhere * else this is a plain extern. */ #if defined(_WIN32) && !defined(OAUTH2_STATIC) #ifdef OAUTH2_EXPORTS #define OAUTH2_EXTERN __declspec(dllexport) extern #else #define OAUTH2_EXTERN __declspec(dllimport) extern #endif #else #define OAUTH2_EXTERN extern #endif /** * @brief Log levels, from most to least severe. * * A sink at a given level emits the messages at that level and the * more severe ones, so a sink at OAUTH2_LOG_WARN emits errors and * warnings only. * * @note Do not change the order or the values of these without * checking the consequences in log.c and in the Apache and NGINX * bindings: they map the levels to their names and to the * native log levels by array index. */ typedef enum oauth2_log_level_t { OAUTH2_LOG_ERROR, /**< an operation failed */ OAUTH2_LOG_WARN, /**< something is off but the operation went on */ OAUTH2_LOG_NOTICE, /**< a normal but significant event */ OAUTH2_LOG_INFO, /**< informational */ OAUTH2_LOG_DEBUG, /**< debugging: function entry and exit, decisions */ OAUTH2_LOG_TRACE1, /**< detailed tracing: intermediate values */ OAUTH2_LOG_TRACE2 /**< very detailed tracing: payloads, wire data */ } oauth2_log_level_t; /** * @name Log macros * The macros the library and its users log through. They are * printf-style, take the log context as their first argument, capture * the source file, line and function of the call site and expand to * _oauth2_log(), which by default calls oauth2_log(). A NULL log * context is accepted and logs nothing. * @{ */ /** * @brief The hook the log macros expand to. * * Defined to call oauth2_log() unless a definition is already in place * when this header is included: a user of the library can define it * beforehand to route the messages elsewhere - e.g. straight into the * native log call of a server, with the level mapped - or to a no-op * to compile the logging out for speed. */ #ifndef _oauth2_log #define _oauth2_log(log, level, fmt, ...) \ oauth2_log(log, __FILE__, __LINE__, __FUNCTION__, level, fmt, \ ##__VA_ARGS__) #endif /** @brief Log a message at OAUTH2_LOG_ERROR. */ #define oauth2_error(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_ERROR, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_WARN. */ #define oauth2_warn(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_WARN, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_NOTICE. */ #define oauth2_notice(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_NOTICE, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_INFO. */ #define oauth2_info(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_INFO, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_DEBUG. */ #define oauth2_debug(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_DEBUG, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_TRACE1. */ #define oauth2_trace1(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_TRACE1, fmt, ##__VA_ARGS__) /** @brief Log a message at OAUTH2_LOG_TRACE2. */ #define oauth2_trace2(log, fmt, ...) \ _oauth2_log(log, OAUTH2_LOG_TRACE2, fmt, ##__VA_ARGS__) /** @} */ /** * @name Log context and sinks * @{ */ /** @brief Opaque log context: the list of sinks messages go to. */ typedef struct oauth2_log_t oauth2_log_t; /** * @brief Opaque log sink: a level, a callback that emits the messages * and an opaque context for that callback. */ typedef struct oauth2_log_sink_t oauth2_log_sink_t; /** * @brief Sink callback, invoked for each message that passes the level * of the sink. * * @param sink the sink the message is delivered to; the context it * was created with is available through * oauth2_log_sink_ctx_get() * @param filename the source file of the log call * @param line the source line of the log call * @param function the function the log call was made from * @param level the level of the message * @param msg the formatted message, valid for the duration of the * call only */ typedef void (*oauth2_log_function_t)(oauth2_log_sink_t *sink, const char *filename, unsigned long line, const char *function, oauth2_log_level_t level, const char *msg); /** * @brief Built-in sink printing to stderr, at level OAUTH2_LOG_INFO * initially. * * Each line reads "[file:line:function:LVL] message". The built-in * sinks are static objects shared by every log context they are added * to: oauth2_log_sink_level_set() on one affects all of them, and * oauth2_log_free() does not free them. */ OAUTH2_EXTERN oauth2_log_sink_t oauth2_log_sink_stderr; /** * @brief Built-in sink printing to stdout, at level OAUTH2_LOG_INFO * initially; see oauth2_log_sink_stderr. */ OAUTH2_EXTERN oauth2_log_sink_t oauth2_log_sink_stdout; /** * @brief Format a message and deliver it to the sinks of a log * context. * * Normally reached through the oauth2_error() ... oauth2_trace2() * macros rather than called directly. The message is formatted once * and handed to every sink whose level is at or above @p level, in the * order the sinks were added. * * @param log the log context; NULL logs nothing * @param filename the source file to attribute the message to * @param line the source line to attribute the message to * @param function the function to attribute the message to * @param level the level of the message * @param fmt printf-style format string, followed by its * arguments; NULL logs nothing */ void oauth2_log(oauth2_log_t *log, const char *filename, unsigned long line, const char *function, oauth2_log_level_t level, const char *fmt, ...); /** * @brief Create a sink. * * @param level the sink emits messages at this level and the more * severe ones * @param callback the function that emits each message * @param ctx an opaque context for the callback, e.g. the server * or request record to log against, retrievable with * oauth2_log_sink_ctx_get() * @return the newly allocated sink; hand it to oauth2_log_init() or * oauth2_log_sink_add(), after which the log context owns it * and oauth2_log_free() releases it */ oauth2_log_sink_t *oauth2_log_sink_create(oauth2_log_level_t level, oauth2_log_function_t callback, void *ctx); /** @brief Get the context a sink was created with. */ void *oauth2_log_sink_ctx_get(oauth2_log_sink_t *sink); /** @brief Get the callback a sink was created with. */ oauth2_log_function_t oauth2_log_sink_callback_get(oauth2_log_sink_t *sink); /** * @brief Add a sink to a log context. * * The log context takes ownership: oauth2_log_free() frees every sink * added to it, except the built-in oauth2_log_sink_stderr and * oauth2_log_sink_stdout. * * @param log the log context to add the sink to * @param add the sink to add */ void oauth2_log_sink_add(oauth2_log_t *log, oauth2_log_sink_t *add); /** * @brief Change the level of a sink. * * Messages less severe than @p level are no longer delivered to the * sink. The Apache binding uses this to apply the configured LogLevel * of a virtual host once the configuration has been read. * * @param sink the sink to change * @param level the new level of the sink */ void oauth2_log_sink_level_set(oauth2_log_sink_t *sink, oauth2_log_level_t level); /** @} */ /** * @name Log context lifecycle * Prefer oauth2_init() and oauth2_shutdown() (util.h), which wrap * these and also initialize and clean up the global state of the * library (OpenSSL, libcurl, the caches); use the functions below for * additional, short-lived contexts such as the per-request ones the * server bindings create. * @{ */ /** * @brief Create a log context with a single sink. * * @param level the level to set on @p sink * @param sink the initial sink, or NULL for oauth2_log_sink_stderr; * its level is overwritten with @p level, also when it is * one of the shared built-in sinks * @return the newly allocated log context, to be released with * oauth2_log_free() */ oauth2_log_t *oauth2_log_init(oauth2_log_level_t level, oauth2_log_sink_t *sink); /** * @brief Free a log context and the sinks it owns. * * Frees every sink added to the context, except the built-in * oauth2_log_sink_stderr and oauth2_log_sink_stdout; a NULL context is * ignored. */ void oauth2_log_free(oauth2_log_t *); /** @} */ #endif /* _OAUTH2_LOG_H_ */ liboauth2-2.4.0/include/oauth2/ipc.h0000664000175000017500000002277115250553546017754 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_IPC_H_ #define _OAUTH2_IPC_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file ipc.h * @brief Inter-process synchronization and shared memory. * * The primitives the cache backends (cache.h) use to coordinate the * worker processes of a pre-forking server such as Apache httpd: a * process-shared mutex, a process-shared counting semaphore and an * anonymous shared memory segment, plus an in-process thread mutex. * Their lifecycle follows that of the server: the object is allocated * with its _init function while the configuration is read, the * underlying OS object is created with its _post_config function in * the parent process once the configuration is complete and before * the workers are forked - so that the workers inherit it - and the * workers then use it; _free releases it. Any function here accepts a * NULL object and fails (or does nothing) on it. * * On POSIX the semaphores are named semaphores that are unlinked right * after their creation and the shared memory is an anonymous * MAP_SHARED mapping, both inherited across fork(). On Windows, where * nothing forks, they are implemented on unnamed kernel semaphores, * critical sections and process heap memory and coordinate the threads * of a single process. */ #include #include "oauth2/util.h" // TODO: macro for post_config and child_init functions /** * @name Process-shared mutex * A binary semaphore in mutex clothing: oauth2_ipc_mutex_init() * allocates it, oauth2_ipc_mutex_post_config() creates the underlying * semaphore in the unlocked state - call it in the parent before * forking - and the workers serialize on it with * oauth2_ipc_mutex_lock() and oauth2_ipc_mutex_unlock(). It is not * recursive and has no notion of an owner: locking it twice from the * same thread deadlocks, and any process or thread can unlock it. * The file cache backend serializes its directory operations on one, * the shared memory cache backend its access to the segment. * @{ */ /** * @brief Opaque process-shared mutex; the _clone function the * declaration macro names is not implemented. */ OAUTH2_TYPE_DECLARE(ipc, mutex) /** * @brief Create the underlying semaphore, in the unlocked state. * * Call once in the parent process, after the configuration has been * read and before the workers are forked. * * @param log the log handle to use * @param m the mutex to create the semaphore for * @return true on success, false when the semaphore could not be * created */ bool oauth2_ipc_mutex_post_config(oauth2_log_t *log, oauth2_ipc_mutex_t *m); /** * @brief Acquire the mutex, blocking until it is available. * * @param log the log handle to use * @param m the mutex to acquire * @return true when the mutex was acquired, false on error */ bool oauth2_ipc_mutex_lock(oauth2_log_t *log, oauth2_ipc_mutex_t *m); /** * @brief Release the mutex. * * @param log the log handle to use * @param m the mutex to release * @return true when the mutex was released, false on error */ bool oauth2_ipc_mutex_unlock(oauth2_log_t *log, oauth2_ipc_mutex_t *m); /** @} */ /** * @name Thread mutex * An in-process mutex - a pthread mutex, a critical section on Windows * - serializing the threads of one process. It is ready for use right * after oauth2_ipc_thread_mutex_init(), there is no post_config step, * and oauth2_ipc_thread_mutex_free() destroys it. The library * serializes its use of the shared libcurl handle on one, the Redis * cache backend the use of its connection. * @{ */ /** * @brief Opaque thread mutex; the _clone function the declaration * macro names is not implemented. */ OAUTH2_TYPE_DECLARE(ipc, thread_mutex) /** * @brief Acquire the mutex, blocking until it is available. * * @param log the log handle to use * @param m the mutex to acquire * @return true when the mutex was acquired, false on error */ bool oauth2_ipc_thread_mutex_lock(oauth2_log_t *log, oauth2_ipc_thread_mutex_t *m); /** * @brief Release the mutex. * * @param log the log handle to use * @param m the mutex to release * @return true when the mutex was released, false on error */ bool oauth2_ipc_thread_mutex_unlock(oauth2_log_t *log, oauth2_ipc_thread_mutex_t *m); /** @} */ /** * @name Process-shared semaphore * A counting semaphore shared with the processes forked after * oauth2_ipc_sema_post_config() created it, starting at a count of * zero: oauth2_ipc_sema_post() increments it and oauth2_ipc_sema_wait() * and oauth2_ipc_sema_trywait() decrement it, the former blocking * while the count is zero. The process-shared mutex and the shared * memory segment are built on it. * @{ */ /** * @brief Opaque process-shared semaphore; the _clone function the * declaration macro names is not implemented. */ OAUTH2_TYPE_DECLARE(ipc, sema) /** * @brief Create the underlying semaphore, with a count of zero. * * Call once in the parent process before the workers are forked. On * POSIX the semaphore is created under a name derived from the process * id and the address of @p sema and unlinked immediately, so that it * lives on only through the handle the workers inherit. * * @param log the log handle to use * @param sema the semaphore to create * @return true on success, false when the semaphore could not be * created */ bool oauth2_ipc_sema_post_config(oauth2_log_t *log, oauth2_ipc_sema_t *sema); /** * @brief Increment the semaphore, waking one waiter if there is any. * * @param log the log handle to use * @param sema the semaphore to increment * @return true on success, false when the semaphore has not been * created with oauth2_ipc_sema_post_config() or the increment * failed */ bool oauth2_ipc_sema_post(oauth2_log_t *log, oauth2_ipc_sema_t *sema); /** * @brief Decrement the semaphore, blocking while its count is zero. * * @param log the log handle to use * @param sema the semaphore to decrement * @return true when the semaphore was decremented, false on error */ bool oauth2_ipc_sema_wait(oauth2_log_t *log, oauth2_ipc_sema_t *sema); /** * @brief Decrement the semaphore if its count is positive, without * blocking. * * @param log the log handle to use * @param sema the semaphore to decrement * @return true when the semaphore was decremented, false when its * count was zero or on error */ bool oauth2_ipc_sema_trywait(oauth2_log_t *log, oauth2_ipc_sema_t *sema); /** @} */ /** * @name Shared memory * A fixed-size block of memory shared between the parent and the * worker processes: oauth2_ipc_shm_init() records the size, * oauth2_ipc_shm_post_config() maps the block in the parent - zero- * filled, before the workers are forked so that they inherit the * mapping - oauth2_ipc_shm_child_init() is called in each worker when * it starts and oauth2_ipc_shm_get() returns the address of the block. * Access to the contents is not synchronized: pair the segment with a * process-shared mutex, as the shared memory cache backend does. * @{ */ /** @brief Opaque shared memory segment. */ typedef struct oauth2_ipc_shm_t oauth2_ipc_shm_t; /** * @brief Allocate a shared memory segment descriptor. * * @param log the log handle to use * @param size the size of the segment in bytes, mapped by * oauth2_ipc_shm_post_config() * @return the new descriptor, to be released with oauth2_ipc_shm_free() */ oauth2_ipc_shm_t *oauth2_ipc_shm_init(oauth2_log_t *log, size_t size); /** * @brief Unmap the segment, if mapped, and free its descriptor. */ void oauth2_ipc_shm_free(oauth2_log_t *, oauth2_ipc_shm_t *); /** * @brief Map the segment in the parent process. * * Creates the internal semaphores of the segment and maps the number * of bytes passed to oauth2_ipc_shm_init() as zero-filled anonymous * shared memory (process heap memory on Windows). Call once, before * the workers are forked. * * @param log the log handle to use * @param shm the segment to map * @return true on success, false when the semaphores or the mapping * could not be created */ bool oauth2_ipc_shm_post_config(oauth2_log_t *log, oauth2_ipc_shm_t *shm); /** * @brief Register a worker process with the segment. * * Call in each worker when it starts, after it was forked from the * parent that mapped the segment. * * @param log the log handle to use * @param shm the segment the worker attaches to * @return true on success, false on error */ bool oauth2_ipc_shm_child_init(oauth2_log_t *log, oauth2_ipc_shm_t *shm); /** * @brief Get the address of the mapped segment. * * @param log the log handle to use * @param s the segment * @return the address of the block, or NULL before * oauth2_ipc_shm_post_config() mapped it */ void *oauth2_ipc_shm_get(oauth2_log_t *log, oauth2_ipc_shm_t *s); /** @} */ #endif /* _OAUTH2_IPC_H_ */ liboauth2-2.4.0/include/oauth2/proto.h0000664000175000017500000001567715250553546020353 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_PROTO_H_ #define _OAUTH2_PROTO_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file proto.h * @brief OAuth 2.x protocol exchanges: source token retrieval and the * Resource Owner Password Credentials and Client Credentials * grants. * * Two halves of what a Resource Server or a token-obtaining client * does on the wire. On the incoming side, oauth2_get_source_token() * picks the access token a client presented on a request out of the * location(s) configured in an oauth2_cfg_source_token_t (cfg.h), for * verification with oauth2_token_verify() (oauth2.h). On the outgoing * side, oauth2_ropc_exec() and oauth2_cc_exec() obtain an access token * from a token endpoint configured in an oauth2_cfg_ropc_t or * oauth2_cfg_cc_t (cfg.h), using the HTTP client in http.h and the * endpoint's client authentication method (oauth2_http_ctx_auth_add(), * oauth2.h). */ #include "oauth2/cfg.h" #include "oauth2/http.h" /** * @name Source token retrieval * @{ */ /** * @brief Retrieve the access token presented on an incoming request. * * Tries the locations enabled in the configuration's accept mask * (oauth2_cfg_source_token_get_accept_in(), by default the environment * variable and the header) in a fixed order and returns the first * token found: * - "environment": the environment variable named by the location's * "name" option (default "access_token"), read through the server * binding's oauth2_cfg_env_get_cb callback; * - "header": the header named by "name" (default "Authorization"), * whose value must start with the scheme in "type" (default * "bearer", matched case-insensitively; an empty "type" accepts the * whole header value); * - "query": the query parameter named by "name" (default * "access_token"); * - "post": the form parameter named by "name" (default * "access_token") of a form-encoded POST, read through the server * binding's oauth2_cfg_form_post_read_cb callback; * - "cookie": the cookie named by "name" (default "access_token"); * - "basic": the password part of an "Authorization: Basic" * credential. * * When the configuration's "strip" flag is set (the default), the * location the token was taken from is cleared - header, query * parameter, cookie or environment variable - so it is not passed on * to the target application; stripping a POST parameter is not * supported and only logs a warning. * * @param log the log handle to use * @param cfg the source token configuration: accepted * locations, their names and the strip flag * @param request the incoming HTTP request, modified in place when * the token is stripped * @param srv_cb the server binding's callbacks, used for the * "environment" and "post" locations * @param srv_cb_ctx the context passed to the callbacks (e.g. the * binding's request context) * @return the token as a newly allocated string, to be released with * oauth2_mem_free(), or NULL when none of the enabled * locations carries one */ char *oauth2_get_source_token(oauth2_log_t *log, oauth2_cfg_source_token_t *cfg, oauth2_http_request_t *request, oauth2_cfg_server_callback_funcs_t *srv_cb, void *srv_cb_ctx); /** @} */ /** * @name Token endpoint grants * Both exchanges POST a form-encoded request to the configured token * endpoint with the endpoint's SSL verification, timeout, retry and * outgoing proxy settings applied, authenticate to it with the * endpoint's client authentication method - adding "client_id" as a * plain parameter only when that method is "none" and a client_id is * configured - merge in the configured extra request parameters and * require a 2xx JSON response carrying an "access_token" member. The * token type, expiry and any refresh token in the response are * ignored, and results are not cached. * @{ */ /** * @brief Obtain an access token with the Resource Owner Password * Credentials grant (RFC 6749 section 4.3). * * Sends "grant_type=password" with the "username" (omitted when NULL) * and "password" parameters. * * @param log the log handle to use * @param cfg the ROPC configuration (token endpoint, client_id * and extra request parameters); the username and * password stored in it are not used, pass them * explicitly * @param username the resource owner's username, may be NULL * @param password the resource owner's password * @param rtoken on success, set to the access token as a newly * allocated string, to be released with * oauth2_mem_free(); untouched on failure * @param status_code set to the HTTP status code of the token endpoint * response once one was received, also on failure * @return true when an access token was obtained, false on a * transport error, a non-2xx response, an error response or a * response without an access token */ bool oauth2_ropc_exec(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg, const char *username, const char *password, char **rtoken, oauth2_uint_t *status_code); /** * @brief Obtain an access token with the Client Credentials grant * (RFC 6749 section 4.4). * * Sends "grant_type=client_credentials"; the client is identified * solely by the endpoint's authentication method (or the configured * client_id when that method is "none"). * * @param log the log handle to use * @param cfg the Client Credentials configuration (token * endpoint, client_id and extra request parameters) * @param rtoken on success, set to the access token as a newly * allocated string, to be released with * oauth2_mem_free(); untouched on failure * @param status_code set to the HTTP status code of the token endpoint * response once one was received, also on failure * @return true when an access token was obtained, false otherwise */ bool oauth2_cc_exec(oauth2_log_t *log, oauth2_cfg_cc_t *cfg, char **rtoken, oauth2_uint_t *status_code); /** @} */ #endif /* _OAUTH2_PROTO_H_ */ liboauth2-2.4.0/include/oauth2/jq.h0000664000175000017500000000654515250553546017614 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_JQ_H #define _OAUTH2_JQ_H /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file jq.h * @brief Claim filtering and transformation with jq expressions. * * Runs a jq filter (https://jqlang.github.io/jq/) over a JSON * document, typically the claims of a verified token, to select, * reshape or rename them before they are passed on to the target * application. Only available when the library was built against * libjq (configure --with-jq): this header is installed and its * functions implemented only then, and the library's pkg-config * cflags define OAUTH2_WITH_JQ so that consumers can test for it. */ #include "oauth2/cache.h" #include "oauth2/log.h" /** @brief Opaque libjq program state, declared here to avoid jq.h. */ typedef struct jq_state jq_state; /** * @brief Compile a jq filter expression. * * Useful to validate a filter at configuration time. * * @param log the log handle to use * @param filter the jq filter expression to compile * @param r_jq when non-NULL, set to the compiled program, to be * released by the caller with jq_teardown(); when NULL * the program is compiled for validation only and * released internally * @return true when the filter compiled, false on error */ bool oauth2_jq_filter_compile(oauth2_log_t *log, const char *filter, jq_state **r_jq); /** * @brief Apply a jq filter to a JSON document. * * Parses @p input, runs the compiled @p filter over it and returns * the last value the filter produced, serialized as compact JSON. * When a cache is passed, the result is looked up in and stored to * it under a SHA-256 hash of the input and filter, with a fixed * time-to-live of 600 seconds. * * @param log the log handle to use * @param cache the cache to memoize results in, or NULL to run the * filter every time * @param input the JSON document to filter; NULL is treated as an * empty object * @param filter the jq filter expression * @param result on success, set to the filter output as a newly * allocated string, to be released with * oauth2_mem_free(), or to NULL when the filter produced * no output (e.g. "empty" or a parse error in the * input) * @return true when the filter was applied (or served from the * cache), false when @p filter is NULL, does not compile or * the cache key could not be computed */ bool oauth2_jq_filter(oauth2_log_t *log, oauth2_cache_t *cache, const char *input, const char *filter, char **result); #endif /* _OAUTH2_JQ_H */ liboauth2-2.4.0/include/oauth2/openidc.h0000664000175000017500000005710415250553546020620 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_OPENIDC_H_ #define _OAUTH2_OPENIDC_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file openidc.h * @brief OpenID Connect Relying Party (client) flow. * * Implements the OpenID Connect Authorization Code flow for web servers: * oauth2_openidc_handle() dispatches an incoming HTTP request against a * location-based configuration (oauth2_cfg_openidc_t) - serving the * redirect URI, initiating an authentication request for unauthenticated * requests per the configured oauth2_unauth_action_t, and returning the * claims associated with an existing session - backed by a cookie-based * session store (see session.h). */ #include "oauth2/http.h" #include "oauth2/oauth2.h" #include "oauth2/util.h" /** * @name Token response members * The members of the token endpoint response (OpenID Connect Core 1.0 * section 3.1.3.3) that the flow extracts. * @{ */ /** @brief The "id_token" member of the token response. */ #define OAUTH2_OPENIDC_ID_TOKEN "id_token" /** @brief The "access_token" member of the token response. */ #define OAUTH2_OPENIDC_ACCESS_TOKEN "access_token" /** @} */ /** * @brief The session configuration type of session.h, declared here * for the session member of oauth2_cfg_openidc_t. * * Only the `_init` and `_free` functions of the declaration are * implemented; `_clone` and `_merge` are not. */ OAUTH2_CFG_TYPE_DECLARE(cfg, session) /** * @brief The provider resolver: the callback that produces the JSON * provider document for an incoming request, its type-specific * context and the cache resolved documents are kept in. * * Created by oauth2_cfg_openidc_provider_resolver_set_options(); the * `_merge` function of the declaration is not implemented. */ OAUTH2_CFG_TYPE_DECLARE(cfg, openidc_provider_resolver) /** * @name Location-based OpenID Connect configuration * oauth2_cfg_openidc_t holds the OpenID Connect RP settings for a * server location/path: the handler path serving the internal endpoints * (the redirect URI lives at "/redirect_uri" by default), * the redirect URI itself (absolute, or a path resolved against the * incoming request's base URL), the action applied to unauthenticated * requests, the state cookie settings that protect the authentication * round-trip to the provider, and the associated session configuration. * Populate it directly through the accessors below or with the option * string parser oauth2_cfg_openidc_set_options(). * @{ */ /** * @brief The location-based configuration object. * * `_init` leaves every setting unset so that the getters return the * defaults listed below; `_clone` deep-copies the resolver and client * and shares the session pointer; `_merge` (for per-location * inheritance in the server bindings) takes each setting from the * overriding (add) configuration when it is set there and from the * base configuration otherwise. */ OAUTH2_CFG_TYPE_DECLARE(cfg, openidc) /** * @brief The path under which the RP serves its internal endpoints; * the redirect URI is "/redirect_uri" unless * configured explicitly. Default "/openid-connect"; option * "handler_path". */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, handler_path, char *) /** * @brief The redirect URI registered with the provider: an absolute * URL, or a path (starting with "/") that is resolved against * the base URL of each incoming request. Unset by default, so * that "/redirect_uri" is used; option * "redirect_uri". Read it, resolved for a request, with * oauth2_cfg_openidc_redirect_uri_get(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET(cfg, openidc, redirect_uri, char *) /** * @brief What oauth2_openidc_handle() does with a request that carries * no authenticated session, see oauth2_unauth_action_t * (oauth2.h). Default OAUTH2_UNAUTH_ACTION_UNDEFINED, which * authenticates; option "unauth_action". */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, unauth_action, oauth2_unauth_action_t) /** * @brief The session configuration (session.h) that the sessions * established by the flow are loaded from and saved to. Stored * by reference, not owned: it is one of the registered session * configurations, selected by the "session" option of * oauth2_cfg_openidc_provider_resolver_set_options() and * oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, session, oauth2_cfg_session_t *) /** * @brief The prefix of the state cookie names: a state cookie is named * after the prefix followed by the "state" value of the * authentication request it belongs to. Default * "openidc_state_"; option "state.cookie.name.prefix". */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_name_prefix, char *) /** * @brief The number of seconds an authentication round-trip may take: * the state cookie's max age and the age after which the state * it carries is rejected and the cookie is cleaned up. Default * 300; option "state.cookie.timeout". */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_timeout, oauth2_time_t) /** * @brief The maximum number of valid state cookies, i.e. outstanding * authentication round-trips, per browser. Once reached, a new * authentication request fails unless * state_cookie_delete_oldest is set. Default 6; option * "state.cookie.max". */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(cfg, openidc, state_cookie_max, oauth2_uint_t) /** * @brief Whether to clear the oldest state cookie(s) to make room for * a new authentication request when state_cookie_max is * reached, rather than failing it. Default false; only settable * through the "state.cookie.delete.oldest" option. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, openidc, state_cookie_delete_oldest, oauth2_flag_t) /** * @brief Obtain the absolute redirect URI for an incoming request. * * Returns the configured redirect URI as-is when it is absolute; * resolves it against the base URL of the incoming request when it is a * path; defaults to "/redirect_uri" under the request's * base URL when none was configured. * * Takes the log handle, the location-based configuration and the * incoming HTTP request whose base URL (scheme, host and port) a * relative redirect URI is resolved against. * * @return the redirect URI as a newly allocated string, to be released * with oauth2_mem_free(), or NULL on error */ char *oauth2_cfg_openidc_redirect_uri_get(oauth2_log_t *, const oauth2_cfg_openidc_t *, const oauth2_http_request_t *); /** @} */ /** * @name OpenID Connect protocol state * oauth2_openidc_proto_state_t is a name/value object holding the * per-authentication-request state - issuer, nonce, PKCE code verifier, * original target URL, request method, timestamp - that is serialized * into an encrypted state cookie while the user's browser round-trips * to the provider's authorization endpoint, and validated against the * authorization response on the redirect URI. * @{ */ /** * @brief The protocol state object: a JSON object of name/value pairs. * `_init` creates an empty one, `_clone` deep-copies the pairs * and `_free` releases it. */ OAUTH2_TYPE_DECLARE(openidc, proto_state) /** * @brief Store a string value in the state, replacing any value stored * under the same name. * * @param log the log handle to use * @param p the protocol state * @param name the name to store the value under * @param value the string value, copied * @return true */ bool oauth2_openidc_proto_state_set(oauth2_log_t *log, oauth2_openidc_proto_state_t *p, const char *name, const char *value); /** * @brief Store an integer value in the state, replacing any value * stored under the same name. * * @param log the log handle to use * @param p the protocol state * @param name the name to store the value under * @param value the integer value * @return true */ bool oauth2_openidc_proto_state_set_int(oauth2_log_t *log, oauth2_openidc_proto_state_t *p, const char *name, const json_int_t value); /** * @brief Get the JSON object holding the state. * * @param p the protocol state * @return a borrowed pointer to the JSON object, owned by the state * and valid until it is modified or freed */ json_t * oauth2_openidc_proto_state_json_get(const oauth2_openidc_proto_state_t *p); /** @} */ /** * @name OpenID Connect provider configuration * oauth2_openidc_provider_t holds the settings of the OpenID Connect * provider (OP): the issuer and its endpoints. It is obtained at * runtime through the configured provider resolver, which produces the * provider settings for an incoming request from a JSON document - a * static string ("string"), a local file ("file") or a URL ("url", * e.g. the provider's Discovery document), cached where applicable. * @{ */ /** * @brief The provider object, populated from the members of the same * name in the provider's JSON document. Only `_init` and * `_free` are implemented; `_clone` is not. */ OAUTH2_TYPE_DECLARE(openidc, provider) /** * @brief The provider's issuer identifier ("issuer"), the one member * the provider document must carry. The "iss" claim of the * id_token must equal it, and it is the key under which the * resolved provider document is cached. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, issuer, char *) /** * @brief The URL of the authorization endpoint * ("authorization_endpoint") the user agent is redirected to * with the authentication request. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, authorization_endpoint, char *) /** * @brief The URL of the token endpoint ("token_endpoint") the * authorization code is exchanged at. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, token_endpoint, char *) /** * @brief The URL of the userinfo endpoint ("userinfo_endpoint") the * userinfo claims are retrieved from with the access token; no * userinfo request is made when it is absent. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, userinfo_endpoint, char *) /** * @brief The URL of the provider's JSON Web Key Set ("jwks_uri") the * id_token signature is verified against. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, provider, jwks_uri, char *) /** * @brief Install a provider resolver on a configuration. * * The configuration takes ownership of the resolver and releases it * with oauth2_cfg_openidc_free(); a different resolver already * installed is released first. * * @param log the log handle to use * @param cfg the location-based configuration * @param resolver the resolver to install * @return true, false when cfg is NULL */ bool oauth2_cfg_openidc_provider_resolver_set( oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, oauth2_cfg_openidc_provider_resolver_t *resolver); /** * @brief Get the provider resolver of a configuration. * * @param log the log handle to use * @param cfg the location-based configuration * @return a borrowed pointer to the resolver, or NULL when @p cfg is * NULL or no resolver was configured */ oauth2_cfg_openidc_provider_resolver_t * oauth2_cfg_openidc_provider_resolver_get(oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg); /** * @brief Configure the provider resolver from an option string. * * Replaces the resolver configured before and selects the session * configuration of the location. Documents produced by a "file" or * "url" resolver are cached under their issuer for 24 hours when a * cache is configured; a resolved document must carry an "issuer" * member. * * @param log the log handle to use * @param cfg the OpenID Connect configuration to set the resolver * on * @param type resolver type: "string" (inline JSON provider * document), "file" (path to a JSON file, default * "conf/provider.json") or "url" (URL serving the JSON * document, e.g. the Discovery endpoint); "dir" is * accepted but not implemented * @param value the JSON document, filename or URL, per @p type * @param options form-encoded parameters: "session" names the * registered session configuration (session.h) to use, * the default one when absent; "cache" names the cache * (cache.h) that stores resolved documents, for the * "file" and "url" types; for "url" also the endpoint * settings of oauth2_cfg_set_endpoint() (cfg.h): * "ssl_verify", "http_timeout", "http_retries", * "http_retry_interval" and "outgoing_proxy" * @return NULL on success, an error string on failure */ char *oauth2_cfg_openidc_provider_resolver_set_options( oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *type, const char *value, const char *options); /** @} */ /** * @name OpenID Connect client configuration * oauth2_openidc_client_t holds the RP's own registration settings: * client identifier and credentials, the scope(s) to request, extra * authorization request parameters, the token endpoint authentication * method (see oauth2_cfg_endpoint_auth_t in cfg.h) and the outgoing * HTTP call settings. Populate it through the accessors below or with * oauth2_openidc_client_set_options(). * @{ */ /** * @brief The client object. `_init` leaves every setting unset, * `_clone` deep-copies it including the token endpoint * authentication and `_free` releases it. */ OAUTH2_TYPE_DECLARE(openidc, client) /** * @brief The "scope" parameter of the authentication request, e.g. * "openid profile email"; the getter returns "openid" when * unset. Set by the "scope" parameter or member of * oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, scope, char *) /** * @brief Extra authentication request parameters as a form-encoded * string, e.g. "acr_values=mfa&prompt=login", merged into the * authentication request. Set by the "authn_request_params" * parameter or member of oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, authn_request_params, char *) /** * @brief The client identifier: sent as "client_id" in the * authentication request and required in the "aud" claim of * the id_token. Set by the "client_id" parameter or member of * oauth2_openidc_client_set_options(), which requires it for * the "json" and "file" types. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, client_id, char *) /** * @brief The client secret, for the token endpoint authentication * methods that use one. Set by the "client_secret" parameter or * member of oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, client_secret, char *) /** * @brief The client authentication method and credentials for the * token endpoint, applied to the code exchange with * oauth2_http_ctx_auth_add() (oauth2.h), which fails when it is * NULL: configure "none" for no client authentication. Stored * by reference and owned by the client, released with the * client. Set by the "token_endpoint_auth_method" parameter or * member of oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, token_endpoint_auth, oauth2_cfg_endpoint_auth_t *) /** * @brief Whether to verify the TLS server certificate on the calls to * the token endpoint, the userinfo endpoint and the JWKS URI; * unset counts as true. Set by the "ssl_verify" option of * oauth2_openidc_client_set_options(). */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, ssl_verify, oauth2_flag_t) /** * @brief The timeout in seconds of the calls to the token and userinfo * endpoints; the getter returns 20 when unset. There is no * option string for it. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_timeout, oauth2_uint_t) /** * @brief The number of times a failed call to the token or userinfo * endpoint is retried. Default 1; there is no option string for * it. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_retries, oauth2_uint_t) /** * @brief The interval in milliseconds between those retries. Default * 300; there is no option string for it. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(openidc, client, http_retry_interval, oauth2_uint_t) /** * @brief Configure the OpenID Connect client from an option string. * * Creates the client of the configuration when there is none yet, * selects the session configuration of the location and applies the * "ssl_verify" option before parsing @p value per @p type. For the * "string" type the token endpoint authentication method and its * parameters (see cfg.h) are taken from @p value; for the "json" and * "file" types the method is the "token_endpoint_auth_method" member * and its parameters come from @p options, plus the "client_id" and * "client_secret" members. A "json" or "file" document that fails to * parse leaves the configuration without a client. * * @param log the log handle to use * @param cfg the OpenID Connect configuration holding the client * @param type "string" (form-encoded parameters: client_id, * client_secret, scope, authn_request_params, * token_endpoint_auth_method plus the parameters of that * method), "json" (inline JSON client metadata with the * members of the same names, client_id being required) * or "file" (path to a JSON client metadata file) * @param value the parameter string, JSON document or filename, * per @p type * @param options form-encoded parameters: "session" names the * registered session configuration (session.h) to use, * the default one when absent; "ssl_verify" ("true" or * "false") sets the client's ssl_verify flag; for the * "json" and "file" types also the parameters of the * token endpoint authentication method * @return NULL on success, an error string on failure */ char *oauth2_openidc_client_set_options(oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *type, const char *value, const char *options); /** * @brief The client of a location-based configuration: a borrowed * pointer owned by the configuration, or NULL when * oauth2_openidc_client_set_options() has not been called on it. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, openidc, client, oauth2_openidc_client_t *) /** @} */ /** * @name OpenID Connect request handling * @{ */ /** * @brief Configure the location-based settings from an option string. * * Parses a form-encoded option string, e.g. * "handler_path=/openid-connect&unauth_action=auth", accepting: * handler_path, redirect_uri, unauth_action ("auth", "pass", "401" or * "410"), state.cookie.name.prefix, state.cookie.timeout (seconds), * state.cookie.max and state.cookie.delete.oldest. Settings absent * from the string are left as they are. * * @param log the log handle to use * @param cfg the location-based configuration to populate * @param options the form-encoded option string * @return NULL on success, an error string on failure */ char *oauth2_cfg_openidc_set_options(oauth2_log_t *log, oauth2_cfg_openidc_t *cfg, const char *options); /** * @brief Obtain the redirect URI for a request, with an "iss" * parameter identifying the provider appended to it. * * As oauth2_cfg_openidc_redirect_uri_get(), with the URL-encoded * issuer of @p provider added as an "iss" query parameter. * * Takes the log handle, the location-based configuration, the incoming * HTTP request and the provider whose issuer is appended. * * @return the redirect URI as a newly allocated string, to be released * with oauth2_mem_free(), or NULL on error */ char *oauth2_cfg_openidc_redirect_uri_get_iss( oauth2_log_t *, const oauth2_cfg_openidc_t *, const oauth2_http_request_t *, const oauth2_openidc_provider_t *); /** * @brief Check whether a request targets the configured redirect URI. * * Compares the request's URL, without its query string, to the * redirect URI resolved for it with * oauth2_cfg_openidc_redirect_uri_get(). * * @param log the log handle to use * @param cfg the location-based configuration * @param request the incoming HTTP request * @return true when the request is for the redirect URI */ bool oauth2_openidc_is_request_to_redirect_uri(oauth2_log_t *log, const oauth2_cfg_openidc_t *cfg, oauth2_http_request_t *request); /** * @brief Handle an incoming HTTP request according to the OpenID * Connect Authorization Code flow. * * The main entry point for using liboauth2 as an OpenID Connect RP: * loads the session associated with the request, then either serves the * internal endpoints (i.e. the authorization response presented on the * redirect URI), returns the claims of an existing authenticated * session, or applies the configured oauth2_unauth_action_t - by * default initiating an authentication request towards the provider. * * On the redirect URI the authorization code is exchanged at the token * endpoint with the PKCE code verifier from the state cookie, the * id_token is verified against the provider's JWKS URI, issuer, the * client_id as audience and the nonce, the userinfo claims are * retrieved when the provider has a userinfo endpoint, the session is * saved with the id_token's "sub" as its user, and the response * redirects to the URL originally requested. An authentication request * is a 302 redirect to the authorization endpoint carrying a freshly * generated state, nonce and PKCE challenge, with the corresponding * state cookie set on the response. * * @param log the log handle to use * @param c the location-based OpenID Connect configuration * @param r the incoming HTTP request * @param response set to a newly allocated HTTP response to be served * to the user agent (redirect, cookies, error status); * to be released by the caller with * oauth2_http_response_free() * @param claims on an authenticated request, set to a newly allocated * JSON object merging the session's id_token and * userinfo claims; to be released by the caller with * json_decref() * * @return true on success, false on error */ bool oauth2_openidc_handle(oauth2_log_t *log, const oauth2_cfg_openidc_t *c, oauth2_http_request_t *r, oauth2_http_response_t **response, json_t **claims); /** @} */ #endif /* _OAUTH2_OPENIDC_H_ */ liboauth2-2.4.0/include/oauth2/cache.h0000664000175000017500000002120215250553546020230 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_CACHE_H_ #define _OAUTH2_CACHE_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file cache.h * @brief Pluggable named caches. * * Cache instances live behind a backend vtable - "shm" (shared * memory) and "file" are always built, "memcache" and "redis" when * their client libraries are found - and are created and registered * under a name with oauth2_cfg_set_cache() (cfg.h), then retrieved * with oauth2_cache_obtain(). Keys are hashed and values are * transparently encrypted, depending on per-instance settings and the * backend's default. Caches back token verification results, session * data, provider documents, DPoP replay detection, etc. */ #include "oauth2/log.h" #include "oauth2/util.h" /** * @brief An opaque cache instance: a backend plus the per-instance * key hashing and value encryption settings, created and * registered by oauth2_cfg_set_cache() and retrieved with * oauth2_cache_obtain(). */ typedef struct oauth2_cache_t oauth2_cache_t; /** * @name Cache backend interface * The function table a cache backend implements (see src/cache/). The * core calls a backend only through this table and does the generic * work around it: keys are hashed before the backend sees them (per * the instance's "key_hash_algo" option, SHA-256 hex by default, * "none" passes them through) and values are encrypted into a compact * JWE under a key derived from the crypto passphrase, and decrypted * again after a lookup, when the instance's "encrypt" option, or the * backend's default, says so. The built-in backends are registered on * first use; the table is public but there is no API to register * another one. * @{ */ /** * @brief Backend init function: create the backend state of a new * cache instance. * * Called with the new instance and the complete option list of the * cache directive from oauth2_cfg_set_cache() (cfg.h), or with an * empty list from oauth2_cache_obtain() for the default cache. The * backend reads its own options from the list and ignores the generic * "name", "encrypt", "key_hash_algo" and "passphrase_hash_algo" ones * that the core consumes afterwards; it must store its state in the * instance's impl member and point the instance's type member at its * own oauth2_cache_type_t, which the core relies on from then on. * Returns true on success, false on error. */ typedef bool (*oauth2_cache_init_function)(oauth2_log_t *log, oauth2_cache_t *, const oauth2_nv_list_t *options); /** * @brief Backend post-config function: complete the setup in the * parent process. * * Called once, right after init and before the server forks its * workers, so this is where process-shared resources are created: the * "shm" backend creates its mutex and shared memory segment and clears * the slots, "file" creates its mutex, "memcache" and "redis" have * nothing to do. Returns true on success, false on error, which fails * the cache directive. */ typedef bool (*oauth2_cache_post_config_function)(oauth2_log_t *log, oauth2_cache_t *); /** * @brief Backend child-init function: attach a forked worker process. * * Meant to be called in each worker process after the fork; only the * "shm" backend uses it, to attach to its shared memory segment, the * others return true without doing anything. Returns true on success, * false on error. */ typedef bool (*oauth2_cache_child_init_function)(oauth2_log_t *log, oauth2_cache_t *); /** * @brief Backend get function: look up a hashed key. * * Called with the key already hashed by the core. The backend sets the * value pointer to the stored value as a newly allocated string, which * the core decrypts when the instance encrypts and releases with * oauth2_mem_free(), or to NULL on a miss, which includes an entry * that has expired. Returns true when the lookup was performed, hit or * miss, false on error. */ typedef bool (*oauth2_cache_get_function)(oauth2_log_t *log, oauth2_cache_t *, const char *key, char **value); /** * @brief Backend set function: store a value under a hashed key. * * Called with the key already hashed and the value already encrypted * by the core when the instance encrypts; the expiry is the * time-to-live in seconds. A NULL value removes the entry. Returns * true when stored or removed, false on error. */ typedef bool (*oauth2_cache_set_function)(oauth2_log_t *log, oauth2_cache_t *, const char *key, const char *value, oauth2_time_t expiry); /** * @brief Backend free function: release the backend state. * * Called when the registered cache instances are released at * oauth2_shutdown() (util.h), after the core released its own * members; must free the impl member and whatever it holds. The * return value is ignored. */ typedef bool (*oauth2_cache_free_function)(oauth2_log_t *log, oauth2_cache_t *); /** * @brief The descriptor of a cache backend: one static instance per * backend (oauth2_cache_shm, oauth2_cache_file, ...), pointed * at by every cache instance of that type. */ typedef struct oauth2_cache_type_t { /** the type name that the type argument of oauth2_cfg_set_cache() * selects: "shm", "file", "memcache" or "redis" */ const char *name; /** whether values are encrypted unless the instance's "encrypt" * option says otherwise: false for "shm", whose memory does not * leave the process tree, true for the other backends */ bool encrypt_by_default; /** see oauth2_cache_init_function */ oauth2_cache_init_function init; /** see oauth2_cache_post_config_function */ oauth2_cache_post_config_function post_config; /** see oauth2_cache_child_init_function */ oauth2_cache_child_init_function child_init; /** see oauth2_cache_get_function */ oauth2_cache_get_function get; /** see oauth2_cache_set_function */ oauth2_cache_set_function set; /** see oauth2_cache_free_function */ oauth2_cache_free_function free; } oauth2_cache_type_t; /** @} */ /** * @brief Retrieve a registered cache instance. * * When no cache has been configured at all, a default shared-memory * cache is created and registered first. * * @param log the log handle to use * @param name the name the cache was registered under (the "name" * option to oauth2_cfg_set_cache()), or NULL for the * default cache * @return a borrowed pointer to the cache, or NULL on error */ oauth2_cache_t *oauth2_cache_obtain(oauth2_log_t *log, const char *name); /** * @brief Attach a forked worker process to a cache. * * Calls the backend's child-init function (see * oauth2_cache_child_init_function); to be called in each worker * process after the fork for every cache obtained there. * * @param log the log handle to use * @param cache the cache to attach to * @return true on success or when the backend has nothing to do, false * on error or for a NULL cache */ bool oauth2_cache_child_init(oauth2_log_t *log, oauth2_cache_t *cache); /** * @brief Look up a key in a cache. * * @param log the log handle to use * @param ctx the cache to look up in * @param key the key to look up * @param value on a hit, set to the (decrypted) value as a newly * allocated string, to be released with * oauth2_mem_free(); set to NULL on a miss * @return true when the lookup was performed successfully (hit or * miss), false on error */ bool oauth2_cache_get(oauth2_log_t *log, oauth2_cache_t *ctx, const char *key, char **value); /** * @brief Store a key/value pair in a cache. * * @param log the log handle to use * @param ctx the cache to store in * @param key the key to store the value under * @param value the value to store * @param ttl_s the time-to-live of the entry in seconds * @return true when stored successfully, false on error */ bool oauth2_cache_set(oauth2_log_t *log, oauth2_cache_t *ctx, const char *key, const char *value, oauth2_time_t ttl_s); #endif /* _OAUTH2_CACHE_H_ */ liboauth2-2.4.0/include/oauth2/version.h0000664000175000017500000000330315250557205020650 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_VERSION_H_ #define _OAUTH2_VERSION_H_ /* include/oauth2/version.h Generated from version.h.in by autoheader. */ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file version.h * @brief Library name and version. * * Generated from version.h.in at configure time from the version in * configure.ac; the functions return the same values as the macros, * but for the library that is actually loaded rather than the headers * that a consumer was compiled against. */ /** @brief The package name, "liboauth2". */ #define OAUTH2_PACKAGE_NAME "liboauth2" /** @brief The package version, e.g. "2.1.0". */ #define OAUTH2_PACKAGE_VERSION "2.4.0" /** @brief The version of the library, as OAUTH2_PACKAGE_VERSION. */ const char *oauth2_version(); /** * @brief The library name and version joined with a dash, e.g. * "liboauth2-2.1.0". */ const char *oauth2_package_string(); #endif // _OAUTH2_VERSION_H_ liboauth2-2.4.0/include/oauth2/oauth2.h0000664000175000017500000003073315250553546020400 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_H_ #define _OAUTH2_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file oauth2.h * @brief OAuth 2.x wire protocol constants and access token verification. * * This is the primary entry point for using liboauth2 as an OAuth 2.x * Resource Server: configure one or more verification methods in an * oauth2_cfg_token_verify_t (see cfg.h) and pass each access token * presented on an incoming HTTP request to oauth2_token_verify(). */ #include #include "oauth2/cfg.h" #include "oauth2/http.h" #include "oauth2/log.h" #include "oauth2/util.h" /** * @name OAuth 2.x protocol parameters * Request/response parameter names and values used in OAuth 2.x flows * (RFC 6749) and PKCE (RFC 7636): the OpenID Connect RP flow (openidc.h) * and the token endpoint grants (proto.h) send them, and the source * token retrieval (proto.h) looks for OAUTH2_ACCESS_TOKEN. * @{ */ /** @brief Token request parameter naming the grant (RFC 6749 4.1.3). */ #define OAUTH2_GRANT_TYPE "grant_type" /** * @brief Token response member carrying the access token (RFC 6749 * section 5.1); also the default query parameter, form parameter * and cookie name a source token is looked for under (proto.h). */ #define OAUTH2_ACCESS_TOKEN "access_token" /** @brief The authorization code (RFC 6749 sections 4.1.2 and 4.1.3). */ #define OAUTH2_CODE "code" /** @brief The requested scope (RFC 6749 section 3.3). */ #define OAUTH2_SCOPE "scope" /** * @brief The nonce binding an id_token to the authentication request * (OpenID Connect Core section 3.1.2.1). */ #define OAUTH2_NONCE "nonce" /** @brief The authorization request state (RFC 6749 section 4.1.1). */ #define OAUTH2_STATE "state" /** @brief The authorization request response type (RFC 6749 3.1.1). */ #define OAUTH2_RESPONSE_TYPE "response_type" /** @brief The redirection endpoint URI (RFC 6749 section 3.1.2). */ #define OAUTH2_REDIRECT_URI "redirect_uri" /** @brief OAUTH2_GRANT_TYPE value of the authorization code grant. */ #define OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE "authorization_code" /** @brief PKCE code challenge (RFC 7636 section 4.3). */ #define OAUTH2_CODE_CHALLENGE "code_challenge" /** @brief PKCE code challenge method (RFC 7636 section 4.3), "S256". */ #define OAUTH2_CODE_CHALLENGE_METHOD "code_challenge_method" /** @brief PKCE code verifier sent to the token endpoint (RFC 7636 4.5). */ #define OAUTH2_CODE_VERIFIER "code_verifier" /** @brief OAUTH2_RESPONSE_TYPE value of the authorization code flow. */ #define OAUTH2_RESPONSE_TYPE_CODE "code" /** @brief The client identifier (RFC 6749 section 2.2). */ #define OAUTH2_CLIENT_ID "client_id" /** @brief The client secret as a form parameter (RFC 6749 2.3.1). */ #define OAUTH2_CLIENT_SECRET "client_secret" /** @} */ /** * @name Error response members and error codes * Error response member names (RFC 6749 section 5.2), bearer token error * codes (RFC 6750 section 3.1) and the step-up authentication challenge * error code (RFC 9470). The server bindings put them in the * WWW-Authenticate challenge of a rejected request (e.g. * oauth2_apache_return_www_authenticate(), apache.h). * @{ */ /** @brief Error response member carrying the error code. */ #define OAUTH2_ERROR "error" /** @brief Error response member carrying a human-readable description. */ #define OAUTH2_ERROR_DESCRIPTION "error_description" /** @brief The token is expired, revoked, malformed or otherwise invalid. */ #define OAUTH2_ERROR_INVALID_TOKEN "invalid_token" /** @brief The request is missing a parameter or otherwise malformed. */ #define OAUTH2_ERROR_INVALID_REQUEST "invalid_request" /** @brief The token's scope does not cover the requested resource. */ #define OAUTH2_ERROR_INSUFFICIENT_SCOPE "insufficient_scope" /** * @brief The user authentication behind the token does not meet the * resource's requirements, e.g. on ACR or freshness (RFC 9470 * section 3). */ #define OAUTH2_ERROR_INSUFFICIENT_USER_AUTHENTICATION \ "insufficient_user_authentication" /** @} */ /** * @name JWT claim names * Registered JSON Web Token claim names (RFC 7519 section 4.1), as set * in the client assertions oauth2_jwt_create() (jose.h) builds and read * from DPoP proofs; the names token verification validates are the * OAUTH2_JOSE_JWT_* ones in jose.h. * @{ */ /** @brief Issuer. */ #define OAUTH2_CLAIM_ISS "iss" /** @brief Subject. */ #define OAUTH2_CLAIM_SUB "sub" /** @brief JWT ID, unique per token; a DPoP proof's replay detection key. */ #define OAUTH2_CLAIM_JTI "jti" /** @brief Expiration time, in seconds since the epoch. */ #define OAUTH2_CLAIM_EXP "exp" /** @brief Audience. */ #define OAUTH2_CLAIM_AUD "aud" /** @brief Issued-at time, in seconds since the epoch. */ #define OAUTH2_CLAIM_IAT "iat" /** @} */ /** * @brief Action to apply to a request that is not (yet) authenticated. * * Consumed by the OpenID Connect RP handling (see openidc.h) and the * server bindings to decide how to respond to a request that carries no * valid session or token. */ typedef enum { /** * No action configured: behaves as * OAUTH2_UNAUTH_ACTION_AUTHENTICATE. */ OAUTH2_UNAUTH_ACTION_UNDEFINED, /** * Initiate an OpenID Connect authentication request towards the * provider; XML HTTP (Ajax) requests receive a 410 instead since * they cannot meaningfully follow the redirect. */ OAUTH2_UNAUTH_ACTION_AUTHENTICATE, /** Pass the request on to the application, unauthenticated. */ OAUTH2_UNAUTH_ACTION_PASS, /** Reject the request with HTTP status 401 Unauthorized. */ OAUTH2_UNAUTH_ACTION_HTTP_401, /** Reject the request with HTTP status 410 Gone. */ OAUTH2_UNAUTH_ACTION_HTTP_410 } oauth2_unauth_action_t; /** * @brief Add client authentication to an outgoing HTTP call. * * Applies the endpoint authentication method configured in @p auth to a * call towards an OAuth 2.x server endpoint (token, introspection, * revocation, etc.): * - client_secret_basic: HTTP basic authentication on @p ctx with the * URL-encoded client_id and client_secret (RFC 6749 section 2.3.1); * - client_secret_post: the "client_id" and "client_secret" form * parameters added to @p params; * - client_secret_jwt / private_key_jwt: a "client_assertion" JWT with * the "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" * "client_assertion_type" set in @p params (RFC 7523), carrying * "iss" and "sub" set to the client_id, the configured "aud", a "jti" * and an "iat" and "exp" 60 seconds ahead; signed with HS256 by the * key derived from the client secret, respectively with RS256 by the * configured private key, which must be an RSA key; * - client_cert: the configured certificate and key files set as the * TLS client certificate of @p ctx; * - basic: HTTP basic authentication on @p ctx with the configured * username and password, for endpoints protected with plain basic * authentication rather than client credentials; * - none: nothing. * * @param log the log handle to use * @param ctx the HTTP call context to add the authentication to * @param auth the endpoint authentication configuration, populated with * oauth2_cfg_set_endpoint_auth() * @param params the form POST parameters of the call, to which the * methods above may add * * @return true on success (including method "none"), false when @p ctx * or @p auth is NULL, the credentials the method needs are not * configured, the private_key_jwt key is not an RSA key or the * assertion could not be created */ bool oauth2_http_ctx_auth_add(oauth2_log_t *log, oauth2_http_call_ctx_t *ctx, const oauth2_cfg_endpoint_auth_t *auth, oauth2_nv_list_t *params); /** * @brief Verify an OAuth 2.x access token. * * Runs @p token through the chain of verification methods configured in * @p verify, in the order they were added with * oauth2_cfg_token_verify_add_options() (cfg.h), until one accepts it. * For each method the result cache of that method is consulted first, * keyed by the token: a cached payload whose "exp" claim has not passed * is returned as is, an expired one is dropped and the method is run * again. The methods are: * - the key-based ones ("plain", "base64", "base64url", "hex", "pem", * "pubkey", "jwk", "jwks_uri", "eckey_uri" and "aws_alb"): the token * is a JWT whose signature and claims are verified per * oauth2_jose_jwt_verify() (jose.h) against the configured key(s); * - "introspect": the token is POSTed to the introspection endpoint * (RFC 7662) as the "token" parameter (or the configured * "introspect.token_param_name") with "token_type_hint=access_token", * the configured extra parameters and the endpoint's authentication * (oauth2_http_ctx_auth_add()); the response must be 2xx, JSON without * an "error" member and carry "active": true; * - "metadata": the authorization server metadata (RFC 8414) is fetched * from the configured URL and, when the token is a JWT, the JWKS at * its "jwks_uri" verifies it with the metadata "issuer" as the * expected "iss" unless one was configured; a token that is not a JWT * is introspected at the metadata's "introspection_endpoint". * The payload the accepting method produced is stored in its cache for * the configured expiry (300 seconds by default). * * When the accepting method was configured with "type=dpop" the request * must carry exactly one DPoP header (RFC 9449) whose JWS has "typ" * "dpop+jwt", an asymmetric "alg", a public-only "jwk" header that * verifies its signature, "htm" and "htu" claims matching the request * method and URL, an "iat" fresh per the "dpop.iat.*" options, a "jti" * not seen before (tracked in the "dpop.cache" for "dpop.expiry"), an * "ath" equal to the SHA-256 hash of the token, and whose key thumbprint * equals the token's "cnf"/"jkt" claim. With "type=mtls" (RFC 8705) the * SHA-256 fingerprint of the client certificate the binding stored in * the request context under OAUTH2_TLS_CERT_VAR_NAME (http.h) must * equal the token's "cnf"/"x5t#S256" claim; with "mtls.policy=optional" * a token without such a claim passes, with the default "required" it * is rejected. * * @param log the log handle to use * @param request the incoming HTTP request on which the token was * presented; used for the DPoP/mTLS proof-of-possession * checks (a DPoP check fails without it), may be NULL for * plain bearer tokens * @param verify the token verification configuration, created and * populated with oauth2_cfg_token_verify_add_options() * @param token the access token, as presented by the client * @param json_payload set to the token's JSON payload, i.e. the JWT * claims or the introspection response, as a new object * to be released by the caller with json_decref(); must * not be NULL. It is set when a method accepted the * token and released again, and set to NULL, when the * proof-of-possession check failed afterwards * @param status_code optional (may be NULL); on failure set to the HTTP * status code that the caller is to return, which is * always an error code i.e. 4xx or 5xx, defaulting to 401 * when the failure is not the result of a call to a remote * endpoint that returned an error status code itself * * @return true when the token was verified successfully, false when * @p verify or @p token is NULL, no method accepted the token or * the proof-of-possession check failed */ bool oauth2_token_verify(oauth2_log_t *log, oauth2_http_request_t *request, oauth2_cfg_token_verify_t *verify, const char *token, json_t **json_payload, oauth2_http_status_code_t *status_code); #endif /* _OAUTH2_H_ */ liboauth2-2.4.0/include/oauth2/session.h0000664000175000017500000003347315250553546020665 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_SESSION_H_ #define _OAUTH2_SESSION_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file session.h * @brief Cookie-based session management. * * Sessions carry the authenticated state of the OpenID Connect RP flow * (see openidc.h) across requests: a session record holds the user, * the id_token and its claims, the userinfo claims and the session * timestamps. Per the session configuration the record is stored * either in the session cookie itself ("cookie" type, encrypted) or in * a cache referenced from the cookie ("cache" type), and both an * inactivity timeout and a maximum session duration are enforced. */ #include "oauth2/cache.h" #include "oauth2/openidc.h" #include "oauth2/util.h" /** * @name Session configuration * `oauth2_cfg_session_t` holds the session settings: storage type, * cookie name and path, inactivity timeout, maximum duration, the * backing cache (for the "cache" type) and the load/save callbacks * implementing the storage type. Configurations are registered under * a name by oauth2_cfg_session_set_options() and selected through the * "session" option of oauth2_cfg_openidc_set_options() (openidc.h); * when none was configured, a default one of the "cache" type is * created on first use. * @{ */ /** * @brief Opaque session configuration. * * A new object has its storage type, cookie name and path, timeouts, * cache and callbacks unset, in which case the getters below return * their defaults. Only the `_init` and `_free` functions of the * declaration are implemented; `_clone` and `_merge` are not. Freeing * the object does not free the cache it refers to, which is owned by * the cache registry (cache.h). */ OAUTH2_CFG_TYPE_DECLARE(cfg, session) /** * @brief The name of the session cookie, default "openidc_session"; * set by the "cookie.name" option. The getter returns a * borrowed pointer, also for a NULL configuration. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cookie_name, char *) /** * @brief The path attribute of the session cookie, default "/"; set * by the "cookie.path" option. The getter returns a borrowed * pointer, also for a NULL configuration. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cookie_path, char *) /** * @brief The inactivity timeout in seconds, default 300; set by the * "inactivity_timeout" option. A session expires when no * request refreshed it for that long: oauth2_session_save() * sets the expiry that far ahead and oauth2_session_handle() * moves it forward on activity; for the "cache" type it is also * the time-to-live of the cache entry. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, inactivity_timeout_s, oauth2_time_t) /** * @brief The maximum session duration in seconds, counted from the * session start, default 28800 (8 hours); set by the * "max_duration" option. A stored session that started longer * ago fails to load. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, max_duration_s, oauth2_time_t) /** * @brief The cache holding the session records of the "cache" storage * type, obtained by name through the "cache" option (the * default cache when absent); NULL for the "cookie" type. The * getter returns a borrowed pointer and requires a non-NULL * configuration. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, cache, oauth2_cache_t *) /** * @brief Callback loading the stored session record for a request. * * Implements the load half of a storage type: the built-in "cookie" * callback takes the encrypted record from the session cookie, the * "cache" callback looks the cookie value up as the session identifier * in the configured cache. Both remove the session cookie from the * request while reading it. * * @param log the log handle to use * @param cfg the session configuration * @param request the incoming request carrying the session cookie * @param json set to the stored record as a new JSON object, to be * released by the caller with json_decref(), or left * NULL when the request carries no session cookie or * no record is stored for it * @return true when the lookup was performed, also without a result, * false when the stored record could not be decrypted, read * or parsed */ typedef bool(oauth2_session_load_callback_t)(oauth2_log_t *log, const oauth2_cfg_session_t *cfg, oauth2_http_request_t *request, json_t **json); /** * @brief Callback persisting a session record and setting the cookie. * * Implements the save half of a storage type: the built-in "cookie" * callback encrypts the record into the session cookie, the "cache" * callback stores it in the configured cache under the record's * session identifier, with the inactivity timeout as time-to-live, and * sets the cookie to that identifier. Both set the cookie with the * configured name and path, with the Secure attribute when the request * came in over https and without Max-Age, so that it lasts for the * browser session. * * @param log the log handle to use * @param cfg the session configuration * @param request the incoming request, consulted for its scheme * @param response the response to set the session cookie on * @param json the record to store, borrowed * @return true when the record was stored and the cookie set, false on * error */ typedef bool(oauth2_session_save_callback_t)( oauth2_log_t *log, const oauth2_cfg_session_t *cfg, const oauth2_http_request_t *request, oauth2_http_response_t *response, json_t *json); /** * @brief The load callback of the configured storage type; the getter * returns the "cookie" one when unset. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, load_callback, oauth2_session_load_callback_t *) /** * @brief The save callback of the configured storage type; the getter * returns the "cookie" one when unset. */ OAUTH2_TYPE_DECLARE_MEMBER_GET(cfg, session, save_callback, oauth2_session_save_callback_t *) /** * @brief Configure the session settings from an option string. * * @param log the log handle to use * @param cfg the session configuration to populate, or NULL to * create a new one * @param type "cookie" (keep the session record in an encrypted * cookie) or "cache" (keep it in a cache, referenced * from the cookie) * @param options form-encoded parameters: "cookie.name", * "cookie.path", "inactivity_timeout" and * "max_duration" (both in seconds; a value that does * not parse leaves the setting unset), for the "cache" * type the "cache" name to use (the default cache when * absent), and "name", the name the configuration is * registered under for the "session" option of * oauth2_cfg_openidc_set_options() (the default * configuration when absent) * @return NULL on success, an error string on failure */ char *oauth2_cfg_session_set_options(oauth2_log_t *log, oauth2_cfg_session_t *cfg, const char *type, const char *options); /** @} */ /** * @name Session record * The per-user session state as loaded from and saved to the * configured storage. * @{ */ /** * @brief Opaque per-user session record. * * A new record has its start set to the current time, no expiry (0) * and no user, id_token or claims; `_clone` is not implemented. The * session identifier the "cache" storage type keys on is generated by * oauth2_session_load() and not exposed. */ OAUTH2_TYPE_DECLARE(session, rec); /** * @brief The authenticated user; the OpenID Connect RP flow stores * the "sub" claim of the id_token here. The setter copies the * string and rejects NULL, the getter returns a borrowed * pointer. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, user, char *) /** * @brief The serialized id_token, for a binding that wants to keep it; * the RP flow itself stores only its claims. The setter copies * the string and rejects NULL, the getter returns a borrowed * pointer. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, id_token, char *) /** * @brief The claims of the verified id_token, a borrowed JSON object * or NULL; set with oauth2_session_rec_id_token_claims_set(). */ OAUTH2_TYPE_DECLARE_MEMBER_GET(session, rec, id_token_claims, json_t *) /** * @brief The claims obtained from the userinfo endpoint, a borrowed * JSON object or NULL; set with * oauth2_session_rec_userinfo_claims_set(). */ OAUTH2_TYPE_DECLARE_MEMBER_GET(session, rec, userinfo_claims, json_t *) /** * @brief The time the session started, in seconds since the epoch: * the creation time of a new record, restored from storage on * load and checked against the maximum duration there. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, start, oauth2_time_t) /** * @brief The time the session expires, in seconds since the epoch: 0 * in a new record, set to the current time plus the inactivity * timeout by the first oauth2_session_save(), moved forward by * oauth2_session_handle() on activity and restored from storage * on load, where a passed expiry discards the session. */ OAUTH2_TYPE_DECLARE_MEMBER_SET_GET(session, rec, expiry, oauth2_time_t) /** * @brief Store the claims of the id_token in the session record. * * The record takes a reference of its own to the object, so the caller * keeps, and eventually releases, its own; a NULL object leaves the * record unchanged. * * @param log the log handle to use * @param session the session record * @param id_token the id_token claims as a JSON object * @return true */ bool oauth2_session_rec_id_token_claims_set(oauth2_log_t *log, oauth2_session_rec_t *session, json_t *id_token); /** * @brief Store the userinfo claims in the session record. * * As oauth2_session_rec_id_token_claims_set(), for the claims obtained * from the userinfo endpoint. * * @param log the log handle to use * @param session the session record * @param userinfo_claims the userinfo claims as a JSON object * @return true */ bool oauth2_session_rec_userinfo_claims_set(oauth2_log_t *log, oauth2_session_rec_t *session, json_t *userinfo_claims); /** @} */ /** * @name Session lifecycle * @{ */ /** * @brief Load the session associated with an incoming request. * * Retrieves the stored record through the configuration's load * callback and validates it: a session that started longer ago than * the maximum duration or whose expiry has passed is dropped and * replaced by a new empty record, as is returned when the request * carries no session at all. A new record gets a fresh session * identifier. * * @param log the log handle to use * @param c the session configuration * @param r the incoming HTTP request * @param session set to the newly allocated session record, to be * released by the caller with oauth2_session_rec_free() * @return true when a session was loaded or a new one created, false * on error */ bool oauth2_session_load(oauth2_log_t *log, const oauth2_cfg_session_t *c, oauth2_http_request_t *r, oauth2_session_rec_t **session); /** * @brief Persist a session and set the session cookie on the response. * * Serializes the record - start, expiry, identifier, user, id_token * and claims - to JSON and hands it to the configuration's save * callback; a record without an expiry gets one set to the current * time plus the inactivity timeout first. * * @param log the log handle to use * @param cfg the session configuration * @param request the incoming HTTP request * @param response the response to set the session cookie on * @param session the session record to store * @return true when stored, false on error */ bool oauth2_session_save(oauth2_log_t *log, const oauth2_cfg_session_t *cfg, const oauth2_http_request_t *request, oauth2_http_response_t *response, oauth2_session_rec_t *session); /** * @brief Refresh the stored session on an authenticated request. * * Moves the expiry forward to the current time plus the inactivity * timeout and saves the session again, but only when less than the * inactivity timeout minus a slack of 10% of it (at most 60 seconds) * remains until the expiry, so that a freshly saved session is not * written back on every request. * * @param log the log handle to use * @param cfg the session configuration * @param request the incoming HTTP request * @param response the response to set the session cookie on * @param session the session record loaded for the request * @return true when nothing needed saving or the save succeeded, false * when it failed */ bool oauth2_session_handle(oauth2_log_t *log, const oauth2_cfg_session_t *cfg, const oauth2_http_request_t *request, oauth2_http_response_t *response, oauth2_session_rec_t *session); /** @} */ #endif /* _OAUTH2_SESSION_H_ */ liboauth2-2.4.0/include/oauth2/cfg.h0000664000175000017500000010446615250553546017742 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_CFG_H_ #define _OAUTH2_CFG_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file cfg.h * @brief The configuration object model. * * Per-feature configuration objects populated from directive-style * option strings: this is how server bindings translate their native * configuration directives (e.g. Apache or NGINX ones) into typed * settings for the core library. Option strings are form-encoded * name/value parameters ("name1=value1&name2=value2"); setters return * NULL on success or a string describing the offending option. * * The object types are declared with OAUTH2_CFG_TYPE_DECLARE(), which * adds a per-directory merge function to the init/clone/free lifecycle * of OAUTH2_TYPE_DECLARE() (util.h). A freshly initialized object has * every setting at its "unset" sentinel; the accessor functions * substitute the documented default for an unset setting, so callers * never see the sentinels. */ #include #include #include #include "oauth2/util.h" /** * @name Sentinel values for unset flag/integer/time settings * A setting left at its sentinel has not been configured: the * accessor functions substitute the default and the merge functions * let an overriding ("add") configuration win over its base only for * settings that are not at their sentinel. * @{ */ /** @brief An oauth2_flag_t setting that has not been configured. */ #define OAUTH2_CFG_FLAG_UNSET (oauth2_flag_t) - 1 /** @brief An oauth2_uint_t setting that has not been configured. */ #define OAUTH2_CFG_UINT_UNSET (oauth2_uint_t) - 1 /** @brief An oauth2_time_t setting that has not been configured. */ #define OAUTH2_CFG_TIME_UNSET (oauth2_time_t) - 1 /** @} */ /** * @name Generic setter plumbing * The slot setters parse a string value into the flag/uint/time/string * member at byte @p offset of a configuration struct; bindings use * them to implement simple scalar directives. They return NULL on * success or a static error string. * @{ */ /** * @brief Parse a boolean into the oauth2_flag_t member at @p offset * of @p cfg. * * @param cfg the configuration struct * @param offset the byte offset of the member, e.g. from offsetof() * @param value "true", "on" or "1" for on, "false", "off" or "0" for * off, compared case-insensitively; NULL leaves the * member as it is * @return NULL on success, a static error string when @p cfg is NULL * or @p value is none of the above */ const char *oauth2_cfg_set_flag_slot(void *cfg, size_t offset, const char *value); /** * @brief Parse a non-negative decimal integer into the oauth2_uint_t * member at @p offset of @p cfg. * * @param cfg the configuration struct * @param offset the byte offset of the member, e.g. from offsetof() * @param value the decimal representation of the value * @return NULL on success, a static error string when @p cfg or * @p value is NULL or @p value is not a plain non-negative * decimal number (no digits, trailing characters, a negative * value or an overflow) */ const char *oauth2_cfg_set_uint_slot(void *cfg, size_t offset, const char *value); /** * @brief Parse a non-negative decimal number of seconds into the * oauth2_time_t member at @p offset of @p cfg. * * Accepts and rejects the same values as oauth2_cfg_set_uint_slot(). */ const char *oauth2_cfg_set_time_slot(void *cfg, size_t offset, const char *value); /** * @brief Store a copy of @p value in the char * member at @p offset * of @p cfg. * * A string previously stored in the member is released. * * @param cfg the configuration struct * @param offset the byte offset of the member, e.g. from offsetof() * @param value the string to copy * @return NULL on success, a static error string when @p cfg or * @p value is NULL or the copy failed */ const char *oauth2_cfg_set_str_slot(void *cfg, size_t offset, const char *value); /** * @brief Declare a configuration object type. * * As OAUTH2_TYPE_DECLARE (util.h), additionally generating a * `oauth2___merge(log, dst, base, add)` function that * combines a base and an overriding (add) configuration into a * destination, as used for per-directory/location inheritance in * server bindings. Not every type implements every generated function; * the per-type documentation below says which ones are missing. */ #define OAUTH2_CFG_TYPE_DECLARE(module, object) \ OAUTH2_TYPE_DECLARE(module, object) \ void oauth2_##module##_##object##_merge( \ oauth2_log_t *, oauth2_##module##_##object##_t *, \ oauth2_##module##_##object##_t *, \ oauth2_##module##_##object##_t *); /** * @brief Set the global passphrase from which crypto keys are derived, * e.g. for cache and (state) cookie encryption. * * @param log the log handle to use * @param dummy ignored; present so that the function can serve * as a directive setter * @param passphrase the passphrase, copied; a previously set one is * released * @return NULL */ const char *oauth2_crypto_passphrase_set(oauth2_log_t *log, void *dummy, const char *passphrase); /** * @brief Get the global crypto passphrase. * * When none was configured a random one of 12 hexadecimal characters * is generated, set and logged as a warning: it neither survives a * restart nor matches other processes, so configure one explicitly * wherever encrypted data has to outlive a single process. * * @param log the log handle to use * @return a borrowed pointer to the passphrase */ const char *oauth2_crypto_passphrase_get(oauth2_log_t *log); /** @} */ /** * @brief Create and register a named cache instance. * * The instance is created, post-configured and registered under its * name in one go, so it can be retrieved right away. * * @param log the log handle to use * @param dummy ignored * @param type cache backend: "shm" or "file", plus "memcache" and * "redis" when compiled in; NULL selects "shm" * @param options form-encoded parameters: "name" to register the * instance under (for retrieval with * oauth2_cache_obtain(), see cache.h), "encrypt" * (whether values are encrypted, defaulting per * backend), "key_hash_algo" and "passphrase_hash_algo" * (OpenSSL digest names for hashing keys and deriving * the encryption key), plus backend-specific settings: * "max_entries", "max_key_size" and "max_val_size" for * "shm", "dir" and "clean_interval" for "file", * "config_string" for "memcache" and "host", "port", * "username" and "password" for "redis" * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_cache(oauth2_log_t *log, void *dummy, const char *type, const char *options); /** * @name Webserver callbacks * Callbacks that a server binding provides so the core library can * read and set environment variables and read form POST data through * the hosting server; the "environment" and "post" token locations of * oauth2_get_source_token() (proto.h) use them. The Apache binding * provides oauth2_apache_server_callback_funcs (apache.h). * @{ */ /** * @brief Read an environment variable of the current request. * * @param log the log handle to use * @param ctx the binding's request context, as passed by the caller * that invokes the callbacks * @param key the name of the variable * @param value set to its value as a newly allocated string, to be * released with oauth2_mem_free(), or to NULL when the * variable is not set * @return true on success, false on error */ typedef bool(oauth2_cfg_env_get_cb)(oauth2_log_t *log, void *ctx, const char *key, char **value); /** * @brief Set an environment variable of the current request, or clear * it when @p value is NULL. * * @param log the log handle to use * @param ctx the binding's request context * @param key the name of the variable * @param value the value to set, NULL to clear the variable * @return true on success, false on error */ typedef bool(oauth2_cfg_env_set_cb)(oauth2_log_t *log, void *ctx, const char *key, const char *value); /** * @brief Read the body of a form-encoded POST request. * * @param log the log handle to use * @param ctx the binding's request context * @param params set to the parsed parameters as a new list, to be * released with oauth2_nv_list_free() * @return true on success, false when the body could not be read or * parsed */ typedef bool(oauth2_cfg_form_post_read_cb)(oauth2_log_t *log, void *ctx, oauth2_nv_list_t **params); /** @brief The set of callbacks a server binding provides. */ typedef struct oauth2_cfg_server_callback_funcs_t { oauth2_cfg_env_get_cb *get; /**< read an environment variable */ oauth2_cfg_env_set_cb *set; /**< set an environment variable */ oauth2_cfg_form_post_read_cb *form_post; /**< read POST form data */ } oauth2_cfg_server_callback_funcs_t; /** @} */ /** * @name Endpoint authentication * oauth2_cfg_endpoint_auth_t holds the client authentication method * and credentials for calls towards an OAuth 2.x server endpoint * (token, introspection, etc.), applied to outgoing calls with * oauth2_http_ctx_auth_add() (oauth2.h). Configured as a type with * type-specific parameters: * @verbatim client_secret_basic client_id=&client_secret= client_secret_post client_id=&client_secret= client_secret_jwt client_id=&client_secret=&aud= private_key_jwt client_id=&jwk=&aud= client_cert cert=&key= basic username=&password= @endverbatim * @{ */ /** @brief The client authentication methods. */ typedef enum oauth2_cfg_endpoint_auth_type_t { /** No client authentication ("none"), the default. */ OAUTH2_ENDPOINT_AUTH_NONE, /** * client_id and client_secret in an HTTP Basic Authorization * header (RFC 6749 section 2.3.1). */ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_BASIC, /** client_id and client_secret as form parameters. */ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_POST, /** * A JWT client assertion (RFC 7523) signed with HS256 using the * client_secret, with the "aud" parameter as its audience. */ OAUTH2_ENDPOINT_AUTH_CLIENT_SECRET_JWT, /** * A JWT client assertion (RFC 7523) signed with RS256 using the * private key in the "jwk" parameter, with the "aud" parameter as * its audience. */ OAUTH2_ENDPOINT_AUTH_PRIVATE_KEY_JWT, /** A TLS client certificate and key from PEM files (RFC 8705). */ OAUTH2_ENDPOINT_AUTH_CLIENT_CERT, /** HTTP Basic authentication with an arbitrary username/password. */ OAUTH2_ENDPOINT_AUTH_BASIC } oauth2_cfg_endpoint_auth_type_t; /** * @brief Opaque endpoint authentication settings. * * A new object has type OAUTH2_ENDPOINT_AUTH_NONE; the clone copies * the credentials of the configured type. The merge function the * declaration macro names is not implemented. */ OAUTH2_CFG_TYPE_DECLARE(cfg, endpoint_auth) /** * @brief Configure an endpoint authentication method. * * @param log the log handle to use * @param auth the endpoint authentication configuration to populate * @param type one of the method names listed above, or "none"; NULL * leaves @p auth untouched * @param params the method-specific parameters listed above, looked up * under their plain names; a missing required parameter * is an error * @param prefix currently unused: the parameters are looked up without * a prefix, also when the method is selected through a * prefixed option such as "introspect.auth" * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_endpoint_auth(oauth2_log_t *log, oauth2_cfg_endpoint_auth_t *auth, const char *type, const oauth2_nv_list_t *params, const char *prefix); /** * @brief Get the configured authentication method. * @return the method, OAUTH2_ENDPOINT_AUTH_NONE when @p auth is NULL */ oauth2_cfg_endpoint_auth_type_t oauth2_cfg_endpoint_auth_type(const oauth2_cfg_endpoint_auth_t *auth); /** @} */ /** * @name Endpoint settings * oauth2_cfg_endpoint_t holds the settings for calling a remote * endpoint: its URL, the authentication method/credentials, TLS server * certificate verification, HTTP timeout and retry behaviour, and an * outgoing proxy. * @{ */ /** * @brief Opaque remote endpoint settings. * * A new object has no URL or authentication settings and every other * setting unset; the clone is a deep copy. The merge function the * declaration macro names is not implemented. */ OAUTH2_CFG_TYPE_DECLARE(cfg, endpoint) /** * @brief Configure an endpoint from a URL and a set of parameters. * * The parameters are looked up under a name prefixed with @p prefix * and a dot when a prefix is given, e.g. "introspect.ssl_verify", and * under their plain name otherwise: * - "url": the endpoint URL, when @p url is NULL * - "auth": the client authentication method, configured with * oauth2_cfg_set_endpoint_auth() from the same parameters (which * are looked up without the prefix); "none" when absent * - "ssl_verify": whether to verify the server's TLS certificate, * default on * - "http_timeout": the HTTP timeout in seconds, default 20 * - "http_retries": how often a failed call is retried, default 1 * - "http_retry_interval": the interval between retries in * milliseconds, default 300 * - "outgoing_proxy": the proxy to route the call through, none by * default * * @param log the log handle to use * @param cfg the endpoint configuration to populate * @param url the endpoint URL, or NULL to take it from the "url" * parameter * @param params the parameters, may be NULL * @param prefix the parameter name prefix, may be NULL * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_endpoint(oauth2_log_t *log, oauth2_cfg_endpoint_t *cfg, const char *url, const oauth2_nv_list_t *params, const char *prefix); /** @brief Get the endpoint URL; NULL when not configured. */ const char *oauth2_cfg_endpoint_get_url(const oauth2_cfg_endpoint_t *cfg); /** @brief Set the endpoint URL to a copy of @p url, replacing any. */ void oauth2_cfg_endpoint_set_url(oauth2_cfg_endpoint_t *cfg, const char *url); /** * @brief Get the client authentication settings. * @return a borrowed pointer, NULL before oauth2_cfg_set_endpoint() * was called */ const oauth2_cfg_endpoint_auth_t * oauth2_cfg_endpoint_get_auth(const oauth2_cfg_endpoint_t *cfg); /** @brief Whether to verify the server certificate; default on. */ oauth2_flag_t oauth2_cfg_endpoint_get_ssl_verify(const oauth2_cfg_endpoint_t *cfg); /** @brief The HTTP timeout in seconds; default 20. */ oauth2_uint_t oauth2_cfg_endpoint_get_http_timeout(const oauth2_cfg_endpoint_t *cfg); /** @brief The number of retries of a failed call; default 1. */ oauth2_uint_t oauth2_cfg_endpoint_get_http_retries(const oauth2_cfg_endpoint_t *cfg); /** @brief The interval between retries in milliseconds; default 300. */ oauth2_uint_t oauth2_cfg_endpoint_get_http_retry_interval(const oauth2_cfg_endpoint_t *cfg); /** @brief The outgoing proxy; NULL when none is configured. */ const char * oauth2_cfg_endpoint_get_outgoing_proxy(const oauth2_cfg_endpoint_t *cfg); /** @} */ /** * @name Token verification * oauth2_cfg_token_verify_t is a chain of verification methods for * incoming access tokens, consumed by oauth2_token_verify() (oauth2.h), * which runs the chain until one method succeeds. * @{ */ /** * @brief How a token is presented by the client: as a plain bearer * token (RFC 6750) or bound to the client with DPoP (RFC 9449) * or mTLS (RFC 8705) proof-of-possession. */ typedef enum oauth2_cfg_token_verify_type_t { OAUTH2_TOKEN_VERIFY_BEARER, /**< "bearer", the default */ OAUTH2_TOKEN_VERIFY_DPOP, /**< "dpop" */ OAUTH2_TOKEN_VERIFY_MTLS /**< "mtls" */ } oauth2_cfg_token_verify_type_t; /** * @brief Opaque chain of token verification methods. * * Every element holds one method with its own result cache and * proof-of-possession settings; the chain is grown with * oauth2_cfg_token_verify_add_options() and freed and cloned as a * whole. The merge function the declaration macro names is not * implemented. */ OAUTH2_CFG_TYPE_DECLARE(cfg, token_verify) /** * @brief Add a verification method to a token verification chain. * * Appends a method to *@p verify, creating the chain when it is NULL. * * @param log the log handle to use * @param verify pointer to the chain to add to; the chain is created * when *@p verify is NULL * @param type the verification type: * - "plain", "base64", "base64url" or "hex": a symmetric key * provided as a string in that encoding * - "pem": a certificate file; "pubkey": a public key file * - "jwk": a JWK; "jwks_uri" or "eckey_uri": a URL serving the * verification keys; "aws_alb": the ARN of an AWS ALB whose keys * are fetched from its region's key endpoint * - "introspect": a token introspection endpoint URL (RFC 7662) * - "metadata": an authorization server metadata URL (RFC 8414) * from which the verification keys or introspection endpoint are * discovered * @param value the key (material), filename, URL or ARN, per @p type * @param options form-encoded options: * - for the JWT types, the expected "iss" and "aud" values and how * strictly each claim is validated, "required", "optional" or * "skip": "verify.iss" and "verify.aud" (required once an expected * value is configured, otherwise not checked), "verify.exp" * (required, except for "eckey_uri" and "aws_alb" tokens that * carry it in the header), "verify.nbf" and "verify.iat" * (optional), with "verify.iat.slack_before" (none by default) * and "verify.iat.slack_after" (10 seconds) for the "iat" check; * "kid" overrides the key identifier of a single key * - endpoint settings (see oauth2_cfg_set_endpoint()) prefixed with * the type name for the "introspect", "jwks_uri", "eckey_uri" and * "metadata" types, e.g. "introspect.auth" or * "jwks_uri.ssl_verify", plus the ".cache" and ".expiry" * (default 86400 seconds) settings for caching what the URL serves * ("metadata" takes both "metadata." and "jwks_uri." settings), * "introspect.token_param_name" (default "token") and * "introspect.params" for extra introspection request parameters, * and "alb_base_url" for "aws_alb" * - the verification result cache: "verify.cache" (a cache name, * the default cache when absent) and "expiry" (300 seconds) * - the presentation type "type=bearer|dpop|mtls" with, for DPoP, * the proof replay cache "dpop.cache" and "dpop.expiry" (10 * seconds), "dpop.iat.verify" (required), "dpop.iat.slack.before" * and "dpop.iat.slack.after" (5 seconds), and for mTLS * "mtls.env_var_name" (the variable carrying the client * certificate) and "mtls.policy" ("optional" or "required") * @return NULL on success, an error string on failure */ char *oauth2_cfg_token_verify_add_options(oauth2_log_t *log, oauth2_cfg_token_verify_t **verify, const char *type, const char *value, const char *options); /** @} */ /** * @name Token presentation in a request * Describes where a token appears in an HTTP request - an environment * variable, a header, a query or form POST parameter, a cookie or * basic authentication - with per-location settings. The defaults * mentioned below are applied by oauth2_get_source_token() (proto.h). * @{ */ /** @brief The "environment" location. */ typedef struct oauth2_cfg_token_in_envvar_t { /** The environment variable name; default "access_token". */ char *name; } oauth2_cfg_token_in_envvar_t; /** @brief The "header" location. */ typedef struct oauth2_cfg_token_in_header_t { /** The header name; default "Authorization". */ char *name; /** * The scheme the header value must start with; default "bearer", * an empty string accepts the whole header value. */ char *type; } oauth2_cfg_token_in_header_t; /** @brief The "query" location. */ typedef struct oauth2_token_in_query_t { /** The query parameter name; default "access_token". */ char *param_name; } oauth2_cfg_token_in_query_t; /** @brief The "post" location. */ typedef struct oauth2_cfg_token_in_post_t { /** The form POST parameter name; default "access_token". */ char *param_name; } oauth2_cfg_token_in_post_t; /** @brief The "cookie" location. */ typedef struct oauth2_cfg_token_in_cookie_t { /** The cookie name; default "access_token". */ char *name; } oauth2_cfg_token_in_cookie_t; /** @brief The "basic" location, which has no settings. */ typedef struct oauth2_cfg_token_in_basic_t { // TODO: what will be the username? uint8_t dummy; /**< placeholder, unused */ } oauth2_cfg_token_in_basic_t; /** @brief The enabled locations and their settings. */ typedef struct oauth2_cfg_token_in_t { /** Bitmask of oauth2_cfg_token_in_type_t values; 0 when unset. */ char value; oauth2_cfg_token_in_envvar_t envvar; /**< "environment" settings */ oauth2_cfg_token_in_header_t header; /**< "header" settings */ oauth2_cfg_token_in_query_t query; /**< "query" settings */ oauth2_cfg_token_in_post_t post; /**< "post" settings */ oauth2_cfg_token_in_cookie_t cookie; /**< "cookie" settings */ oauth2_cfg_token_in_basic_t basic; /**< "basic" settings */ } oauth2_cfg_token_in_t; /** @brief The token locations, as bit flags. */ typedef enum oauth2_cfg_token_in_type_t { // undefined = 0 OAUTH2_CFG_TOKEN_IN_ENVVAR = 1, /**< an environment variable */ OAUTH2_CFG_TOKEN_IN_HEADER = 2, /**< a header */ OAUTH2_CFG_TOKEN_IN_QUERY = 4, /**< a query parameter */ OAUTH2_CFG_TOKEN_IN_POST = 8, /**< a form POST parameter */ OAUTH2_CFG_TOKEN_IN_COOKIE = 16, /**< a cookie */ OAUTH2_CFG_TOKEN_IN_BASIC = 32 /**< basic authentication */ } oauth2_cfg_token_in_type_t; /** @brief The name of the OAUTH2_CFG_TOKEN_IN_ENVVAR location. */ #define OAUTH2_CFG_TOKEN_IN_ENVVAR_STR "environment" /** @brief The name of the OAUTH2_CFG_TOKEN_IN_HEADER location. */ #define OAUTH2_CFG_TOKEN_IN_HEADER_STR "header" /** @brief The name of the OAUTH2_CFG_TOKEN_IN_QUERY location. */ #define OAUTH2_CFG_TOKEN_IN_QUERY_STR "query" /** @brief The name of the OAUTH2_CFG_TOKEN_IN_POST location. */ #define OAUTH2_CFG_TOKEN_IN_POST_STR "post" /** @brief The name of the OAUTH2_CFG_TOKEN_IN_COOKIE location. */ #define OAUTH2_CFG_TOKEN_IN_COOKIE_STR "cookie" /** @brief The name of the OAUTH2_CFG_TOKEN_IN_BASIC location. */ #define OAUTH2_CFG_TOKEN_IN_BASIC_STR "basic" /** * @brief Enable a token location. * * Adds the location to the ones already enabled in @p cfg and stores * its settings, so calling this once per location enables several. * * @param log the log handle to use * @param cfg the token location configuration to populate * @param method one of the OAUTH2_CFG_TOKEN_IN_..._STR names * @param params location-specific parameters: "name" sets the * variable, header, parameter or cookie name of the * location and "type" the scheme of the "header" * location; "basic" has none * @param allowed bitmask of oauth2_cfg_token_in_type_t values * restricting the methods accepted here * @return NULL on success, an error string when @p method is NULL, * unknown or not in @p allowed */ char *oauth2_cfg_token_in_set(oauth2_log_t *log, oauth2_cfg_token_in_t *cfg, const char *method, const oauth2_nv_list_t *params, oauth2_uint_t allowed); /** @} */ /** * @name Source token retrieval * oauth2_cfg_source_token_t configures where an incoming (source) * token is accepted in a request - one or more of the locations above - * and whether it is stripped from the request before the request is * passed on to the target application. Consumed by * oauth2_get_source_token() (proto.h). * @{ */ /** * @brief Opaque source token settings. * * A new object has no location enabled and the strip flag unset. The * merge takes the "add" configuration as a whole when it has any * location enabled and the "base" one otherwise. */ OAUTH2_CFG_TYPE_DECLARE(cfg, source_token) /** * @brief Enable a location the token is accepted in. * * @param log the log handle to use * @param cfg the source token configuration to populate * @param method one of the OAUTH2_CFG_TOKEN_IN_..._STR names; every * location is allowed * @param options form-encoded parameters: the location's own ones (see * oauth2_cfg_token_in_set()) plus "strip" (a boolean) * setting the strip flag * @return NULL on success, an error string on failure */ char *oauth2_cfg_source_token_set_accept_in(oauth2_log_t *log, oauth2_cfg_source_token_t *cfg, const char *method, const char *options); /** * @brief Return the accepted locations as a bitmask. * @return the enabled oauth2_cfg_token_in_type_t values; the * environment variable and the header when none was enabled */ char oauth2_cfg_source_token_get_accept_in(oauth2_cfg_source_token_t *cfg); /** * @brief Whether the token is stripped from the location it was taken * from; default on. */ oauth2_flag_t oauth2_cfg_source_token_get_strip(oauth2_cfg_source_token_t *cfg); /** @} */ /** * @name Target pass * oauth2_cfg_target_pass_t configures how the verified token's claims * are passed on to the target application: as headers and/or * environment variables, under which name prefix, which claim becomes * the remote user, the header carrying the authenticated user, the * claim under which the full JSON payload is passed, and the encoding * applied to claim values. Options: * "headers", "envvars", "prefix", "authn_header", "remote_user_claim", * "json_payload_claim", "encoding". * @{ */ /** * @brief Opaque target pass settings. * * A new object has every setting unset. The merge takes each setting * from the "add" configuration when set there and from the "base" one * otherwise. The clone function the declaration macro names is not * implemented. */ OAUTH2_CFG_TYPE_DECLARE(cfg, target_pass) /** * @brief The encoding applied to claim values passed on to the target * application in headers and environment variables. */ typedef enum oauth2_cfg_target_pass_encoding_t { /** "none": the value as it is. */ OAUTH2_CFG_TARGET_PASS_ENCODING_NONE, /** * "latin1": the UTF-8 value converted to ISO-8859-1, characters * outside of it becoming "?"; the default. */ OAUTH2_CFG_TARGET_PASS_ENCODING_LATIN1, /** "base64url": the value base64url-encoded. */ OAUTH2_CFG_TARGET_PASS_ENCODING_BASE64URL } oauth2_cfg_target_pass_encoding_t; /** * @brief Configure the target pass settings from an option string. * * @param log the log handle to use * @param cfg the target pass configuration to populate * @param options form-encoded parameters: "headers" and "envvars" * (booleans, both default on), "prefix" (default * "OAUTH2_CLAIM_"), "authn_header" (no default), * "remote_user_claim" (default "sub"), * "json_payload_claim" (no default) and "encoding" * ("latin1", "base64url" or "none", default "latin1") * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_target_pass_options(oauth2_log_t *log, oauth2_cfg_target_pass_t *cfg, const char *options); /** @brief Whether claims are passed as headers; default on. */ oauth2_flag_t oauth2_cfg_target_pass_get_as_headers(oauth2_cfg_target_pass_t *cfg); /** @brief Whether claims are passed as environment variables; on. */ oauth2_flag_t oauth2_cfg_target_pass_get_as_envvars(oauth2_cfg_target_pass_t *cfg); /** * @brief The prefix of the header and environment variable names; * default "OAUTH2_CLAIM_". */ const char *oauth2_cfg_target_pass_get_prefix(oauth2_cfg_target_pass_t *cfg); /** * @brief The header carrying the authenticated user; NULL when none * is configured. */ const char * oauth2_cfg_target_pass_get_authn_header(oauth2_cfg_target_pass_t *cfg); /** @brief The claim that becomes the remote user; default "sub". */ const char * oauth2_cfg_target_get_remote_user_claim(oauth2_cfg_target_pass_t *cfg); /** * @brief The name under which the full JSON payload is passed; NULL * when it is not passed. */ const char * oauth2_cfg_target_get_json_payload_claim(oauth2_cfg_target_pass_t *cfg); /** @brief The encoding applied to claim values; default latin1. */ oauth2_cfg_target_pass_encoding_t oauth2_cfg_target_pass_get_encoding(oauth2_cfg_target_pass_t *cfg); /** * @brief Apply the configured encoding to a claim value. * * @param log the log handle to use * @param cfg the target pass configuration * @param value the value to encode * @return a newly allocated string, to be released with * oauth2_mem_free(), or NULL when @p value is NULL */ char *oauth2_cfg_target_pass_encode(oauth2_log_t *log, oauth2_cfg_target_pass_t *cfg, const char *value); /** @} */ /** * @name Resource Owner Password Credentials * oauth2_cfg_ropc_t configures the ROPC grant (RFC 6749 section 4.3): * the token endpoint (URL plus endpoint/auth options) and the * "client_id", "username", "password" and extra request parameters. * Executed with oauth2_ropc_exec() (proto.h). * @{ */ /** * @brief Opaque ROPC grant settings. * * A new object has no token endpoint or other settings. The merge * takes the "add" configuration as a whole when it has a token * endpoint and the "base" one otherwise. */ OAUTH2_CFG_TYPE_DECLARE(cfg, ropc) /** * @brief Configure the ROPC grant from a token endpoint URL and an * option string. * * @param log the log handle to use * @param cfg the ROPC configuration to populate * @param url the token endpoint URL, or NULL to take it from the * "url" option * @param options form-encoded parameters: the unprefixed endpoint * settings of oauth2_cfg_set_endpoint() ("auth", * "ssl_verify", "http_timeout", ...), "client_id" * (sent as a parameter when the endpoint * authentication method is "none"), "username" and * "password", and "params" (a form-encoded string of * extra parameters to send in the token request, e.g. * "scope=openid") * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_ropc(oauth2_log_t *log, oauth2_cfg_ropc_t *cfg, const char *url, const char *options); // TODO: just ropc_exec, no member get functions? /** * @brief The token endpoint; NULL before oauth2_cfg_set_ropc() was * called. */ const oauth2_cfg_endpoint_t * oauth2_cfg_ropc_get_token_endpoint(oauth2_cfg_ropc_t *cfg); /** @brief The client_id; NULL when not configured. */ const char *oauth2_cfg_ropc_get_client_id(oauth2_cfg_ropc_t *cfg); /** * @brief The configured username; NULL when not configured. Not used * by oauth2_ropc_exec(), which takes the username as an * argument. */ const char *oauth2_cfg_ropc_get_username(oauth2_cfg_ropc_t *cfg); /** * @brief The configured password; NULL when not configured. Not used * by oauth2_ropc_exec(), which takes the password as an * argument. */ const char *oauth2_cfg_ropc_get_password(oauth2_cfg_ropc_t *cfg); /** * @brief The extra token request parameters; NULL when none are * configured. */ const oauth2_nv_list_t * oauth2_cfg_ropc_get_request_parameters(oauth2_cfg_ropc_t *cfg); /** @} */ /** * @name Client Credentials * oauth2_cfg_cc_t configures the Client Credentials grant (RFC 6749 * section 4.4): the token endpoint (URL plus endpoint/auth options) * and extra request parameters. Executed with oauth2_cc_exec() * (proto.h). * @{ */ /** * @brief Opaque Client Credentials grant settings. * * A new object has no token endpoint or other settings. The merge * takes the "add" configuration as a whole when it has a token * endpoint and the "base" one otherwise. */ OAUTH2_CFG_TYPE_DECLARE(cfg, cc) /** * @brief Configure the Client Credentials grant from a token endpoint * URL and an option string. * * @param log the log handle to use * @param cfg the Client Credentials configuration to populate * @param url the token endpoint URL, or NULL to take it from the * "url" option * @param options form-encoded parameters: the unprefixed endpoint * settings of oauth2_cfg_set_endpoint() ("auth", * "ssl_verify", "http_timeout", ...), "client_id" (sent * as a parameter when the endpoint authentication * method is "none") and "params" (a form-encoded string * of extra parameters to send in the token request) * @return NULL on success, an error string on failure */ char *oauth2_cfg_set_cc(oauth2_log_t *log, oauth2_cfg_cc_t *cfg, const char *url, const char *options); /** * @brief The token endpoint; NULL before oauth2_cfg_set_cc() was * called. */ const oauth2_cfg_endpoint_t * oauth2_cfg_cc_get_token_endpoint(oauth2_cfg_cc_t *cfg); /** @brief The client_id; NULL when not configured. */ const char *oauth2_cfg_cc_get_client_id(oauth2_cfg_cc_t *cfg); /** * @brief The extra token request parameters; NULL when none are * configured. */ const oauth2_nv_list_t * oauth2_cfg_cc_get_request_parameters(oauth2_cfg_cc_t *cfg); /** @} */ #endif /* _OAUTH2_CFG_H_ */ liboauth2-2.4.0/include/oauth2/apache.h0000664000175000017500000011540115250557177020417 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_APACHE_H_ #define _OAUTH2_APACHE_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file apache.h * @brief Apache httpd binding. * * The liboauth2_apache library adapts the server-agnostic core to * Apache httpd 2.x, for modules such as mod_oauth2 and mod_sts: it * maps the native request_rec and server_rec onto the * oauth2_http_request_t / oauth2_http_response_t abstraction (http.h) * and onto log sinks (log.h), passes verified claims on to the target * application and evaluates Require directives against them, and * provides the macros that generate a module's boilerplate: the log * callbacks, the cleanup and post_config hooks, the directive * handlers, the per-server config and the module record itself. * * Towards these macros a module is identified by a short name - "foo" * below, e.g. oauth2 for mod_oauth2 - from which the generated and the * expected symbols are derived: the module record is foo_module, the * command table foo_commands and the hook registration function * foo_register_hooks. The module must also define * OAUTH2_PACKAGE_NAME_VERSION to its "\-\" string (e.g. * "mod_oauth2-4.2.0"), which is used in log messages and as the key * that marks the post_config pass. */ #include #include #include #include #include #include #include // avoid errors about ap_auto_config overriding these, so undefine first #undef PACKAGE_BUGREPORT #undef PACKAGE_NAME #undef PACKAGE_STRING #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #include #include #include #include /** * @brief Storage class of the data symbols liboauth2_apache exports. * * See OAUTH2_EXTERN in log.h: the same for the data that liboauth2_apache * exports, which is a library of its own (OAUTH2_APACHE_EXPORTS when building * it, OAUTH2_APACHE_STATIC when its objects are linked in directly). */ #if defined(_WIN32) && !defined(OAUTH2_APACHE_STATIC) #ifdef OAUTH2_APACHE_EXPORTS #define OAUTH2_APACHE_EXTERN __declspec(dllexport) extern #else #define OAUTH2_APACHE_EXTERN __declspec(dllimport) extern #endif #else #define OAUTH2_APACHE_EXTERN extern #endif /** * @brief The server callbacks the core needs from this binding. * * Reads and sets environment variables in the request's * subprocess_env table and reads a form-encoded POST body (at most 1 * MB), all with the request_rec as callback context. Pass it, * together with the request_rec, to the functions that take an * oauth2_cfg_server_callback_funcs_t, such as * oauth2_get_source_token() (proto.h). */ OAUTH2_APACHE_EXTERN oauth2_cfg_server_callback_funcs_t oauth2_apache_server_callback_funcs; /** * @name Logging * A module logs through oauth2_log_sink_t objects whose callbacks * forward to ap_log_error() (server scope) or ap_log_rerror() (request * scope); the level maps translate between the httpd (APLOG_*) and * the library (OAUTH2_LOG_*) log levels in both directions. * @{ */ /** @brief Library log level (OAUTH2_LOG_*) to httpd level (APLOG_*). */ OAUTH2_APACHE_EXTERN oauth2_uint_t log_level_log2apache[]; /** @brief httpd log level (APLOG_*) to library log level (OAUTH2_LOG_*). */ OAUTH2_APACHE_EXTERN oauth2_uint_t log_level_apache2oauth2[]; /** * @brief Backport of httpd's APLOG_USE_MODULE for versions that lack * it: makes the module's log messages carry its module index. */ #ifndef APLOG_USE_MODULE #define APLOG_USE_MODULE(foo) \ extern module AP_MODULE_DECLARE_DATA foo##_module; \ AP_MAYBE_UNUSED(static int *const aplog_module_index) = \ &(foo##_module.module_index) #endif /** * @brief Generate a module's log sink callbacks. * * Expands to APLOG_USE_MODULE(foo) plus two static functions, * foo_log_server() and foo_log_request(): the oauth2_log_function_t * callbacks of the server and request log sinks created by * oauth2_apache_cfg_srv_create() and oauth2_apache_request_context(), * logging "\: \" through ap_log_error() and * ap_log_rerror() with the sink's context as the server_rec or * request_rec. Use it once, near the top of the module source, since * the other macros refer to the functions it generates. * * @param foo the module's short name */ #define OAUTH2_APACHE_LOG(foo) \ \ APLOG_USE_MODULE(foo); \ \ static void foo##_log_server( \ oauth2_log_sink_t *sink, const char *filename, unsigned long line, \ const char *function, oauth2_log_level_t level, const char *msg) \ { \ ap_log_error( \ filename, line, \ aplog_module_index ? *aplog_module_index \ : APLOG_NO_MODULE, \ log_level_log2apache[level], 0, \ (const server_rec *)oauth2_log_sink_ctx_get(sink), \ "%s: %s", function, msg); \ } \ \ static void foo##_log_request( \ oauth2_log_sink_t *sink, const char *filename, unsigned long line, \ const char *function, oauth2_log_level_t level, const char *msg) \ { \ ap_log_rerror( \ filename, line, \ aplog_module_index ? *aplog_module_index \ : APLOG_NO_MODULE, \ log_level_log2apache[level], 0, \ (const request_rec *)oauth2_log_sink_ctx_get(sink), \ "%s: %s", function, msg); \ } /** @} */ /** * @name Parent/child cleanup * The pool cleanups that oauth2_apache_post_config() registers on the * configuration pool; both end in oauth2_shutdown() (util.h), which * releases the global cache, session, libcurl and OpenSSL state. * @{ */ /** * @brief Child cleanup: calls oauth2_shutdown(). * * @param data the server_rec the cleanup was * registered with * @param m the module record * @param package_name_version the module's "\-\" string * @return APR_SUCCESS */ apr_status_t oauth2_apache_child_cleanup(void *data, module *m, const char *package_name_version); /** * @brief Generate foo_child_cleanup(), the apr pool cleanup function * that logs "enter" at debug level and calls * oauth2_apache_child_cleanup() for foo_module and * OAUTH2_PACKAGE_NAME_VERSION. */ #define OAUTH2_APACHE_CHILD_CLEANUP(foo) \ static apr_status_t foo##_child_cleanup(void *data) \ { \ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \ (const server_rec *)data, "%s: %s", __FUNCTION__, \ "enter"); \ return oauth2_apache_child_cleanup( \ data, &foo##_module, OAUTH2_PACKAGE_NAME_VERSION); \ } /** * @brief Parent cleanup: logs the module shutdown through the server * config's log and calls oauth2_apache_child_cleanup(). * * @param data the server_rec the cleanup was * registered with * @param m the module record, used to look up the * module's oauth2_apache_cfg_srv_t * @param package_name_version the module's "\-\" string * @return APR_SUCCESS */ apr_status_t oauth2_apache_parent_cleanup(void *data, module *m, const char *package_name_version); /** * @brief Generate foo_parent_cleanup(), the apr pool cleanup function * that logs "enter" at debug level and calls * oauth2_apache_parent_cleanup() for foo_module and * OAUTH2_PACKAGE_NAME_VERSION. */ #define OAUTH2_APACHE_PARENT_CLEANUP(foo) \ static apr_status_t foo##_parent_cleanup(void *data) \ { \ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \ (const server_rec *)data, "%s: %s", __FUNCTION__, \ "enter"); \ return oauth2_apache_parent_cleanup( \ data, &foo##_module, OAUTH2_PACKAGE_NAME_VERSION); \ } /** @} */ /** * @name Post config * @{ */ /** * @brief The post_config hook implementation shared by all modules. * * Does nothing on httpd's first, configuration-checking pass through * post_config, which it detects through a marker it leaves in the * process pool's userdata under @p package_name_version. On the * second pass it initializes the library with oauth2_init() (OpenSSL * and libcurl), sets the level of every virtual host's server log * sink to the LogLevel that is only known by now, registers * @p parent_cleanup and @p child_cleanup as the plain and the child * cleanup of @p pool (see apr_pool_cleanup_register()), retrieves * mod_ssl's ssl_var_lookup optional function - used to obtain the * client certificate for mTLS-bound tokens - and logs an "init" * message with the module and library versions. * * @param pool the configuration pool * @param p1 the log pool * @param p2 the temporary pool * @param s the main server record * @param m the module record * @param package_name_version the module's "\-\" string * @param parent_cleanup the parent cleanup function * @param child_cleanup the child cleanup function * @return OK */ int oauth2_apache_post_config(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2, server_rec *s, module *m, const char *package_name_version, apr_status_t (*parent_cleanup)(void *), apr_status_t (*child_cleanup)(void *)); /** @brief The name of the generated post_config hook: foo_post_config. */ #define OAUTH2_APACHE_POST_CONFIG(foo) foo##_post_config /** * @brief Generate foo_post_config(), a static function with httpd's * post_config hook signature that logs "enter" at debug level * and calls oauth2_apache_post_config() for foo_module, * OAUTH2_PACKAGE_NAME_VERSION and the foo_parent_cleanup() and * foo_child_cleanup() functions. * * The module registers it from foo_register_hooks() with * ap_hook_post_config(OAUTH2_APACHE_POST_CONFIG(foo), ...). */ #define OAUTH2_APACHE_POST_CONFIG_IMPL(foo) \ static apr_status_t OAUTH2_APACHE_POST_CONFIG(foo)( \ apr_pool_t * pool, apr_pool_t * p1, apr_pool_t * p2, \ server_rec * s) \ { \ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, \ (const server_rec *)s, "%s: %s", __FUNCTION__, \ "enter"); \ return oauth2_apache_post_config( \ pool, p1, p2, s, &foo##_module, \ OAUTH2_PACKAGE_NAME_VERSION, foo##_parent_cleanup, \ foo##_child_cleanup); \ } /** @} */ /** * @name Directory config * Directive handlers and per-directory configuration. * OAUTH2_APACHE_CMD_ARGS1(), OAUTH2_APACHE_CMD_ARGS2(), * OAUTH2_APACHE_CMD_ARGS3() and OAUTH2_APACHE_CMD_ARGSV4() each * generate a static directive handler apache_\_set_\() * with the signature of an AP_INIT_TAKE1, TAKE2, TAKE3 or TAKE_ARGV * command: it fetches the module's oauth2_apache_cfg_srv_t for its * log and hands the directive's arguments to a liboauth2 configuration * setter as func(log, member, v1, ...), which returns NULL on success * or an error string that httpd reports as a configuration error. * OAUTH2_APACHE_CMD_ARGS() then builds the command_rec entry that * refers to such a handler. * @{ */ /** * @brief Generate the handler for a directive taking one argument. * * @param module the module's short name (the module record is * module_module) * @param type the module's per-directory config struct type * @param primitive the name of the generated handler * apache_\_set_\(); pass the same * name as the "member" argument of * OAUTH2_APACHE_CMD_ARGS() * @param func the setter to call, returning NULL on success or an * error string, as the oauth2_cfg_*_set* functions in * cfg.h do * @param member the setter's target: an expression evaluated inside * the handler with the per-directory config in scope * as "cfg" (e.g. cfg->source_token or &cfg->verify), * or NULL for global setters such as * oauth2_cfg_set_cache() */ #define OAUTH2_APACHE_CMD_ARGS1(module, type, primitive, func, member) \ static const char *apache_##module##_set_##primitive( \ cmd_parms *cmd, void *m, const char *v1) \ { \ oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \ cmd->server->module_config, &module##_module); \ type *cfg = (type *)m; \ (void)cfg; \ return func(srv_cfg->log, member, v1); \ } /** * @brief Generate the handler for a directive taking two arguments; * the parameters are as for OAUTH2_APACHE_CMD_ARGS1(). */ #define OAUTH2_APACHE_CMD_ARGS2(module, type, primitive, func, member) \ static const char *apache_##module##_set_##primitive( \ cmd_parms *cmd, void *m, const char *v1, const char *v2) \ { \ oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \ cmd->server->module_config, &module##_module); \ type *cfg = (type *)m; \ (void)cfg; \ return func(srv_cfg->log, member, v1, v2); \ } /** * @brief Generate the handler for a directive taking three arguments; * the parameters are as for OAUTH2_APACHE_CMD_ARGS1(). */ #define OAUTH2_APACHE_CMD_ARGS3(module, type, primitive, func, member) \ static const char *apache_##module##_set_##primitive( \ cmd_parms *cmd, void *m, const char *v1, const char *v2, \ const char *v3) \ { \ oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \ cmd->server->module_config, &module##_module); \ type *cfg = (type *)m; \ (void)cfg; \ return func(srv_cfg->log, member, v1, v2, v3); \ } /** * @brief Generate the handler for a directive taking a variable number * of arguments (AP_INIT_TAKE_ARGV), up to four; the arguments * not given are passed to the setter as NULL. The parameters * are as for OAUTH2_APACHE_CMD_ARGS1(). */ #define OAUTH2_APACHE_CMD_ARGSV4(module, type, primitive, func, member) \ static const char *apache_##module##_set_##primitive( \ cmd_parms *cmd, void *m, int argc, char *const argv[]) \ { \ oauth2_apache_cfg_srv_t *srv_cfg = ap_get_module_config( \ cmd->server->module_config, &module##_module); \ type *cfg = (type *)m; \ (void)cfg; \ return func(srv_cfg->log, member, argc > 0 ? argv[0] : NULL, \ argc > 1 ? argv[1] : NULL, \ argc > 2 ? argv[2] : NULL, \ argc > 3 ? argv[3] : NULL); \ } /** * @brief Generate a command_rec entry for a directive. * * Expands to AP_INIT_TAKE\(cmd, apache_\_set_\, * ...), allowing the directive in the server config, in directory and * location sections and in .htaccess files with AuthConfig override * (RSRC_CONF | ACCESS_CONF | OR_AUTHCFG). * * @param module the module's short name * @param nargs the suffix of the AP_INIT_TAKE macro to use: 1, 2, 3, * 12, 23, _ARGV, ...; must accept the number of * arguments the handler was generated for * @param cmd the directive name, e.g. "OAuth2TokenVerify" * @param member the "primitive" name the handler was generated with * @param desc the directive's description, shown by httpd -L */ #define OAUTH2_APACHE_CMD_ARGS(module, nargs, cmd, member, desc) \ AP_INIT_TAKE##nargs(cmd, apache_##module##_set_##member, NULL, \ RSRC_CONF | ACCESS_CONF | OR_AUTHCFG, desc) /** * @brief The name of a per-directory config function generated by * OAUTH2_APACHE_DIR_CTX_FUNCS(): oauth2_\_dir_\. */ #define OAUTH2_APACHE_DIR_CTX(type, method) oauth2_##type##_dir_##method /** * @brief Generate the per-directory config functions for a config type. * * For a per-directory config type following the library's object * conventions - oauth2_\_t with oauth2_\_create(log, path), * oauth2_\_merge(log, cfg, base, add) and oauth2_\_free(log, * cfg) - generates oauth2_\_dir_create() (allocates a config for * the directory or location path and registers its release as a pool * cleanup) and the static oauth2_\_dir_merge() (creates a new * config and merges the base and the more specific config into it), * the create_dir_config and merge_dir_config functions of the module * record, plus oauth2_\_dir_cleanup(), the pool cleanup. * * @param type the config type's name, e.g. sts_cfg for oauth2_sts_cfg_t */ #define OAUTH2_APACHE_DIR_CTX_FUNCS(type) \ apr_status_t OAUTH2_APACHE_DIR_CTX(type, cleanup)(void *data) \ { \ oauth2_##type##_t *cfg = (oauth2_##type##_t *)data; \ oauth2_##type##_free(NULL, cfg); \ return APR_SUCCESS; \ } \ \ void *OAUTH2_APACHE_DIR_CTX(type, create)(apr_pool_t * pool, \ char *path) \ { \ oauth2_##type##_t *cfg = oauth2_##type##_create(NULL, path); \ apr_pool_cleanup_register( \ pool, cfg, OAUTH2_APACHE_DIR_CTX(type, cleanup), \ OAUTH2_APACHE_DIR_CTX(type, cleanup)); \ return cfg; \ } \ \ static void *OAUTH2_APACHE_DIR_CTX(type, merge)(apr_pool_t * pool, \ void *b, void *a) \ { \ oauth2_##type##_t *cfg = \ OAUTH2_APACHE_DIR_CTX(type, create)(pool, NULL); \ oauth2_##type##_t *base = b; \ oauth2_##type##_t *add = a; \ oauth2_##type##_merge(NULL, cfg, base, add); \ return cfg; \ } /** @} */ /** * @name Server config * The per-server (virtual host) config this binding keeps for a * module: the server-scoped log sink and log handle, used where there * is no request, e.g. in the directive handlers and at shutdown. * @{ */ typedef struct oauth2_apache_cfg_srv_t { /** @brief The server log sink: foo_log_server() with the server_rec * as context. */ oauth2_log_sink_t *sink; /** @brief The server-scoped log handle. */ oauth2_log_t *log; /** @brief Unused. */ bool is_child; } oauth2_apache_cfg_srv_t; /** * @brief Create a server config, the module record's * create_server_config function. * * Creates the log sink with @p server_log_cb and @p s as its context * and the log handle, at the server's LogLevel when already known and * at trace1 otherwise (oauth2_apache_post_config() sets the definitive * level later), and registers their release as a cleanup of @p pool. * * @param pool the pool to register the cleanup on * @param s the server record * @param server_log_cb the foo_log_server() function generated by * OAUTH2_APACHE_LOG() * @return the new oauth2_apache_cfg_srv_t */ void *oauth2_apache_cfg_srv_create(apr_pool_t *pool, server_rec *s, oauth2_log_function_t server_log_cb); /** * @brief Merge server configs, the module record's * merge_server_config function: creates a fresh config for the * virtual host's server_rec. * * @param pool the pool to register the cleanup on * @param b the base (main server) config * @param a the virtual host's config * @return the new oauth2_apache_cfg_srv_t */ void *oauth2_apache_cfg_srv_merge(apr_pool_t *pool, void *b, void *a); /** @} */ /** * @name Handlers * @{ */ /** * @brief Generate the three hook functions a module gets from this * binding: OAUTH2_APACHE_CHILD_CLEANUP(), * OAUTH2_APACHE_PARENT_CLEANUP() and * OAUTH2_APACHE_POST_CONFIG_IMPL() for foo. */ #define OAUTH2_APACHE_HANDLERS(foo) \ OAUTH2_APACHE_CHILD_CLEANUP(foo) \ OAUTH2_APACHE_PARENT_CLEANUP(foo) \ OAUTH2_APACHE_POST_CONFIG_IMPL(foo) /** @} */ /** * @name Module config * @{ */ /** @brief The name of the module's command_rec table: foo_commands. */ #define OAUTH2_APACHE_COMMANDS(foo) foo##_commands /** * @brief The name of the module's hook registration function: * foo_register_hooks. */ #define OAUTH2_APACHE_REGISTER_HOOKS(foo) foo##_register_hooks /** * @brief Generate the module record. * * Defines oauth2_apache_foo_cfg_srv_create(), which binds * oauth2_apache_cfg_srv_create() to foo_log_server(), and the exported * module record foo_module with the given per-directory config * functions, the binding's server config create and merge functions, * the command table foo_commands and the hook registration function * foo_register_hooks. The module must have defined the command table * and the hook function, as well as OAUTH2_APACHE_LOG(foo) and * OAUTH2_APACHE_HANDLERS(foo), before using this macro. * * @param foo the module's short name * @param dir_create the create_dir_config function * @param dir_merge the merge_dir_config function */ #define OAUTH2_APACHE_MODULE_DECLARE_EX(foo, dir_create, dir_merge) \ \ void *oauth2_apache_##foo##_cfg_srv_create(apr_pool_t *pool, \ server_rec *s) \ { \ return oauth2_apache_cfg_srv_create(pool, s, \ foo##_log_server); \ } \ \ module AP_MODULE_DECLARE_DATA foo##_module = { \ STANDARD20_MODULE_STUFF, \ dir_create, \ dir_merge, \ oauth2_apache_##foo##_cfg_srv_create, \ oauth2_apache_cfg_srv_merge, \ OAUTH2_APACHE_COMMANDS(foo), \ OAUTH2_APACHE_REGISTER_HOOKS(foo)}; /** * @brief Generate the per-directory config functions with * OAUTH2_APACHE_DIR_CTX_FUNCS() for @p type and the module * record with OAUTH2_APACHE_MODULE_DECLARE_EX() using them. */ #define OAUTH2_APACHE_MODULE_DECLARE(foo, type) \ \ OAUTH2_APACHE_DIR_CTX_FUNCS(type) \ \ OAUTH2_APACHE_MODULE_DECLARE_EX(foo, \ OAUTH2_APACHE_DIR_CTX(type, create), \ OAUTH2_APACHE_DIR_CTX(type, merge)) /** @} */ /** * @name Request context * The per-request state this binding keeps for a module: a * request-scoped log handle, whose sink is foo_log_request() with the * request_rec as context, and the abstract oauth2_http_request_t * populated from the request_rec, on which the core operates. * @{ */ /** * @brief Get the request context for @p r, calling * oauth2_apache_request_context() with foo_log_request() and a * userdata key derived from the module name. */ #define OAUTH2_APACHE_REQUEST_CTX(r, foo) \ oauth2_apache_request_context( \ r, foo##_log_request, \ "oauth2_" OAUTH2_TOSTRING(foo) "_module_user_data_key"); typedef struct oauth2_apache_request_ctx_t { /** @brief The request-scoped log handle. */ oauth2_log_t *log; /** @brief The abstract request populated from r. */ oauth2_http_request_t *request; /** @brief The (main) request record. */ request_rec *r; } oauth2_apache_request_ctx_t; /** * @brief Get, creating it on first use, the request context. * * The context is created once per main request - for a subrequest the * main request's context is returned - and cached in the request * pool's userdata under @p user_data_key, with a cleanup that releases * it when the request is done. On creation the log handle is set up * at the request's LogLevel and the abstract request is populated * with the scheme, the server name as used in self-referential URLs, * the local port, the URI path, the method, the query string and all * incoming headers. The client's TLS certificate, looked up through * mod_ssl's ssl_var_lookup as SSL_CLIENT_CERT, is stored in the * request's context under OAUTH2_TLS_CERT_VAR_NAME (http.h) for * verifying mTLS-bound tokens, since mod_ssl's own environment * variables only become available in the fixups phase. * * @param r the request record * @param request_log_cb the foo_log_request() function generated by * OAUTH2_APACHE_LOG() * @param user_data_key the pool userdata key to cache the context * under, unique per module * @return the request context, owned by the request pool */ oauth2_apache_request_ctx_t * oauth2_apache_request_context(request_rec *r, oauth2_log_function_t request_log_cb, const char *user_data_key); /** @} */ /** * @name Request and response handling * @{ */ /** * @brief Apply an abstract request back onto the request_rec. * * Sets every header of @p request as an incoming header of @p r, * overwriting headers of the same name, and replaces the request's * query string with the one in @p request. Used after * oauth2_get_source_token() (proto.h) stripped the token from the * request, so that the target application does not see it. * * @param log the log handle to use * @param request the abstract request * @param r the request record to update * @return true on success, false when @p request is NULL */ bool oauth2_apache_http_request_set(oauth2_log_t *log, oauth2_http_request_t *request, request_rec *r); /** * @brief Add a WWW-Authenticate challenge to the response and return * the status code to end the handler with. * * The challenge's scheme is "Basic" when the source token * configuration accepts the token in the basic authentication header * only and "Bearer" otherwise; the AuthName of the location, when * set, is added as its realm and @p error and @p error_description, * when not NULL, as the RFC 6750 section 3 attributes. The header is * added to the error headers, so it is sent with the error response. * * @param cfg the source token configuration * @param ctx the request context * @param status_code the status code to return, typically * HTTP_UNAUTHORIZED * @param error the "error" attribute, e.g. * OAUTH2_ERROR_INVALID_TOKEN (oauth2.h), or * NULL * @param error_description the "error_description" attribute, or NULL * @return @p status_code */ int oauth2_apache_return_www_authenticate(oauth2_cfg_source_token_t *cfg, oauth2_apache_request_ctx_t *ctx, int status_code, const char *error, const char *error_description); /** * @brief Set an incoming request header, overwriting an existing one. * * @param log the log handle to use * @param rec the request_rec, typed void so this can serve as the * callback of oauth2_http_request_headers_loop() * @param name the header name * @param value the header value * @return true */ bool oauth2_apache_request_header_set(oauth2_log_t *log, void *rec, const char *name, const char *value); /** * @brief Add an outgoing response header. * * The header goes into the request's err_headers_out table, so that it * is sent with error responses as well and survives internal * redirects. * * @param log the log handle to use * @param r the request record * @param name the header name * @param value the header value */ void oauth2_apache_hdr_out_add(oauth2_log_t *log, const request_rec *r, const char *name, const char *value); /** * @brief Remove incoming headers a client could use to spoof claims. * * When the target pass configuration passes claims as headers, drops * every incoming request header whose name starts with the configured * header prefix, and the configured authentication header, from the * request, logging a warning for each; nothing is done when claims * are passed as environment variables only. Call before passing * claims with oauth2_apache_target_pass(). * * @param ctx the request context * @param target_pass the target pass configuration */ void oauth2_apache_scrub_headers(oauth2_apache_request_ctx_t *ctx, oauth2_cfg_target_pass_t *target_pass); /** * @brief Set the request's authenticated user from the token claims. * * Sets r->user to the value of the string claim named by the target * pass configuration's "remote_user_claim" option (default "sub"). * * @param target_pass the target pass configuration * @param ctx the request context * @param json_token the claims of the verified token * @return true when the user was set, false - having logged an error - * when either argument is NULL, no remote user claim is * configured, or the claim is absent or not a string */ bool oauth2_apache_set_request_user(oauth2_cfg_target_pass_t *target_pass, oauth2_apache_request_ctx_t *ctx, json_t *json_token); /** * @brief Pass the token and its claims on to the target application. * * Per the target pass configuration, passes: * - the request's user (r->user) in the configured authentication * header, when both are set; * - each claim of @p json_token as a header and/or an environment * variable named "\\", the name with characters * that are not allowed in a header name replaced by "-", and the * value - non-string claims JSON-encoded - encoded per the * configured encoding ("none", "latin1" or "base64url"); * - @p target_token, when not NULL, as "\access_token"; * - when a JSON payload claim name is configured, all claims as one * compact JSON object under that name. * * @param ctx the request context * @param target_pass the target pass configuration * @param target_token the token to pass on, or NULL * @param json_token the claims to pass on */ void oauth2_apache_target_pass(oauth2_apache_request_ctx_t *ctx, oauth2_cfg_target_pass_t *target_pass, const char *target_token, json_t *json_token); /** * @brief Apply an abstract response onto the request_rec. * * Adds every header of @p response, including any Set-Cookie headers, * to the request's error headers with oauth2_apache_hdr_out_add() and * sets r->status to the response's status code. No body is written: * the handler returns the status code to let httpd generate the * response, as for the redirects and error responses of the OpenID * Connect RP flow (openidc.h). * * @param log the log handle to use * @param response the abstract response * @param r the request record to update * @return true on success, false when either argument is NULL */ bool oauth2_apache_http_response_set(oauth2_log_t *log, oauth2_http_response_t *response, request_rec *r); /** * @brief Store a JSON object in the request state. * * The request state is a table in the main request's pool userdata, * shared by all modules using this binding and by the subrequests of * the main request; the object is stored serialized. * * @param ctx the request context * @param key the key to store the object under * @param claims the JSON object to store */ void oauth2_apache_request_state_set_json(oauth2_apache_request_ctx_t *ctx, const char *key, json_t *claims); /** * @brief Retrieve a JSON object from the request state. * * @param ctx the request context * @param key the key the object was stored under * @param claims when an object is stored under @p key, set to a newly * decoded copy of it, to be released with json_decref(); * left untouched otherwise */ void oauth2_apache_request_state_get_json(oauth2_apache_request_ctx_t *ctx, const char *key, json_t **claims); /** @} */ /** * @name Authorization * Evaluating Require directives against the claims of the verified * token, for use from an authz provider registered with * ap_register_auth_provider(). * @{ */ /** * @brief A function matching one Require specification against the * claims: the request context, the specification and the * claims; oauth2_apache_authz_match_claim() is the default. */ typedef bool (*oauth2_apache_authz_match_claim_fn_type)( oauth2_apache_request_ctx_t *, const char *const, const json_t *const); /** * @brief Match a claim specification against a claims object. * * The specification is a claim name followed by an operator and an * operand: * - "\:\": the claim equals the value; a string claim is * compared case-sensitively, an integer claim against the value * parsed as an integer, a boolean claim against "true" or "false", * and an array claim matches when any of its string, integer or * boolean elements does; * - "\~\": the string claim, or any string element of the * array claim, matches the PCRE regular expression; * - "\.\": for an object claim, \ is matched * recursively against its members; for an array claim, \ is a * value matched against its elements as for ":". * * @param ctx the request context * @param attr_spec the specification, one word of the Require * directive's arguments * @param claims the claims to match against * @return true when a claim matches the specification, false otherwise * or when @p claims is NULL */ bool oauth2_apache_authz_match_claim(oauth2_apache_request_ctx_t *ctx, const char *const attr_spec, const json_t *const claims); /** * @brief Evaluate a Require directive's arguments against the claims. * * Splits @p require_args into its (possibly quoted) words and returns * as soon as one of them matches per @p match_claim_fn, so the words * are alternatives; the only way to require several claims is to use * several Require directives. * * @param ctx the request context * @param claims the claims of the verified token * @param require_args the arguments of the Require directive, as * passed to the authz provider's * check_authorization function * @param match_claim_fn the function matching one word against the * claims, typically * oauth2_apache_authz_match_claim() * @return AUTHZ_DENIED_NO_USER when the request has no authenticated * user, AUTHZ_GRANTED when a word matched, AUTHZ_DENIED * otherwise - also when @p claims is NULL or, with a warning, * when no words were given */ authz_status oauth2_apache_authorize(oauth2_apache_request_ctx_t *ctx, const json_t *const claims, const char *require_args, oauth2_apache_authz_match_claim_fn_type match_claim_fn); /** @} */ #endif /* _OAUTH2_APACHE_H_ */ liboauth2-2.4.0/include/oauth2/mem.h0000664000175000017500000001217015250553546017747 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_MEM_H_ #define _OAUTH2_MEM_H_ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file mem.h * @brief Overridable memory allocation. * * All memory the library allocates goes through a single, replaceable * set of allocator functions, shared with cjose and libcurl: by * default the C library's malloc/realloc/free, or whatever a server * binding installs instead (e.g. to account allocations to a server * pool or to instrument them). Consequently every string, buffer or * object the API hands out must be released with oauth2_mem_free(), * and never with free(), and any memory a caller passes ownership of * to the library must have been obtained from oauth2_mem_alloc(). */ #include /** * @name Allocator functions * The basic allocator interface, with the signatures of malloc(), * realloc() and free(); the getters return the functions currently in * effect. * @{ */ /** @brief Allocate a block of the given size, as malloc(). */ typedef void *(*oauth2_mem_alloc_fn_t)(size_t); /** @brief Resize a block, as realloc(). */ typedef void *(*oauth2_mem_realloc_fn_t)(void *, size_t); /** @brief Release a block, as free(). */ typedef void (*oauth2_mem_dealloc_fn_t)(void *); /** @brief The allocation function currently in effect. */ oauth2_mem_alloc_fn_t oauth2_mem_get_alloc(); /** @brief The reallocation function currently in effect. */ oauth2_mem_realloc_fn_t oauth2_mem_get_realloc(); /** @brief The deallocation function currently in effect. */ oauth2_mem_dealloc_fn_t oauth2_mem_get_dealloc(); /** * @brief Install a custom allocator. * * Call before oauth2_init() (util.h) and before any allocation is * made: the functions are installed into cjose and, through * curl_global_init_mem(), into libcurl, so memory allocated earlier * would be released through the wrong function. * * @param alloc the allocation function * @param realloc the reallocation function * @param dealloc the deallocation function */ void oauth2_mem_set_alloc_funcs(oauth2_mem_alloc_fn_t alloc, oauth2_mem_realloc_fn_t realloc, oauth2_mem_dealloc_fn_t dealloc); /** @} */ /** * @name Extended allocator functions * The same interface with the source file name and line number of the * allocation site as extra arguments, for allocators that track where * memory is allocated (e.g. a debugging allocator or a server pool * API that records the caller). Installing extended functions with * oauth2_mem_set_alloc_ex_funcs() also installs plain wrappers around * them, so oauth2_mem_get_alloc() etc. keep working, and vice versa. * @{ */ /** @brief Allocate a block, given the caller's file and line. */ typedef void *(*oauth2_mem_alloc3_fn_t)(size_t, const char *, int); /** @brief Resize a block, given the caller's file and line. */ typedef void *(*oauth2_mem_realloc3_fn_t)(void *, size_t, const char *, int); /** @brief Release a block, given the caller's file and line. */ typedef void (*oauth2_mem_dealloc3_fn_t)(void *, const char *, int); /** @brief The extended allocation function currently in effect. */ oauth2_mem_alloc3_fn_t oauth2_mem_get_alloc3(); /** @brief The extended reallocation function currently in effect. */ oauth2_mem_realloc3_fn_t oauth2_mem_get_realloc3(); /** @brief The extended deallocation function currently in effect. */ oauth2_mem_dealloc3_fn_t oauth2_mem_get_dealloc3(); /** * @brief Install a custom allocator that receives the allocation * site. * * As oauth2_mem_set_alloc_funcs(), to be called before anything is * allocated; libcurl, which has no such interface, is set up with the * plain wrappers. * * @param alloc3 the allocation function * @param realloc3 the reallocation function * @param dealloc3 the deallocation function */ void oauth2_mem_set_alloc_ex_funcs(oauth2_mem_alloc3_fn_t alloc3, oauth2_mem_realloc3_fn_t realloc3, oauth2_mem_dealloc3_fn_t dealloc3); /** @} */ /** * @name Allocation * @{ */ /** * @brief Allocate a zero-initialized block through the installed * allocator. * * @return the block, or NULL when the allocator returns NULL */ void *oauth2_mem_alloc(size_t); /** * @brief Release memory obtained from oauth2_mem_alloc() or returned * by any library function. * * Whether a NULL pointer is accepted depends on the installed * deallocation function; the default one (free()) ignores it. */ void oauth2_mem_free(void *); /** @} */ #endif /* _OAUTH2_MEM_H_ */ liboauth2-2.4.0/include/oauth2/version.h.in0000664000175000017500000000332415250553546021264 0ustar00hzandbelthzandbelt#ifndef _OAUTH2_VERSION_H_ #define _OAUTH2_VERSION_H_ /* include/oauth2/version.h Generated from version.h.in by autoheader. */ /*************************************************************************** * * Copyright (C) 2018-2026 - ZmartZone Holding BV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @Author: Hans Zandbelt - hans.zandbelt@openidc.com * **************************************************************************/ /** * @file version.h * @brief Library name and version. * * Generated from version.h.in at configure time from the version in * configure.ac; the functions return the same values as the macros, * but for the library that is actually loaded rather than the headers * that a consumer was compiled against. */ /** @brief The package name, "liboauth2". */ #define OAUTH2_PACKAGE_NAME "@PACKAGE_NAME@" /** @brief The package version, e.g. "2.1.0". */ #define OAUTH2_PACKAGE_VERSION "@PACKAGE_VERSION@" /** @brief The version of the library, as OAUTH2_PACKAGE_VERSION. */ const char *oauth2_version(); /** * @brief The library name and version joined with a dash, e.g. * "liboauth2-2.1.0". */ const char *oauth2_package_string(); #endif // _OAUTH2_VERSION_H_ liboauth2-2.4.0/ltmain.sh0000755000175000017500000122276515250556145016030 0ustar00hzandbelthzandbelt#! /usr/bin/env sh ## DO NOT EDIT - This file generated from ./build-aux/ltmain.in ## by inline-source v2019-02-19.15 # libtool (GNU libtool) 2.5.4 # Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 # Copyright (C) 1996-2019, 2021-2024 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, # if you distribute this file as part of a program or library that # is built using GNU Libtool, you may include this file under the # same distribution terms that you use for the rest of that program. # # GNU Libtool is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . PROGRAM=libtool PACKAGE=libtool VERSION="2.5.4 Debian-2.5.4-9" package_revision=2.5.4 ## ------ ## ## Usage. ## ## ------ ## # Run './libtool --help' for help with using this script from the # command line. ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # After configure completes, it has a better idea of some of the # shell tools we need than the defaults used by the functions shared # with bootstrap, so set those here where they can still be over- # ridden by the user, but otherwise take precedence. : ${AUTOCONF="autoconf"} : ${AUTOMAKE="automake"} ## -------------------------- ## ## Source external libraries. ## ## -------------------------- ## # Much of our low-level functionality needs to be sourced from external # libraries, which are installed to $pkgauxdir. # Set a version string for this script. scriptversion=2024-12-01.17; # UTC # General shell script boiler plate, and helper functions. # Written by Gary V. Vaughan, 2004 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2004-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # ## ------ ## ## Usage. ## ## ------ ## # Evaluate this file near the top of your script to gain access to # the functions and variables defined here: # # . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh # # If you need to override any of the default environment variable # settings, do that before evaluating this file. ## -------------------- ## ## Shell normalisation. ## ## -------------------- ## # Some shells need a little help to be as Bourne compatible as possible. # Before doing anything else, make sure all that help has been provided! DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi # NLS nuisances: We save the old values in case they are required later. _G_user_locale= _G_safe_locale= for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test set = \"\${$_G_var+set}\"; then save_$_G_var=\$$_G_var $_G_var=C export $_G_var _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done # These NLS vars are set unconditionally (bootstrap issue #24). Unset those # in case the environment reset is needed later and the $save_* variant is not # defined (see the code above). LC_ALL=C LANGUAGE=C export LANGUAGE LC_ALL # Make sure IFS has a sensible default sp=' ' nl=' ' IFS="$sp $nl" # There are apparently some systems that use ';' as a PATH separator! if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # func_unset VAR # -------------- # Portably unset VAR. # In some shells, an 'unset VAR' statement leaves a non-zero return # status if VAR is already unset, which might be problematic if the # statement is used at the end of a function (thus poisoning its return # value) or when 'set -e' is active (causing even a spurious abort of # the script in this case). func_unset () { { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; } } # Make sure CDPATH doesn't cause `cd` commands to output the target dir. func_unset CDPATH # Make sure ${,E,F}GREP behave sanely. func_unset GREP_OPTIONS ## ------------------------- ## ## Locate command utilities. ## ## ------------------------- ## # func_executable_p FILE # ---------------------- # Check that FILE is an executable regular file. func_executable_p () { test -f "$1" && test -x "$1" } # func_path_progs PROGS_LIST CHECK_FUNC [PATH] # -------------------------------------------- # Search for either a program that responds to --version with output # containing "GNU", or else returned by CHECK_FUNC otherwise, by # trying all the directories in PATH with each of the elements of # PROGS_LIST. # # CHECK_FUNC should accept the path to a candidate program, and # set $func_check_prog_result if it truncates its output less than # $_G_path_prog_max characters. func_path_progs () { _G_progs_list=$1 _G_check_func=$2 _G_PATH=${3-"$PATH"} _G_path_prog_max=0 _G_path_prog_found=false _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} for _G_dir in $_G_PATH; do IFS=$_G_save_IFS test -z "$_G_dir" && _G_dir=. for _G_prog_name in $_G_progs_list; do for _exeext in '' .EXE; do _G_path_prog=$_G_dir/$_G_prog_name$_exeext func_executable_p "$_G_path_prog" || continue case `"$_G_path_prog" --version 2>&1` in *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; *) $_G_check_func $_G_path_prog func_path_progs_result=$func_check_prog_result ;; esac $_G_path_prog_found && break 3 done done done IFS=$_G_save_IFS test -z "$func_path_progs_result" && { echo "no acceptable sed could be found in \$PATH" >&2 exit 1 } } # We want to be able to use the functions in this file before configure # has figured out where the best binaries are kept, which means we have # to search for them ourselves - except when the results are already set # where we skip the searches. # Unless the user overrides by setting SED, search the path for either GNU # sed, or the sed that truncates its output the least. test -z "$SED" && { _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for _G_i in 1 2 3 4 5 6 7; do _G_sed_script=$_G_sed_script$nl$_G_sed_script done echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed _G_sed_script= func_check_prog_sed () { _G_path_prog=$1 _G_count=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo '' >> conftest.nl "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin" rm -f conftest.sed SED=$func_path_progs_result } # Unless the user overrides by setting GREP, search the path for either GNU # grep, or the grep that truncates its output the least. test -z "$GREP" && { func_check_prog_grep () { _G_path_prog=$1 _G_count=0 _G_path_prog_max=0 printf 0123456789 >conftest.in while : do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo 'GREP' >> conftest.nl "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break diff conftest.out conftest.nl >/dev/null 2>&1 || break _G_count=`expr $_G_count + 1` if test "$_G_count" -gt "$_G_path_prog_max"; then # Best one so far, save it but keep looking for a better one func_check_prog_result=$_G_path_prog _G_path_prog_max=$_G_count fi # 10*(2^10) chars as input seems more than enough test 10 -lt "$_G_count" && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out } func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin" GREP=$func_path_progs_result } ## ------------------------------- ## ## User overridable command paths. ## ## ------------------------------- ## # All uppercase variable names are used for environment variables. These # variables can be overridden by the user before calling a script that # uses them if a suitable command of that name is not already available # in the command search PATH. : ${CP="cp -f"} : ${ECHO="printf %s\n"} : ${EGREP="$GREP -E"} : ${FGREP="$GREP -F"} : ${LN_S="ln -s"} : ${MAKE="make"} : ${MKDIR="mkdir"} : ${MV="mv -f"} : ${RM="rm -f"} : ${SHELL="${CONFIG_SHELL-/bin/sh}"} ## -------------------- ## ## Useful sed snippets. ## ## -------------------- ## sed_dirname='s|/[^/]*$||' sed_basename='s|^.*/||' # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='s|\([`"$\\]\)|\\\1|g' # Same as above, but do not quote variable references. sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution that turns a string into a regex matching for the # string literally. sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' # Sed substitution that converts a w32 file name or path # that contains forward slashes, into one that contains # (escaped) backslashes. A very naive implementation. sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' # Re-'\' parameter expansions in output of sed_double_quote_subst that # were '\'-ed in input to the same. If an odd number of '\' preceded a # '$' in input to sed_double_quote_subst, that '$' was protected from # expansion. Since each input '\' is now two '\'s, look for any number # of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. _G_bs='\\' _G_bs2='\\\\' _G_bs4='\\\\\\\\' _G_dollar='\$' sed_double_backslash="\ s/$_G_bs4/&\\ /g s/^$_G_bs2$_G_dollar/$_G_bs&/ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g s/\n//g" # require_check_ifs_backslash # --------------------------- # Check if we can use backslash as IFS='\' separator, and set # $check_ifs_backshlash_broken to ':' or 'false'. require_check_ifs_backslash=func_require_check_ifs_backslash func_require_check_ifs_backslash () { _G_save_IFS=$IFS IFS='\' _G_check_ifs_backshlash='a\\b' for _G_i in $_G_check_ifs_backshlash do case $_G_i in a) check_ifs_backshlash_broken=false ;; '') break ;; *) check_ifs_backshlash_broken=: break ;; esac done IFS=$_G_save_IFS require_check_ifs_backslash=: } ## ----------------- ## ## Global variables. ## ## ----------------- ## # Except for the global variables explicitly listed below, the following # functions in the '^func_' namespace, and the '^require_' namespace # variables initialised in the 'Resource management' section, sourcing # this file will not pollute your global namespace with anything # else. There's no portable way to scope variables in Bourne shell # though, so actually running these functions will sometimes place # results into a variable named after the function, and often use # temporary variables in the '^_G_' namespace. If you are careful to # avoid using those namespaces casually in your sourcing script, things # should continue to work as you expect. And, of course, you can freely # overwrite any of the functions or variables defined here before # calling anything to customize them. EXIT_SUCCESS=0 EXIT_FAILURE=1 EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. # Allow overriding, eg assuming that you follow the convention of # putting '$debug_cmd' at the start of all your functions, you can get # bash to show function call trace with: # # debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name debug_cmd=${debug_cmd-":"} exit_cmd=: # By convention, finish your script with: # # exit $exit_status # # so that you can set exit_status to non-zero if you want to indicate # something went wrong during execution without actually bailing out at # the point of failure. exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. progpath=$0 # The name of this program. progname=`$ECHO "$progpath" |$SED "$sed_basename"` # Make sure we have an absolute progpath for reexecution: case $progpath in [\\/]*|[A-Za-z]:\\*) ;; *[\\/]*) progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` progdir=`cd "$progdir" && pwd` progpath=$progdir/$progname ;; *) _G_IFS=$IFS IFS=${PATH_SEPARATOR-:} for progdir in $PATH; do IFS=$_G_IFS test -x "$progdir/$progname" && break done IFS=$_G_IFS test -n "$progdir" || progdir=`pwd` progpath=$progdir/$progname ;; esac ## ----------------- ## ## Standard options. ## ## ----------------- ## # The following options affect the operation of the functions defined # below, and should be set appropriately depending on run-time para- # meters passed on the command line. opt_dry_run=false opt_quiet=false opt_verbose=false # Categories 'all' and 'none' are always available. Append any others # you will pass as the first argument to func_warning from your own # code. warning_categories= # By default, display warnings according to 'opt_warning_types'. Set # 'warning_func' to ':' to elide all warnings, or func_fatal_error to # treat the next displayed warning as a fatal error. warning_func=func_warn_and_continue # Set to 'all' to display all warnings, 'none' to suppress all # warnings, or a space delimited list of some subset of # 'warning_categories' to display only the listed warnings. opt_warning_types=all ## -------------------- ## ## Resource management. ## ## -------------------- ## # This section contains definitions for functions that each ensure a # particular resource (a file, or a non-empty configuration variable for # example) is available, and if appropriate to extract default values # from pertinent package files. Call them using their associated # 'require_*' variable to ensure that they are executed, at most, once. # # It's entirely deliberate that calling these functions can set # variables that don't obey the namespace limitations obeyed by the rest # of this file, in order that that they be as useful as possible to # callers. # require_term_colors # ------------------- # Allow display of bold text on terminals that support it. require_term_colors=func_require_term_colors func_require_term_colors () { $debug_cmd test -t 1 && { # COLORTERM and USE_ANSI_COLORS environment variables take # precedence, because most terminfo databases neglect to describe # whether color sequences are supported. test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} if test 1 = "$USE_ANSI_COLORS"; then # Standard ANSI escape sequences tc_reset='' tc_bold=''; tc_standout='' tc_red=''; tc_green='' tc_blue=''; tc_cyan='' else # Otherwise trust the terminfo database after all. test -n "`tput sgr0 2>/dev/null`" && { tc_reset=`tput sgr0` test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` tc_standout=$tc_bold test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` } fi } require_term_colors=: } ## ----------------- ## ## Function library. ## ## ----------------- ## # This section contains a variety of useful functions to call in your # scripts. Take note of the portable wrappers for features provided by # some modern shells, which will fall back to slower equivalents on # less featureful shells. # func_append VAR VALUE # --------------------- # Append VALUE onto the existing contents of VAR. # _G_HAVE_PLUSEQ_OP # Can be empty, in which case the shell is probed, "yes" if += is # usable or anything else if it does not work. if test -z "$_G_HAVE_PLUSEQ_OP" && \ __PLUSEQ_TEST="a" && \ __PLUSEQ_TEST+=" b" 2>/dev/null && \ test "a b" = "$__PLUSEQ_TEST"; then _G_HAVE_PLUSEQ_OP=yes fi if test yes = "$_G_HAVE_PLUSEQ_OP" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_append () { $debug_cmd eval "$1+=\$2" }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_append () { $debug_cmd eval "$1=\$$1\$2" } fi # func_append_quoted VAR VALUE # ---------------------------- # Quote VALUE and append to the end of shell variable VAR, separated # by a space. if test yes = "$_G_HAVE_PLUSEQ_OP"; then eval 'func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1+=\\ \$func_quote_arg_result" }' else func_append_quoted () { $debug_cmd func_quote_arg pretty "$2" eval "$1=\$$1\\ \$func_quote_arg_result" } fi # func_append_uniq VAR VALUE # -------------------------- # Append unique VALUE onto the existing contents of VAR, assuming # entries are delimited by the first character of VALUE. For example: # # func_append_uniq options " --another-option option-argument" # # will only append to $options if " --another-option option-argument " # is not already present somewhere in $options already (note spaces at # each end implied by leading space in second argument). func_append_uniq () { $debug_cmd eval _G_current_value='`$ECHO $'$1'`' _G_delim=`expr "$2" : '\(.\)'` case $_G_delim$_G_current_value$_G_delim in *"$2$_G_delim"*) ;; *) func_append "$@" ;; esac } # func_arith TERM... # ------------------ # Set func_arith_result to the result of evaluating TERMs. test -z "$_G_HAVE_ARITH_OP" \ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ && _G_HAVE_ARITH_OP=yes if test yes = "$_G_HAVE_ARITH_OP"; then eval 'func_arith () { $debug_cmd func_arith_result=$(( $* )) }' else func_arith () { $debug_cmd func_arith_result=`expr "$@"` } fi # func_basename FILE # ------------------ # Set func_basename_result to FILE with everything up to and including # the last / stripped. if test yes = "$_G_HAVE_XSI_OPS"; then # If this shell supports suffix pattern removal, then use it to avoid # forking. Hide the definitions single quotes in case the shell chokes # on unsupported syntax... _b='func_basename_result=${1##*/}' _d='case $1 in */*) func_dirname_result=${1%/*}$2 ;; * ) func_dirname_result=$3 ;; esac' else # ...otherwise fall back to using sed. _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` if test "X$func_dirname_result" = "X$1"; then func_dirname_result=$3 else func_append func_dirname_result "$2" fi' fi eval 'func_basename () { $debug_cmd '"$_b"' }' # func_dirname FILE APPEND NONDIR_REPLACEMENT # ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. eval 'func_dirname () { $debug_cmd '"$_d"' }' # func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT # -------------------------------------------------------- # Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result # to NONDIR_REPLACEMENT. # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value returned in "$func_basename_result" # For efficiency, we do not delegate to the functions above but instead # duplicate the functionality here. eval 'func_dirname_and_basename () { $debug_cmd '"$_b"' '"$_d"' }' # func_echo ARG... # ---------------- # Echo program name prefixed message. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname: $_G_line" done IFS=$func_echo_IFS } # func_echo_all ARG... # -------------------- # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } # func_echo_infix_1 INFIX ARG... # ------------------------------ # Echo program name, followed by INFIX on the first line, with any # additional lines not showing INFIX. func_echo_infix_1 () { $debug_cmd $require_term_colors _G_infix=$1; shift _G_indent=$_G_infix _G_prefix="$progname: $_G_infix: " _G_message=$* # Strip color escape sequences before counting printable length for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" do test -n "$_G_tc" && { _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` } done _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes func_echo_infix_1_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_infix_1_IFS $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 _G_prefix=$_G_indent done IFS=$func_echo_infix_1_IFS } # func_error ARG... # ----------------- # Echo program name prefixed message to standard error. func_error () { $debug_cmd $require_term_colors func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 } # func_fatal_error ARG... # ----------------------- # Echo program name prefixed message to standard error, and exit. func_fatal_error () { $debug_cmd func_error "$*" exit $EXIT_FAILURE } # func_grep EXPRESSION FILENAME # ----------------------------- # Check whether EXPRESSION matches any line of FILENAME, without output. func_grep () { $debug_cmd $GREP "$1" "$2" >/dev/null 2>&1 } # func_len STRING # --------------- # Set func_len_result to the length of STRING. STRING may not # start with a hyphen. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_len () { $debug_cmd func_len_result=${#1} }' else func_len () { $debug_cmd func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } fi # func_mkdir_p DIRECTORY-PATH # --------------------------- # Make sure the entire path to DIRECTORY-PATH is available. func_mkdir_p () { $debug_cmd _G_directory_path=$1 _G_dir_list= if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then # Protect directory names starting with '-' case $_G_directory_path in -*) _G_directory_path=./$_G_directory_path ;; esac # While some portion of DIR does not yet exist... while test ! -d "$_G_directory_path"; do # ...make a list in topmost first order. Use a colon delimited # list in case some portion of path contains whitespace. _G_dir_list=$_G_directory_path:$_G_dir_list # If the last portion added has no slash in it, the list is done case $_G_directory_path in */*) ;; *) break ;; esac # ...otherwise throw away the child directory and loop _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` done _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` func_mkdir_p_IFS=$IFS; IFS=: for _G_dir in $_G_dir_list; do IFS=$func_mkdir_p_IFS # mkdir can fail with a 'File exist' error if two processes # try to create one of the directories concurrently. Don't # stop in that case! $MKDIR "$_G_dir" 2>/dev/null || : done IFS=$func_mkdir_p_IFS # Bail out if we (or some other process) failed to create a directory. test -d "$_G_directory_path" || \ func_fatal_error "Failed to create '$1'" fi } # func_mktempdir [BASENAME] # ------------------------- # Make a temporary directory that won't clash with other running # libtool processes, and avoids race conditions if possible. If # given, BASENAME is the basename for that directory. func_mktempdir () { $debug_cmd _G_template=${TMPDIR-/tmp}/${1-$progname} if test : = "$opt_dry_run"; then # Return a directory name, but don't create it in dry-run mode _G_tmpdir=$_G_template-$$ else # If mktemp works, use that first and foremost _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` if test ! -d "$_G_tmpdir"; then # Failing that, at least try and use $RANDOM to avoid a race _G_tmpdir=$_G_template-${RANDOM-0}$$ func_mktempdir_umask=`umask` umask 0077 $MKDIR "$_G_tmpdir" umask $func_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure test -d "$_G_tmpdir" || \ func_fatal_error "cannot create temporary directory '$_G_tmpdir'" fi $ECHO "$_G_tmpdir" } # func_normal_abspath PATH # ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. func_normal_abspath () { $debug_cmd # These SED scripts presuppose an absolute path with a trailing slash. _G_pathcar='s|^/\([^/]*\).*$|\1|' _G_pathcdr='s|^/[^/]*||' _G_removedotparts=':dotsl s|/\./|/|g t dotsl s|/\.$|/|' _G_collapseslashes='s|/\{1,\}|/|g' _G_finalslash='s|/*$|/|' # Start from root dir and reassemble the path. func_normal_abspath_result= func_normal_abspath_tpath=$1 func_normal_abspath_altnamespace= case $func_normal_abspath_tpath in "") # Empty path, that just means $cwd. func_stripname '' '/' "`pwd`" func_normal_abspath_result=$func_stripname_result return ;; # The next three entries are used to spot a run of precisely # two leading slashes without using negated character classes; # we take advantage of case's first-match behaviour. ///*) # Unusual form of absolute path, do nothing. ;; //*) # Not necessarily an ordinary path; POSIX reserves leading '//' # and for example Cygwin uses it to access remote file shares # over CIFS/SMB, so we conserve a leading double slash if found. func_normal_abspath_altnamespace=/ ;; /*) # Absolute path, do nothing. ;; *) # Relative path, prepend $cwd. func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac # Cancel out all the simple stuff to save iterations. We also want # the path to end with a slash for ease of parsing, so make sure # there is one (and only one) here. func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` while :; do # Processed it all yet? if test / = "$func_normal_abspath_tpath"; then # If we ascended to the root using ".." the result may be empty now. if test -z "$func_normal_abspath_result"; then func_normal_abspath_result=/ fi break fi func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcar"` func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ -e "$_G_pathcdr"` # Figure out what to do with it case $func_normal_abspath_tcomponent in "") # Trailing empty path component, ignore it. ;; ..) # Parent dir; strip last assembled component from result. func_dirname "$func_normal_abspath_result" func_normal_abspath_result=$func_dirname_result ;; *) # Actual path component, append it. func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" ;; esac done # Restore leading double-slash if one was found on entry. func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } # func_notquiet ARG... # -------------------- # Echo program name prefixed message only when not in quiet mode. func_notquiet () { $debug_cmd $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to # work around that: : } # func_relative_path SRCDIR DSTDIR # -------------------------------- # Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. func_relative_path () { $debug_cmd func_relative_path_result= func_normal_abspath "$1" func_relative_path_tlibdir=$func_normal_abspath_result func_normal_abspath "$2" func_relative_path_tbindir=$func_normal_abspath_result # Ascend the tree starting from libdir while :; do # check if we have found a prefix of bindir case $func_relative_path_tbindir in $func_relative_path_tlibdir) # found an exact match func_relative_path_tcancelled= break ;; $func_relative_path_tlibdir*) # found a matching prefix func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" func_relative_path_tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $func_relative_path_tlibdir func_relative_path_tlibdir=$func_dirname_result if test -z "$func_relative_path_tlibdir"; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result func_relative_path_tcancelled=$func_relative_path_tbindir break fi func_relative_path_result=../$func_relative_path_result ;; esac done # Now calculate path; take care to avoid doubling-up slashes. func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '/' '/' "$func_relative_path_tcancelled" if test -n "$func_stripname_result"; then func_append func_relative_path_result "/$func_stripname_result" fi # Normalisation. If bindir is libdir, return '.' else relative path. if test -n "$func_relative_path_result"; then func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi test -n "$func_relative_path_result" || func_relative_path_result=. : } # func_quote_portable EVAL ARG # ---------------------------- # Internal function to portably implement func_quote_arg. Note that we still # keep attention to performance here so we as much as possible try to avoid # calling sed binary (so far O(N) complexity as long as func_append is O(1)). func_quote_portable () { $debug_cmd $require_check_ifs_backslash func_quote_portable_result=$2 # one-time-loop (easy break) while true do if $1; then func_quote_portable_result=`$ECHO "$2" | $SED \ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` break fi # Quote for eval. case $func_quote_portable_result in *[\\\`\"\$]*) # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string # contains the shell wildcard characters. case $check_ifs_backshlash_broken$func_quote_portable_result in :*|*[\[\*\?]*) func_quote_portable_result=`$ECHO "$func_quote_portable_result" \ | $SED "$sed_quote_subst"` break ;; esac func_quote_portable_old_IFS=$IFS for _G_char in '\' '`' '"' '$' do # STATE($1) PREV($2) SEPARATOR($3) set start "" "" func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy IFS=$_G_char for _G_part in $func_quote_portable_result do case $1 in quote) func_append func_quote_portable_result "$3$2" set quote "$_G_part" "\\$_G_char" ;; start) set first "" "" func_quote_portable_result= ;; first) set quote "$_G_part" "" ;; esac done done IFS=$func_quote_portable_old_IFS ;; *) ;; esac break done func_quote_portable_unquoted_result=$func_quote_portable_result case $func_quote_portable_result in # double-quote args containing shell metacharacters to delay # word splitting, command substitution and variable expansion # for a subsequent eval. # many bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") func_quote_portable_result=\"$func_quote_portable_result\" ;; esac } # func_quotefast_eval ARG # ----------------------- # Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG', # but optimized for speed. Result is stored in $func_quotefast_eval. if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then printf -v _GL_test_printf_tilde %q '~' if test '\~' = "$_GL_test_printf_tilde"; then func_quotefast_eval () { printf -v func_quotefast_eval_result %q "$1" } else # Broken older Bash implementations. Make those faster too if possible. func_quotefast_eval () { case $1 in '~'*) func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result ;; *) printf -v func_quotefast_eval_result %q "$1" ;; esac } fi else func_quotefast_eval () { func_quote_portable false "$1" func_quotefast_eval_result=$func_quote_portable_result } fi # func_quote_arg MODEs ARG # ------------------------ # Quote one ARG to be evaled later. MODEs argument may contain zero or more # specifiers listed below separated by ',' character. This function returns two # values: # i) func_quote_arg_result # double-quoted (when needed), suitable for a subsequent eval # ii) func_quote_arg_unquoted_result # has all characters that are still active within double # quotes backslashified. Available only if 'unquoted' is specified. # # Available modes: # ---------------- # 'eval' (default) # - escape shell special characters # 'expand' # - the same as 'eval'; but do not quote variable references # 'pretty' # - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might # be used later in func_quote to get output like: 'echo "a b"' instead # of 'echo a\ b'. This is slower than default on some shells. # 'unquoted' # - produce also $func_quote_arg_unquoted_result which does not contain # wrapping double-quotes. # # Examples for 'func_quote_arg pretty,unquoted string': # # string | *_result | *_unquoted_result # ------------+-----------------------+------------------- # " | \" | \" # a b | "a b" | a b # "a b" | "\"a b\"" | \"a b\" # * | "*" | * # z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\" # # Examples for 'func_quote_arg pretty,unquoted,expand string': # # string | *_result | *_unquoted_result # --------------+---------------------+-------------------- # z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\" func_quote_arg () { _G_quote_expand=false case ,$1, in *,expand,*) _G_quote_expand=: ;; esac case ,$1, in *,pretty,*|*,expand,*|*,unquoted,*) func_quote_portable $_G_quote_expand "$2" func_quote_arg_result=$func_quote_portable_result func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result ;; *) # Faster quote-for-eval for some shells. func_quotefast_eval "$2" func_quote_arg_result=$func_quotefast_eval_result ;; esac } # func_quote MODEs ARGs... # ------------------------ # Quote all ARGs to be evaled later and join them into single command. See # func_quote_arg's description for more info. func_quote () { $debug_cmd _G_func_quote_mode=$1 ; shift func_quote_result= while test 0 -lt $#; do func_quote_arg "$_G_func_quote_mode" "$1" if test -n "$func_quote_result"; then func_append func_quote_result " $func_quote_arg_result" else func_append func_quote_result "$func_quote_arg_result" fi shift done } # func_stripname PREFIX SUFFIX NAME # --------------------------------- # strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_stripname () { $debug_cmd # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are # positional parameters, so assign one to ordinary variable first. func_stripname_result=$3 func_stripname_result=${func_stripname_result#"$1"} func_stripname_result=${func_stripname_result%"$2"} }' else func_stripname () { $debug_cmd case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; esac } fi # func_show_eval CMD [FAIL_EXP] # ----------------------------- # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} func_quote_arg pretty,expand "$_G_cmd" eval "func_notquiet $func_quote_arg_result" $opt_dry_run || { eval "$_G_cmd" _G_status=$? if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_show_eval_locale CMD [FAIL_EXP] # ------------------------------------ # Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { $debug_cmd _G_cmd=$1 _G_fail_exp=${2-':'} $opt_quiet || { func_quote_arg expand,pretty "$_G_cmd" eval "func_echo $func_quote_arg_result" } $opt_dry_run || { eval "$_G_user_locale $_G_cmd" _G_status=$? eval "$_G_safe_locale" if test 0 -ne "$_G_status"; then eval "(exit $_G_status); $_G_fail_exp" fi } } # func_tr_sh # ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { $debug_cmd case $1 in [0-9]* | *[!a-zA-Z0-9_]*) func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` ;; * ) func_tr_sh_result=$1 ;; esac } # func_verbose ARG... # ------------------- # Echo program name prefixed message in verbose mode only. func_verbose () { $debug_cmd $opt_verbose && func_echo "$*" : } # func_warn_and_continue ARG... # ----------------------------- # Echo program name prefixed warning message to standard error. func_warn_and_continue () { $debug_cmd $require_term_colors func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } # func_warning CATEGORY ARG... # ---------------------------- # Echo program name prefixed warning message to standard error. Warning # messages can be filtered according to CATEGORY, where this function # elides messages where CATEGORY is not listed in the global variable # 'opt_warning_types'. func_warning () { $debug_cmd # CATEGORY must be in the warning_categories list! case " $warning_categories " in *" $1 "*) ;; *) func_internal_error "invalid warning category '$1'" ;; esac _G_category=$1 shift case " $opt_warning_types " in *" $_G_category "*) $warning_func ${1+"$@"} ;; esac } # func_sort_ver VER1 VER2 # ----------------------- # 'sort -V' is not generally available. # Note this deviates from the version comparison in automake # in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a # but this should suffice as we won't be specifying old # version formats or redundant trailing .0 in bootstrap.conf. # If we did want full compatibility then we should probably # use m4_version_compare from autoconf. func_sort_ver () { $debug_cmd printf '%s\n%s\n' "$1" "$2" \ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n } # func_lt_ver PREV CURR # --------------------- # Return true if PREV and CURR are in the correct order according to # func_sort_ver, otherwise false. Use it like this: # # func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." func_lt_ver () { $debug_cmd test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: #! /bin/sh # A portable, pluggable option parser for Bourne shell. # Written by Gary V. Vaughan, 2010 # This is free software. There is NO warranty; not even for # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors # # This file is dual licensed under the terms of the MIT license # , and GPL version 2 or later # . You must apply one of # these licenses when using or redistributing this software or any of # the files within it. See the URLs above, or the file `LICENSE` # included in the Bootstrap distribution for the full license texts. # Please report bugs or propose patches to: # # Set a version string for this script. scriptversion=2019-02-19.15; # UTC ## ------ ## ## Usage. ## ## ------ ## # This file is a library for parsing options in your shell scripts along # with assorted other useful supporting features that you can make use # of too. # # For the simplest scripts you might need only: # # #!/bin/sh # . relative/path/to/funclib.sh # . relative/path/to/options-parser # scriptversion=1.0 # func_options ${1+"$@"} # eval set dummy "$func_options_result"; shift # ...rest of your script... # # In order for the '--version' option to work, you will need to have a # suitably formatted comment like the one at the top of this file # starting with '# Written by ' and ending with '# Copyright'. # # For '-h' and '--help' to work, you will also need a one line # description of your script's purpose in a comment directly above the # '# Written by ' line, like the one at the top of this file. # # The default options also support '--debug', which will turn on shell # execution tracing (see the comment above debug_cmd below for another # use), and '--verbose' and the func_verbose function to allow your script # to display verbose messages only when your user has specified # '--verbose'. # # After sourcing this file, you can plug in processing for additional # options by amending the variables from the 'Configuration' section # below, and following the instructions in the 'Option parsing' # section further down. ## -------------- ## ## Configuration. ## ## -------------- ## # You should override these variables in your script after sourcing this # file so that they reflect the customisations you have added to the # option parser. # The usage line for option parsing errors and the start of '-h' and # '--help' output messages. You can embed shell variables for delayed # expansion at the time the message is displayed, but you will need to # quote other shell meta-characters carefully to prevent them being # expanded when the contents are evaled. usage='$progpath [OPTION]...' # Short help message in response to '-h' and '--help'. Add to this or # override it after sourcing this library to reflect the full set of # options your script accepts. usage_message="\ --debug enable verbose shell tracing -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -v, --verbose verbosely report processing --version print version information and exit -h, --help print short or long help message and exit " # Additional text appended to 'usage_message' in response to '--help'. long_help_message=" Warning categories include: 'all' show all warnings 'none' turn off all the warnings 'error' warnings are treated as fatal errors" # Help message printed before fatal option parsing errors. fatal_help="Try '\$progname --help' for more information." ## ------------------------- ## ## Hook function management. ## ## ------------------------- ## # This section contains functions for adding, removing, and running hooks # in the main code. A hook is just a list of function names that can be # run in order later on. # func_hookable FUNC_NAME # ----------------------- # Declare that FUNC_NAME will run hooks added with # 'func_add_hook FUNC_NAME ...'. func_hookable () { $debug_cmd func_append hookable_fns " $1" } # func_add_hook FUNC_NAME HOOK_FUNC # --------------------------------- # Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must # first have been declared "hookable" by a call to 'func_hookable'. func_add_hook () { $debug_cmd case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not accept hook functions." ;; esac eval func_append ${1}_hooks '" $2"' } # func_remove_hook FUNC_NAME HOOK_FUNC # ------------------------------------ # Remove HOOK_FUNC from the list of hook functions to be called by # FUNC_NAME. func_remove_hook () { $debug_cmd eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' } # func_propagate_result FUNC_NAME_A FUNC_NAME_B # --------------------------------------------- # If the *_result variable of FUNC_NAME_A _is set_, assign its value to # *_result variable of FUNC_NAME_B. func_propagate_result () { $debug_cmd func_propagate_result_result=: if eval "test \"\${${1}_result+set}\" = set" then eval "${2}_result=\$${1}_result" else func_propagate_result_result=false fi } # func_run_hooks FUNC_NAME [ARG]... # --------------------------------- # Run all hook functions registered to FUNC_NAME. # It's assumed that the list of hook functions contains nothing more # than a whitespace-delimited list of legal shell function names, and # no effort is wasted trying to catch shell meta-characters or preserve # whitespace. func_run_hooks () { $debug_cmd _G_rc_run_hooks=false case " $hookable_fns " in *" $1 "*) ;; *) func_fatal_error "'$1' does not support hook functions." ;; esac eval _G_hook_fns=\$$1_hooks; shift for _G_hook in $_G_hook_fns; do func_unset "${_G_hook}_result" eval $_G_hook '${1+"$@"}' func_propagate_result $_G_hook func_run_hooks if $func_propagate_result_result; then eval set dummy "$func_run_hooks_result"; shift fi done } ## --------------- ## ## Option parsing. ## ## --------------- ## # In order to add your own option parsing hooks, you must accept the # full positional parameter list from your hook function. You may remove # or edit any options that you action, and then pass back the remaining # unprocessed options in '_result', escaped # suitably for 'eval'. # # The '_result' variable is automatically unset # before your hook gets called; for best performance, only set the # *_result variable when necessary (i.e. don't call the 'func_quote' # function unnecessarily because it can be an expensive operation on some # machines). # # Like this: # # my_options_prep () # { # $debug_cmd # # # Extend the existing usage message. # usage_message=$usage_message' # -s, --silent don'\''t print informational messages # ' # # No change in '$@' (ignored completely by this hook). Leave # # my_options_prep_result variable intact. # } # func_add_hook func_options_prep my_options_prep # # # my_silent_option () # { # $debug_cmd # # args_changed=false # # # Note that, for efficiency, we parse as many options as we can # # recognise in a loop before passing the remainder back to the # # caller on the first unrecognised argument we encounter. # while test $# -gt 0; do # opt=$1; shift # case $opt in # --silent|-s) opt_silent=: # args_changed=: # ;; # # Separate non-argument short options: # -s*) func_split_short_opt "$_G_opt" # set dummy "$func_split_short_opt_name" \ # "-$func_split_short_opt_arg" ${1+"$@"} # shift # args_changed=: # ;; # *) # Make sure the first unrecognised option "$_G_opt" # # is added back to "$@" in case we need it later, # # if $args_changed was set to 'true'. # set dummy "$_G_opt" ${1+"$@"}; shift; break ;; # esac # done # # # Only call 'func_quote' here if we processed at least one argument. # if $args_changed; then # func_quote eval ${1+"$@"} # my_silent_option_result=$func_quote_result # fi # } # func_add_hook func_parse_options my_silent_option # # # my_option_validation () # { # $debug_cmd # # $opt_silent && $opt_verbose && func_fatal_help "\ # '--silent' and '--verbose' options are mutually exclusive." # } # func_add_hook func_validate_options my_option_validation # # You'll also need to manually amend $usage_message to reflect the extra # options you parse. It's preferable to append if you can, so that # multiple option parsing hooks can be added safely. # func_options_finish [ARG]... # ---------------------------- # Finishing the option parse loop (call 'func_options' hooks ATM). func_options_finish () { $debug_cmd func_run_hooks func_options ${1+"$@"} func_propagate_result func_run_hooks func_options_finish } # func_options [ARG]... # --------------------- # All the functions called inside func_options are hookable. See the # individual implementations for details. func_hookable func_options func_options () { $debug_cmd _G_options_quoted=false for my_func in options_prep parse_options validate_options options_finish do func_unset func_${my_func}_result func_unset func_run_hooks_result eval func_$my_func '${1+"$@"}' func_propagate_result func_$my_func func_options if $func_propagate_result_result; then eval set dummy "$func_options_result"; shift _G_options_quoted=: fi done $_G_options_quoted || { # As we (func_options) are top-level options-parser function and # nobody quoted "$@" for us yet, we need to do it explicitly for # caller. func_quote eval ${1+"$@"} func_options_result=$func_quote_result } } # func_options_prep [ARG]... # -------------------------- # All initialisations required before starting the option parse loop. # Note that when calling hook functions, we pass through the list of # positional parameters. If a hook function modifies that list, and # needs to propagate that back to rest of this script, then the complete # modified list must be put in 'func_run_hooks_result' before returning. func_hookable func_options_prep func_options_prep () { $debug_cmd # Option defaults: opt_verbose=false opt_warning_types= func_run_hooks func_options_prep ${1+"$@"} func_propagate_result func_run_hooks func_options_prep } # func_parse_options [ARG]... # --------------------------- # The main option parsing loop. func_hookable func_parse_options func_parse_options () { $debug_cmd _G_parse_options_requote=false # this just eases exit handling while test $# -gt 0; do # Defer to hook functions for initial option parsing, so they # get priority in the event of reusing an option name. func_run_hooks func_parse_options ${1+"$@"} func_propagate_result func_run_hooks func_parse_options if $func_propagate_result_result; then eval set dummy "$func_parse_options_result"; shift # Even though we may have changed "$@", we passed the "$@" array # down into the hook and it quoted it for us (because we are in # this if-branch). No need to quote it again. _G_parse_options_requote=false fi # Break out of the loop if we already parsed every option. test $# -gt 0 || break # We expect that one of the options parsed in this function matches # and thus we remove _G_opt from "$@" and need to re-quote. _G_match_parse_options=: _G_opt=$1 shift case $_G_opt in --debug|-x) debug_cmd='set -x' func_echo "enabling shell trace mode" >&2 $debug_cmd ;; --no-warnings|--no-warning|--no-warn) set dummy --warnings none ${1+"$@"} shift ;; --warnings|--warning|-W) if test $# = 0 && func_missing_arg $_G_opt; then _G_parse_options_requote=: break fi case " $warning_categories $1" in *" $1 "*) # trailing space prevents matching last $1 above func_append_uniq opt_warning_types " $1" ;; *all) opt_warning_types=$warning_categories ;; *none) opt_warning_types=none warning_func=: ;; *error) opt_warning_types=$warning_categories warning_func=func_fatal_error ;; *) func_fatal_error \ "unsupported warning category: '$1'" ;; esac shift ;; --verbose|-v) opt_verbose=: ;; --version) func_version ;; -\?|-h) func_usage ;; --help) func_help ;; # Separate optargs to long options (plugins may need this): --*=*) func_split_equals "$_G_opt" set dummy "$func_split_equals_lhs" \ "$func_split_equals_rhs" ${1+"$@"} shift ;; # Separate optargs to short options: -W*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "$func_split_short_opt_arg" ${1+"$@"} shift ;; # Separate non-argument short options: -\?*|-h*|-v*|-x*) func_split_short_opt "$_G_opt" set dummy "$func_split_short_opt_name" \ "-$func_split_short_opt_arg" ${1+"$@"} shift ;; --) _G_parse_options_requote=: ; break ;; -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; *) set dummy "$_G_opt" ${1+"$@"}; shift _G_match_parse_options=false break ;; esac if $_G_match_parse_options; then _G_parse_options_requote=: fi done if $_G_parse_options_requote; then # save modified positional parameters for caller func_quote eval ${1+"$@"} func_parse_options_result=$func_quote_result fi } # func_validate_options [ARG]... # ------------------------------ # Perform any sanity checks on option settings and/or unconsumed # arguments. func_hookable func_validate_options func_validate_options () { $debug_cmd # Display all warnings if -W was not given. test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" func_run_hooks func_validate_options ${1+"$@"} func_propagate_result func_run_hooks func_validate_options # Bail if the options were screwed! $exit_cmd $EXIT_FAILURE } ## ----------------- ## ## Helper functions. ## ## ----------------- ## # This section contains the helper functions used by the rest of the # hookable option parser framework in ascii-betical order. # func_fatal_help ARG... # ---------------------- # Echo program name prefixed message to standard error, followed by # a help hint, and exit. func_fatal_help () { $debug_cmd eval \$ECHO \""Usage: $usage"\" eval \$ECHO \""$fatal_help"\" func_error ${1+"$@"} exit $EXIT_FAILURE } # func_help # --------- # Echo long help message to standard output and exit. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message" exit 0 } # func_missing_arg ARGNAME # ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { $debug_cmd func_error "Missing argument for '$1'." exit_cmd=exit } # func_split_equals STRING # ------------------------ # Set func_split_equals_lhs and func_split_equals_rhs shell variables # after splitting STRING at the '=' sign. test -z "$_G_HAVE_XSI_OPS" \ && (eval 'x=a/b/c; test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ && _G_HAVE_XSI_OPS=yes if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_equals () { $debug_cmd func_split_equals_lhs=${1%%=*} func_split_equals_rhs=${1#*=} if test "x$func_split_equals_lhs" = "x$1"; then func_split_equals_rhs= fi }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_equals () { $debug_cmd func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` func_split_equals_rhs= test "x$func_split_equals_lhs=" = "x$1" \ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` } fi #func_split_equals # func_split_short_opt SHORTOPT # ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. if test yes = "$_G_HAVE_XSI_OPS" then # This is an XSI compatible shell, allowing a faster implementation... eval 'func_split_short_opt () { $debug_cmd func_split_short_opt_arg=${1#??} func_split_short_opt_name=${1%"$func_split_short_opt_arg"} }' else # ...otherwise fall back to using expr, which is often a shell builtin. func_split_short_opt () { $debug_cmd func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'` func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` } fi #func_split_short_opt # func_usage # ---------- # Echo short help message to standard output and exit. func_usage () { $debug_cmd func_usage_message $ECHO "Run '$progname --help |${PAGER-more}' for full usage" exit 0 } # func_usage_message # ------------------ # Echo short help message to standard output. func_usage_message () { $debug_cmd eval \$ECHO \""Usage: $usage"\" echo $SED -n 's|^# || /^Written by/{ x;p;x } h /^Written by/q' < "$progpath" echo eval \$ECHO \""$usage_message"\" } # func_version # ------------ # Echo version message to standard output and exit. # The version message is extracted from the calling file's header # comments, with leading '# ' stripped: # 1. First display the progname and version # 2. Followed by the header comment line matching /^# Written by / # 3. Then a blank line followed by the first following line matching # /^# Copyright / # 4. Immediately followed by any lines between the previous matches, # except lines preceding the intervening completely blank line. # For example, see the header comments of this file. func_version () { $debug_cmd printf '%s\n' "$progname $scriptversion" $SED -n ' /^# Written by /!b s|^# ||; p; n :fwd2blnk /./ { n b fwd2blnk } p; n :holdwrnt s|^# || s|^# *$|| /^Copyright /!{ /./H n b holdwrnt } s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| G s|\(\n\)\n*|\1|g p; q' < "$progpath" exit $? } # Local variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC" # time-stamp-time-zone: "UTC" # End: # Set a version string. scriptversion='(GNU libtool) 2.5.4' # func_version # ------------ # Echo version message to standard output and exit. func_version () { $debug_cmd year=`date +%Y` cat < This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Originally written by Gordon Matzigkeit, 1996 (See AUTHORS for complete contributor listing) EOF exit $? } # func_echo ARG... # ---------------- # Libtool also displays the current mode in messages, so override # funclib.sh func_echo with this custom definition. func_echo () { $debug_cmd _G_message=$* func_echo_IFS=$IFS IFS=$nl for _G_line in $_G_message; do IFS=$func_echo_IFS $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" done IFS=$func_echo_IFS } ## ---------------- ## ## Options parsing. ## ## ---------------- ## # Hook in the functions to make sure our own options are parsed during # the option parsing loop. usage='$progpath [OPTION]... [MODE-ARG]...' # Short help message in response to '-h'. usage_message="Options: --config show all configuration variables --debug enable verbose shell tracing -n, --dry-run display commands without modifying any files --features display basic configuration information --finish use operation '--mode=finish' --mode=MODE use operation mode MODE --no-finish don't update shared library cache --no-quiet, --no-silent print default informational messages --no-warnings equivalent to '-Wnone' --preserve-dup-deps don't remove duplicate dependency libraries --quiet, --silent don't print informational messages --reorder-cache=DIRS reorder shared library cache for preferred DIRS --tag=TAG use configuration variables from tag TAG -v, --verbose print more informational messages than default --version print version information -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] -h, --help, --help-all print short, long, or detailed help message " # Additional text appended to 'usage_message' in response to '--help'. func_help () { $debug_cmd func_usage_message $ECHO "$long_help_message MODE must be one of the following: clean remove files from the build directory compile compile a source file into a libtool object execute automatically set library path, then run a program finish complete the installation of libtool libraries install install libraries or executables link create a library or an executable uninstall remove libraries from an installed directory MODE-ARGS vary depending on the MODE. When passed as first option, '--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. Try '$progname --help --mode=MODE' for a more detailed description of MODE. When reporting a bug, please describe a test case to reproduce it and include the following information: host-triplet: $host shell: $SHELL compiler: $LTCC compiler flags: $LTCFLAGS linker: $LD (gnu? $with_gnu_ld) version: $progname $scriptversion Debian-2.5.4-9 automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` Report bugs to . GNU libtool home page: . General help using GNU software: ." exit 0 } # func_lo2o OBJECT-NAME # --------------------- # Transform OBJECT-NAME from a '.lo' suffix to the platform specific # object suffix. lo2o=s/\\.lo\$/.$objext/ o2lo=s/\\.$objext\$/.lo/ if test yes = "$_G_HAVE_XSI_OPS"; then eval 'func_lo2o () { case $1 in *.lo) func_lo2o_result=${1%.lo}.$objext ;; * ) func_lo2o_result=$1 ;; esac }' # func_xform LIBOBJ-OR-SOURCE # --------------------------- # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) # suffix to a '.lo' libtool-object suffix. eval 'func_xform () { func_xform_result=${1%.*}.lo }' else # ...otherwise fall back to using sed. func_lo2o () { func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` } func_xform () { func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` } fi # func_fatal_configuration ARG... # ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { func_fatal_error ${1+"$@"} \ "See the $PACKAGE documentation for more information." \ "Fatal configuration error." } # func_config # ----------- # Display the configuration for all the tags in this script. func_config () { re_begincf='^# ### BEGIN LIBTOOL' re_endcf='^# ### END LIBTOOL' # Default configuration. $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" # Now print the configurations for the tags. for tagname in $taglist; do $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done exit $? } # func_features # ------------- # Display the features supported by this script. func_features () { echo "host: $host" if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" fi exit $? } # func_enable_tag TAGNAME # ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { # Global variable: tagname=$1 re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" sed_extractcf=/$re_begincf/,/$re_endcf/p # Validate tagname. case $tagname in *[!-_A-Za-z0-9,/]*) func_fatal_error "invalid tag name: $tagname" ;; esac # Don't test for the "default" C tag, as we know it's # there but not specially marked. case $tagname in CC) ;; *) if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then taglist="$taglist $tagname" # Evaluate the configuration. Be careful to quote the path # and the sed script, to avoid splitting on whitespace, but # also don't use non-portable quotes within backquotes within # quotes we have to do it in 2 steps: extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` eval "$extractedcf" else func_error "ignoring unknown tag $tagname" fi ;; esac } # func_check_version_match # ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { if test "$package_revision" != "$macro_revision"; then if test "$VERSION" != "$macro_version"; then if test -z "$macro_version"; then cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF fi else cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF fi exit $EXIT_MISMATCH fi } # libtool_options_prep [ARG]... # ----------------------------- # Preparation for options parsed by libtool. libtool_options_prep () { $debug_mode # Option defaults: opt_config=false opt_dlopen= opt_dry_run=false opt_help=false opt_mode= opt_reorder_cache=false opt_preserve_dup_deps=false opt_quiet=false opt_finishing=true opt_warning= nonopt= preserve_args= _G_rc_lt_options_prep=: _G_rc_lt_options_prep=: # Shorthand for --mode=foo, only valid as the first argument case $1 in clean|clea|cle|cl) shift; set dummy --mode clean ${1+"$@"}; shift ;; compile|compil|compi|comp|com|co|c) shift; set dummy --mode compile ${1+"$@"}; shift ;; execute|execut|execu|exec|exe|ex|e) shift; set dummy --mode execute ${1+"$@"}; shift ;; finish|finis|fini|fin|fi|f) shift; set dummy --mode finish ${1+"$@"}; shift ;; install|instal|insta|inst|ins|in|i) shift; set dummy --mode install ${1+"$@"}; shift ;; link|lin|li|l) shift; set dummy --mode link ${1+"$@"}; shift ;; uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) _G_rc_lt_options_prep=false ;; esac if $_G_rc_lt_options_prep; then # Pass back the list of options. func_quote eval ${1+"$@"} libtool_options_prep_result=$func_quote_result fi } func_add_hook func_options_prep libtool_options_prep # libtool_parse_options [ARG]... # --------------------------------- # Provide handling for libtool specific options. libtool_parse_options () { $debug_cmd _G_rc_lt_parse_options=false # Perform our own loop to consume as many options as possible in # each iteration. while test $# -gt 0; do _G_match_lt_parse_options=: _G_opt=$1 shift case $_G_opt in --dry-run|--dryrun|-n) opt_dry_run=: ;; --config) func_config ;; --dlopen|-dlopen) opt_dlopen="${opt_dlopen+$opt_dlopen }$1" shift ;; --preserve-dup-deps) opt_preserve_dup_deps=: ;; --features) func_features ;; --finish) set dummy --mode finish ${1+"$@"}; shift ;; --help) opt_help=: ;; --help-all) opt_help=': help-all' ;; --mode) test $# = 0 && func_missing_arg $_G_opt && break opt_mode=$1 case $1 in # Valid mode arguments: clean|compile|execute|finish|install|link|relink|uninstall) ;; # Catch anything else as an error *) func_error "invalid argument '$1' for $_G_opt" exit_cmd=exit ;; esac shift ;; --no-finish) opt_finishing=false func_append preserve_args " $_G_opt" ;; --no-silent|--no-quiet) opt_quiet=false func_append preserve_args " $_G_opt" ;; --no-warnings|--no-warning|--no-warn) opt_warning=false func_append preserve_args " $_G_opt" ;; --no-verbose) opt_verbose=false func_append preserve_args " $_G_opt" ;; --reorder-cache) opt_reorder_cache=true shared_lib_dirs=$1 if test -n "$shared_lib_dirs"; then case $1 in # Must begin with /: /*) ;; # Catch anything else as an error (relative paths) *) func_error "invalid argument '$1' for $_G_opt" func_error "absolute paths are required for $_G_opt" exit_cmd=exit ;; esac fi shift ;; --silent|--quiet) opt_quiet=: opt_verbose=false func_append preserve_args " $_G_opt" ;; --tag) test $# = 0 && func_missing_arg $_G_opt && break opt_tag=$1 func_append preserve_args " $_G_opt $1" func_enable_tag "$1" shift ;; --verbose|-v) opt_quiet=false opt_verbose=: func_append preserve_args " $_G_opt" ;; # An option not handled by this hook function: *) set dummy "$_G_opt" ${1+"$@"} ; shift _G_match_lt_parse_options=false break ;; esac $_G_match_lt_parse_options && _G_rc_lt_parse_options=: done if $_G_rc_lt_parse_options; then # save modified positional parameters for caller func_quote eval ${1+"$@"} libtool_parse_options_result=$func_quote_result fi } func_add_hook func_parse_options libtool_parse_options # func_warning ARG... # ------------------- # Libtool warnings are not categorized, so override funclib.sh # func_warning with this simpler definition. func_warning () { if $opt_warning; then $debug_cmd $warning_func ${1+"$@"} fi } # libtool_validate_options [ARG]... # --------------------------------- # Perform any sanity checks on option settings and/or unconsumed # arguments. libtool_validate_options () { # save first non-option argument if test 0 -lt $#; then nonopt=$1 shift fi # preserve --debug test : = "$debug_cmd" || func_append preserve_args " --debug" case $host_os in # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 cygwin* | mingw* | windows* | pw32* | cegcc* | solaris2* | os2*) # don't eliminate duplications in $postdeps and $predeps opt_duplicate_compiler_generated_deps=: ;; *) opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps ;; esac $opt_help || { # Sanity checks first: func_check_version_match test yes != "$build_libtool_libs" \ && test yes != "$build_old_libs" \ && func_fatal_configuration "not configured to build any kind of library" # Darwin sucks eval std_shrext=\"$shrext_cmds\" # Only execute mode is allowed to have -dlopen flags. if test -n "$opt_dlopen" && test execute != "$opt_mode"; then func_error "unrecognized option '-dlopen'" $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help=$help help="Try '$progname --help --mode=$opt_mode' for more information." } # Pass back the unparsed argument list func_quote eval ${1+"$@"} libtool_validate_options_result=$func_quote_result } func_add_hook func_validate_options libtool_validate_options # Process options as early as possible so that --help and --version # can return quickly. func_options ${1+"$@"} eval set dummy "$func_options_result"; shift ## ----------- ## ## Main. ## ## ----------- ## magic='%%%MAGIC variable%%%' magic_exe='%%%MAGIC EXE variable%%%' # Global variables. extracted_archives= extracted_serial=0 # If this variable is set in any of the actions, the command in it # will be execed at the end. This prevents here-documents from being # left over by shells. exec_cmd= # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } # func_generated_by_libtool # True iff stdin has been generated by Libtool. This function is only # a basic sanity check; it will hardly flush out determined imposters. func_generated_by_libtool_p () { $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 } # func_lalib_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file # True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be # fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then for lalib_p_l in 1 2 3 4 do read lalib_p_line case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi test yes = "$lalib_p" } # func_ltwrapper_script_p file # True iff FILE is a libtool wrapper script # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_script_p () { test -f "$1" && $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file # True iff FILE is a libtool wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_executable_p () { func_ltwrapper_exec_suffix= case $1 in *.exe) ;; *) func_ltwrapper_exec_suffix=.exe ;; esac $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 } # func_ltwrapper_scriptname file # Assumes file is an ltwrapper_executable # uses $file to determine the appropriate filename for a # temporary ltwrapper_script. func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file # True iff FILE is a libtool wrapper script or wrapper executable # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_ltwrapper_p () { func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" } # func_execute_cmds commands fail_cmd # Execute tilde-delimited COMMANDS. # If FAIL_CMD is given, eval that upon failure. # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { $debug_cmd save_ifs=$IFS; IFS='~' for cmd in $1; do IFS=$sp$nl eval cmd=\"$cmd\" IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs } # func_source file # Source FILE, adding directory component if necessary. # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing # 'FILE.' does not work on cygwin managed mounts. func_source () { $debug_cmd case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; esac } # func_resolve_sysroot PATH # Replace a leading = in PATH with a sysroot. Store the result into # func_resolve_sysroot_result func_resolve_sysroot () { func_resolve_sysroot_result=$1 case $func_resolve_sysroot_result in =*) func_stripname '=' '' "$func_resolve_sysroot_result" func_resolve_sysroot_result=$lt_sysroot$func_stripname_result ;; esac } # func_replace_sysroot PATH # If PATH begins with the sysroot, replace it with = and # store the result into func_replace_sysroot_result. func_replace_sysroot () { case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. func_replace_sysroot_result=$1 ;; esac } # func_infer_tag arg # Infer tagged configuration to use if any are available and # if one wasn't chosen via the "--tag" command line option. # Only attempt this if the compiler in the base compile # command doesn't match the default compiler. # arg is usually of the form 'gcc ...' func_infer_tag () { $debug_cmd if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case $@ in # Blanks in the command may have been stripped by the calling shell, # but not from the CC environment variable when configure was run. " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; # Blanks at the start of $base_compile will cause this to fail # if we don't check for them as well. *) for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. func_append_quoted CC_quoted "$arg" done CC_expanded=`func_echo_all $CC` CC_quoted_expanded=`func_echo_all $CC_quoted` case "$@ " in " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) # The compiler in the base compile command matches # the one in the tagged configuration. # Assume this is the tagged configuration we want. tagname=$z break ;; esac fi done # If $tagname still isn't set, then no tagged configuration # was found and let the user know that the "--tag" command # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi ;; esac fi } # func_write_libtool_object output_name pic_name nonpic_name # Create a libtool object file (analogous to a ".la" file), # but don't create it if we're doing a dry run. func_write_libtool_object () { write_libobj=$1 if test yes = "$build_libtool_libs"; then write_lobj=\'$2\' else write_lobj=none fi if test yes = "$build_old_libs"; then write_oldobj=\'$3\' else write_oldobj=none fi $opt_dry_run || { cat >${write_libobj}T </dev/null` if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi fi } # end: func_convert_core_file_wine_to_w32 # func_convert_core_path_wine_to_w32 ARG # Helper function used by path conversion functions when $build is *nix, and # $host is mingw, windows, cygwin, or some other w32 environment. Relies on a # correctly configured wine environment available, with the winepath program # in $build's $PATH. Assumes ARG has no leading or trailing path separator # characters. # # ARG is path to be converted from $build format to win32. # Result is available in $func_convert_core_path_wine_to_w32_result. # Unconvertible file (directory) names in ARG are skipped; if no directory names # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { $debug_cmd # unfortunately, winepath doesn't convert paths, only file names func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi fi done IFS=$oldIFS fi } # end: func_convert_core_path_wine_to_w32 # func_cygpath ARGS... # Wrapper around calling the cygpath program via LT_CYGPATH. This is used when # when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) # $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or # (2), returns the Cygwin file name or path in func_cygpath_result (input # file name or path is assumed to be in w32 format, as previously converted # from $build's *nix or MSYS format). In case (3), returns the w32 file name # or path in func_cygpath_result (input file name or path is assumed to be in # Cygwin format). Returns an empty string on error. # # ARGS are passed to cygpath, with the last one being the file name or path to # be converted. # # Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH # environment variable; do not put it in $PATH. func_cygpath () { $debug_cmd if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then # on failure, ensure result is empty func_cygpath_result= fi else func_cygpath_result= func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath # func_convert_core_msys_to_w32 ARG # Convert file name or path ARG from MSYS format to w32 format. Return # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { $debug_cmd # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 # func_convert_file_check ARG1 ARG2 # Verify that ARG1 (a file name in $build format) was converted to $host # format in ARG2. Otherwise, emit an error message, but continue (resetting # func_to_host_file_result to ARG1). func_convert_file_check () { $debug_cmd if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: func_to_host_file_result=$1 fi } # end func_convert_file_check # func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH # Verify that FROM_PATH (a path in $build format) was converted to $host # format in TO_PATH. Otherwise, emit an error message, but continue, resetting # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { $debug_cmd if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. if test "x$1" != "x$2"; then lt_replace_pathsep_chars="s|$1|$2|g" func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else func_to_host_path_result=$3 fi fi } # end func_convert_path_check # func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG # Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { $debug_cmd case $4 in $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in $2 ) func_append func_to_host_path_result "$3" ;; esac } # end func_convert_path_front_back_pathsep # func_convert_delimited_path PATH ORIG_DELIMITER NEW_DELIMITER # Replaces a delimiter for a given path. func_convert_delimited_path () { converted_path=`$ECHO "$1" | $SED "s#$2#$3#g"` } # end func_convert_delimited_path ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## # invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. # func_to_host_file ARG # Converts the file name ARG from $build format to $host format. Return result # in func_to_host_file_result. func_to_host_file () { $debug_cmd $to_host_file_cmd "$1" } # end func_to_host_file # func_to_tool_file ARG LAZY # converts the file name ARG from $build format to toolchain format. Return # result in func_to_tool_file_result. If the conversion in use is listed # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { $debug_cmd case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 ;; *) $to_tool_file_cmd "$1" func_to_tool_file_result=$func_to_host_file_result ;; esac } # end func_to_tool_file # func_convert_file_noop ARG # Copy ARG to func_to_host_file_result. func_convert_file_noop () { func_to_host_file_result=$1 } # end func_convert_file_noop # func_convert_file_msys_to_w32 ARG # Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_file_result. func_convert_file_msys_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_w32 # func_convert_file_cygwin_to_w32 ARG # Convert file name ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. func_to_host_file_result=`cygpath -m "$1"` fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_cygwin_to_w32 # func_convert_file_nix_to_w32 ARG # Convert file name ARG from *nix to w32 format. Requires a wine environment # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_w32 # func_convert_file_msys_to_cygwin ARG # Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_msys_to_cygwin # func_convert_file_nix_to_cygwin ARG # Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed # in a wine environment, working winepath, and LT_CYGPATH set. Returns result # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { $debug_cmd func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } # end func_convert_file_nix_to_cygwin ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# # invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. # # Path separators are also converted from $build format to $host format. If # ARG begins or ends with a path separator character, it is preserved (but # converted to $host format) on output. # # All path conversion functions are named using the following convention: # file name conversion function : func_convert_file_X_to_Y () # path conversion function : func_convert_path_X_to_Y () # where, for any given $build/$host combination the 'X_to_Y' value is the # same. If conversion functions are added for new $build/$host combinations, # the two new functions must follow this pattern, or func_init_to_host_path_cmd # will break. # func_init_to_host_path_cmd # Ensures that function "pointer" variable $to_host_path_cmd is set to the # appropriate value, based on the value of $to_host_file_cmd. to_host_path_cmd= func_init_to_host_path_cmd () { $debug_cmd if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" to_host_path_cmd=func_convert_path_$func_stripname_result fi } # func_to_host_path ARG # Converts the path ARG from $build format to $host format. Return result # in func_to_host_path_result. func_to_host_path () { $debug_cmd func_init_to_host_path_cmd $to_host_path_cmd "$1" } # end func_to_host_path # func_convert_path_noop ARG # Copy ARG to func_to_host_path_result. func_convert_path_noop () { func_to_host_path_result=$1 } # end func_convert_path_noop # func_convert_path_msys_to_w32 ARG # Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic # conversion to w32 is not available inside the cwrapper. Returns result in # func_to_host_path_result. func_convert_path_msys_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; # and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_msys_to_w32 # func_convert_path_cygwin_to_w32 ARG # Convert path ARG from Cygwin to w32 format. Returns result in # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_cygwin_to_w32 # func_convert_path_nix_to_w32 ARG # Convert path ARG from *nix to w32 format. Requires a wine environment and # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" fi } # end func_convert_path_nix_to_w32 # func_convert_path_msys_to_cygwin ARG # Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_msys_to_cygwin # func_convert_path_nix_to_cygwin ARG # Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a # a wine environment, working winepath, and LT_CYGPATH set. Returns result in # func_to_host_file_result. func_convert_path_nix_to_cygwin () { $debug_cmd func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them # into '.;' and ';.', and winepath ignores them completely. func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" fi } # end func_convert_path_nix_to_cygwin # func_dll_def_p FILE # True iff FILE is a Windows DLL '.def' file. # Keep in sync with _LT_DLL_DEF_P in libtool.m4 func_dll_def_p () { $debug_cmd func_dll_def_p_tmp=`$SED -n \ -e 's/^[ ]*//' \ -e '/^\(;.*\)*$/d' \ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ -e q \ "$1"` test DEF = "$func_dll_def_p_tmp" } # func_reorder_shared_lib_cache DIRS # Reorder the shared library cache by unconfiguring previous shared library cache # and configuring preferred search directories before previous search directories. # Previous shared library cache: /usr/lib /usr/local/lib # Preferred search directories: /tmp/testing # Reordered shared library cache: /tmp/testing /usr/lib /usr/local/lib func_reorder_shared_lib_cache () { $debug_cmd case $host_os in openbsd*) get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"` func_convert_delimited_path "$get_search_directories" ':' '\ ' save_search_directories=$converted_path func_convert_delimited_path "$1" ':' '\ ' # Ensure directories exist for dir in $converted_path; do # Ensure each directory is an absolute path case $dir in /*) ;; *) func_error "Directory '$dir' is not an absolute path" exit $EXIT_FAILURE ;; esac # Ensure no trailing slashes func_stripname '' '/' "$dir" dir=$func_stripname_result if test -d "$dir"; then if test -n "$preferred_search_directories"; then preferred_search_directories="$preferred_search_directories $dir" else preferred_search_directories=$dir fi else func_error "Directory '$dir' does not exist" exit $EXIT_FAILURE fi done PATH="$PATH:/sbin" ldconfig -U $save_search_directories PATH="$PATH:/sbin" ldconfig -m $preferred_search_directories $save_search_directories get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"` func_convert_delimited_path "$get_search_directories" ':' '\ ' reordered_search_directories=$converted_path $ECHO "Original: $save_search_directories" $ECHO "Reordered: $reordered_search_directories" exit $EXIT_SUCCESS ;; *) func_error "--reorder-cache is not supported for host_os=$host_os." exit $EXIT_FAILURE ;; esac } # end func_reorder_shared_lib_cache # func_mode_compile arg... func_mode_compile () { $debug_cmd # Get the compilation command and the source file. base_compile= srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal libobj= later= pie_flag= for arg do case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile lastarg=$arg arg_mode=normal ;; target ) libobj=$arg arg_mode=normal continue ;; normal ) # Accept any command-line options. case $arg in -o) test -n "$libobj" && \ func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; -pie | -fpie | -fPIE) func_append pie_flag " $arg" continue ;; -shared | -static | -prefer-pic | -prefer-non-pic) func_append later " $arg" continue ;; -no-suppress) suppress_opt=no continue ;; -Xcompiler) arg_mode=arg # the next one goes into the "base_compile" arg list continue # The current "srcfile" will either be retained or ;; # replaced later. I would guess that would be a bug. -Wc,*) func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= save_ifs=$IFS; IFS=, for arg in $args; do IFS=$save_ifs func_append_quoted lastarg "$arg" done IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result # Add the arguments to base_compile. func_append base_compile " $lastarg" continue ;; *) # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # lastarg=$srcfile srcfile=$arg ;; esac # case $arg ;; esac # case $arg_mode # Aesthetically quote the previous argument. func_append_quoted base_compile "$lastarg" done # for arg case $arg_mode in arg) func_fatal_error "you must specify an argument for -Xcompile" ;; target) func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" libobj=$func_basename_result } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo case $libobj in *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; esac case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac func_infer_tag $base_compile for arg in $later; do case $arg in -shared) test yes = "$build_libtool_libs" \ || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; -static) build_libtool_libs=no build_old_libs=yes continue ;; -prefer-pic) pic_mode=yes continue ;; -prefer-non-pic) pic_mode=no continue ;; esac done func_quote_arg pretty "$libobj" test "X$libobj" != "X$func_quote_arg_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" objname=$func_basename_result xdir=$func_dirname_result lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" fi # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in cygwin* | mingw* | windows* | pw32* | os2* | cegcc*) pic_mode=default ;; esac if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c if test no = "$compiler_c_o"; then output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext lockfile=$output_obj.lock else output_obj= need_locks=no lockfile= fi # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi func_append removelist " $output_obj" $ECHO "$srcfile" > "$lockfile" fi $opt_dry_run || $RM $removelist func_append removelist " $lockfile" trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 srcfile=$func_to_tool_file_result func_quote_arg pretty "$srcfile" qsrcfile=$func_quote_arg_result # Only build a PIC object if we are building libtool libraries. if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code command="$base_compile $qsrcfile" fi func_mkdir_p "$xdir$objdir" if test -z "$output_obj"; then # Place PIC objects in $objdir func_append command " -o $lobj" fi func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then func_show_eval '$MV "$output_obj" "$lobj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi # Allow error messages only from the first compilation. if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. if test yes = "$build_old_libs"; then if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi # Suppress compiler output if we already did a PIC compilation. func_append command "$suppress_output" func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` but it should contain: $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then func_show_eval '$MV "$output_obj" "$obj"' \ 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi fi $opt_dry_run || { func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi } exit $EXIT_SUCCESS } $opt_help || { test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () { # We need to display help for each of the modes. case $opt_mode in "") # Generic help is extracted from the usage comments # at the start of this file. func_help ;; clean) $ECHO \ "Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; compile) $ECHO \ "Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE Compile a source file into a libtool library object. This mode accepts the following additional options: -o OUTPUT-FILE set the output file name to OUTPUT-FILE -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only -shared do not build a '.o' file suitable for static linking -static only build a '.o' file suitable for static linking -Wc,FLAG -Xcompiler FLAG pass FLAG directly to the compiler COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from SOURCEFILE, then substituting the C source code suffix '.c' with the library object suffix, '.lo'." ;; execute) $ECHO \ "Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... Automatically set library path, then run a program. This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated into their corresponding uninstalled binary, and any of their required library directories are added to the library path. Then, COMMAND is executed, with ARGS as arguments." ;; finish) $ECHO \ "Usage: $progname [OPTION]... --mode=finish [LIBDIR]... Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use the '--dry-run' option if you just want to see what would be executed." ;; install) $ECHO \ "Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation The rest of the components are interpreted as arguments to that command (only BSD-compatible install options are recognized)." ;; link) $ECHO \ "Usage: $progname [OPTION]... --mode=link LINK-COMMAND... Link object files or libraries together to form another library, or to create an executable program. LINK-COMMAND is a command using the C compiler that you would use to create a program from several object files. The following components of LINK-COMMAND are treated specially: -all-static do not do any dynamic linking at all -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE try to export only the symbols listed in SYMFILE -export-symbols-regex REGEX try to export only the symbols matching REGEX -LLIBDIR search LIBDIR for required installed libraries -lNAME OUTPUT-FILE requires the installed library libNAME -module build a library that can dlopened -no-fast-install disable the fast-install mode -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects -objectlist FILE use a list of object files found in FILE to specify objects -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information -rpath LIBDIR the created library will eventually be installed in LIBDIR -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries -shared only do dynamic linking of libtool libraries -shrext SUFFIX override the standard shared library file extension -static do not do any dynamic linking of uninstalled libtool libraries -static-libtool-libs do not do any dynamic linking of libtool libraries -version-info CURRENT[:REVISION[:AGE]] specify library version info [each variable defaults to 0] -weak LIBNAME declare that the target provides the LIBNAME interface -Wc,FLAG -Xcompiler FLAG pass linker-specific FLAG directly to the compiler -Wa,FLAG -Xassembler FLAG pass linker-specific FLAG directly to the assembler -Wl,FLAG -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) All other options (arguments beginning with '-') are ignored. Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. If the OUTPUT-FILE ends in '.la', then a libtool library is created, only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created using 'ar' and 'ranlib', or on Windows using 'lib'. If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; uninstall) $ECHO \ "Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE (typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. Otherwise, only FILE itself is deleted using RM." ;; *) func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then if test : = "$opt_help"; then func_mode_help else { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do echo func_mode_help done } | $SED '1d /^When reporting/,/^Report/{ H d } $x /information about other modes/d /more detailed .*MODE/d s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' fi exit $? fi # If option '--reorder-cache', reorder the shared library cache and exit. if $opt_reorder_cache; then func_reorder_shared_lib_cache $shared_lib_dirs fi # func_mode_execute arg... func_mode_execute () { $debug_cmd # The first argument is the command name. cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ || func_fatal_help "'$file' is not a file" dir= case $file in *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= library_names= func_source "$file" # Skip this library if it cannot be dlopened. if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." dir=$func_dirname_result ;; *) func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then eval "$shlibpath_var=\"\$dir\"" else eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" fi done # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= for file do case $file in -* | *.la | *.lo ) ;; *) # Do a test to see if this is really a libtool program. if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. file=$progdir/$program fi ;; esac # Quote arguments (to preserve shell metacharacters). func_append_quoted args "$file" done if $opt_dry_run; then # Display what would be done. if test -n "$shlibpath_var"; then eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" echo "export $shlibpath_var" fi $ECHO "$cmd$args" exit $EXIT_SUCCESS else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" fi # Restore saved environment variables for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do eval "if test \"\${save_$lt_var+set}\" = set; then $lt_var=\$save_$lt_var; export $lt_var else $lt_unset $lt_var fi" done # Now prepare to actually exec the command. exec_cmd=\$cmd$args fi } test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { $debug_cmd libs= libdirs= admincmds= for opt in "$nonopt" ${1+"$@"} do if test -d "$opt"; then func_append libdirs " $opt" elif test -f "$opt"; then if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else func_warning "'$opt' is not a valid libtool archive" fi else func_fatal_error "invalid argument '$opt'" fi done if test -n "$libs"; then if test -n "$lt_sysroot"; then sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" else sysroot_cmd= fi # Remove sysroot references if $opt_dry_run; then for lib in $libs; do echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done ${RM}r "$tmpdir" fi fi if test -n "$finish_cmds$finish_eval" && test -n "$libdirs" && $opt_finishing; then for libdir in $libdirs; do if test -n "$finish_cmds"; then # Do each command in the finish commands. func_execute_cmds "$finish_cmds" 'admincmds="$admincmds '"$cmd"'"' fi if test -n "$finish_eval"; then # Do the single finish_eval. eval cmds=\"$finish_eval\" $opt_dry_run || eval "$cmds" || func_append admincmds " $cmds" fi done fi # Exit here if they wanted silent mode. $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" echo "Libraries have been installed in:" for libdir in $libdirs; do $ECHO " $libdir" done if test "false" = "$opt_finishing"; then echo echo "NOTE: finish_cmds were not executed during testing, so you must" echo "manually run ldconfig to add a given test directory, LIBDIR, to" echo "the search path for generated executables." fi echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo echo "See any operating system documentation about shared libraries for" case $host in solaris2.[6789]|solaris2.1[0-9]) echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" echo "pages." ;; *) echo "more information, such as the ld(1) and ld.so(8) manual pages." ;; esac echo "----------------------------------------------------------------------" fi exit $EXIT_SUCCESS } test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { $debug_cmd # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. case $nonopt in *shtool*) :;; *) false;; esac then # Aesthetically quote it. func_quote_arg pretty "$nonopt" install_prog="$func_quote_arg_result " arg=$1 shift else install_prog= arg=$nonopt fi # The real first argument should be the name of the installation program. # Aesthetically quote it. func_quote_arg pretty "$arg" func_append install_prog "$func_quote_arg_result" install_shared_prog=$install_prog case " $install_prog " in *[\\\ /]cp\ *) install_cp=: ;; *) install_cp=false ;; esac # We need to accept at least all the BSD install flags. dest= files= opts= prev= install_type= isdir=false stripme= no_mode=: for arg do arg2= if test -n "$dest"; then func_append files " $dest" dest=$arg continue fi case $arg in -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg fi ;; -g | -m | -o) prev=$arg ;; -s) stripme=" -s" continue ;; -*) ;; *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi prev= else dest=$arg continue fi ;; esac # Aesthetically quote the argument. func_quote_arg pretty "$arg" func_append install_prog " $func_quote_arg_result" if test -n "$arg2"; then func_quote_arg pretty "$arg2" fi func_append install_shared_prog " $func_quote_arg_result" done test -z "$install_prog" && \ func_fatal_help "you must specify an install program" test -n "$prev" && \ func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else func_quote_arg pretty "$install_override_mode" func_append install_shared_prog " -m $func_quote_arg_result" fi fi if test -z "$files"; then if test -z "$dest"; then func_fatal_help "no file or destination specified" else func_fatal_help "you must specify a destination" fi fi # Strip any trailing slash from the destination. func_stripname '' '/' "$dest" dest=$func_stripname_result # Check to see that the destination is a directory. test -d "$dest" && isdir=: if $isdir; then destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." destdir=$func_dirname_result destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) for file in $files; do case $file in *.lo) ;; *) func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done ;; esac # This variable tells wrapper scripts just to set variables rather # than running their programs. libtool_install_magic=$magic staticlibs= future_libdirs= current_libdirs= for file in $files; do # Do each installation. case $file in *.$libext) # Do the static libraries later. func_append staticlibs " $file" ;; *.la) func_resolve_sysroot "$file" file=$func_resolve_sysroot_result # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= relink_command= func_source "$file" # Add the libdir to current_libdirs if it is the destination. if test "X$destdir" = "X$libdir"; then case "$current_libdirs " in *" $libdir "*) ;; *) func_append current_libdirs " $libdir" ;; esac else # Note the libdir as a future libdir. case "$future_libdirs " in *" $libdir "*) ;; *) func_append future_libdirs " $libdir" ;; esac fi func_dirname "$file" "/" "" dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then # Strip any trailing slash from the destination. func_stripname '' '/' "$libdir" destlibdir=$func_stripname_result func_stripname '' '/' "$destdir" s_destdir=$func_stripname_result # Determine the prefix the user has applied to our future dir. inst_prefix_dir=`$ECHO "X$s_destdir" | $Xsed -e "s%$destlibdir\$%%"` # Don't allow the user to place us outside of our expected # location b/c this prevents finding dependent libraries that # are installed to the same prefix. # At present, this check doesn't affect windows .dll's that # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` else relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi func_warning "relinking '$file'" func_show_eval "$relink_command" \ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then realname=$1 shift srcname=$realname test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' tstripme=$stripme case $host_os in cygwin* | mingw* | windows* | pw32* | cegcc*) case $realname in *.dll.a) tstripme= ;; esac ;; os2*) case $realname in *_dll.a) tstripme= ;; esac ;; esac if test -n "$tstripme" && test -n "$striplib"; then func_show_eval "$striplib $destdir/$realname" 'exit $?' fi if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname do test "$linkname" != "$realname" \ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done fi # Do each command in the postinstall commands. lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" name=$func_basename_result instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. test -n "$old_library" && func_append staticlibs " $dir/$old_library" ;; *.lo) # Install (i.e. copy) a libtool object. # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. case $destfile in *.lo) func_lo2o "$destfile" staticdest=$func_lo2o_result ;; *.$objext) staticdest=$destfile destfile= ;; *) func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac # Install the libtool object if requested. test -n "$destfile" && \ func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi exit $EXIT_SUCCESS ;; *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then destfile=$destdir/$destname else func_basename "$file" destfile=$func_basename_result destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result stripped_ext=.exe fi ;; esac # Do a test to see if this is really a libtool program. case $host in *cygwin* | *mingw* | *windows*) if func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" wrapper=$func_ltwrapper_scriptname_result else func_stripname '' '.exe' "$file" wrapper=$func_stripname_result fi ;; *) wrapper=$file ;; esac if func_ltwrapper_script_p "$wrapper"; then notinst_deplibs= relink_command= func_source "$wrapper" # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ func_fatal_error "invalid libtool wrapper script '$wrapper'" finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then func_warning "'$lib' has not been installed in '$libdir'" finalize=false fi done relink_command= func_source "$wrapper" outputname= if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" file=$func_basename_result outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` $opt_quiet || { func_quote_arg expand,pretty "$relink_command" eval "func_echo $func_quote_arg_result" } if eval "$relink_command"; then : else func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi file=$outputname else func_warning "cannot relink '$file'" fi } else # Install the binary that we compiled earlier. file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` fi fi # remove .exe since cygwin /usr/bin/install will append another # one anyway case $install_prog,$host in */usr/bin/install*,*cygwin*) case $file:$destfile in *.exe:*.exe) # this is ok ;; *.exe:*) destfile=$destfile.exe ;; *:*.exe) func_stripname '' '.exe' "$destfile" destfile=$func_stripname_result ;; esac ;; esac func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' $opt_dry_run || if test -n "$outputname"; then ${RM}r "$tmpdir" fi ;; esac done for file in $staticlibs; do func_basename "$file" name=$func_basename_result # Set up the ranlib parameters. oldlib=$destdir/$name func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. func_execute_cmds "$old_postinstall_cmds" 'exit $?' done test -n "$future_libdirs" && \ func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p # Extract symbols from dlprefiles and create ${outputname}S.o with # a dlpreopen symbol table. func_generate_dlsyms () { $debug_cmd my_outputname=$1 my_originator=$2 my_pic_p=${3-false} my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi fi if test -n "$my_dlsyms"; then case $my_dlsyms in "") ;; *.c) # Discover the nlist of each of the dlfiles. nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" # Parse the name list into a source file. func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ /* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ /* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* External symbol declarations for the compiler. */\ " if test yes = "$dlself"; then func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" # Add our own program objects to the symbol list. progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done if test -n "$exclude_expsyms"; then $opt_dry_run || { eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi if test -n "$export_symbols_regex"; then $opt_dry_run || { eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' } fi # Prepare the list of exported symbols if test -z "$export_symbols"; then export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' ;; esac } else $opt_dry_run || { eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' ;; esac } fi fi for dlprefile in $dlprefiles; do func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" name=$func_basename_result case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" dlprefile_dlbasename=$sharedlib_from_linklib_result fi fi $opt_dry_run || { if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" eval '$ECHO ": $name " >> "$nlist"' fi func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 case $host in i[3456]86-*-mingw32*) eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" ;; *) eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/__nm_//' >> '$nlist'" ;; esac } else # not an import lib $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } fi ;; *) $opt_dry_run || { eval '$ECHO ": $name " >> "$nlist"' func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" } ;; esac done $opt_dry_run || { # Make sure we have at least an empty file. test -f "$nlist" || : > "$nlist" if test -n "$exclude_expsyms"; then $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T $MV "$nlist"T "$nlist" fi # Try sorting and uniquifying the output. if $GREP -v "^: " < "$nlist" | if sort -k 3 /dev/null 2>&1; then sort -k 3 else sort +2 fi | uniq > "$nlist"S; then : else $GREP -v "^: " < "$nlist" > "$nlist"S fi if test -f "$nlist"S; then eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi func_show_eval '$RM "${nlist}I"' if test -n "$global_symbol_to_import"; then eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' fi echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[];\ " if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ static void lt_syminit(void) { LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; for (; symbol->name; ++symbol) {" $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" echo >> "$output_objdir/$my_dlsyms" "\ } }" fi echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = { {\"$my_originator\", (void *) 0}," if test -s "$nlist"I; then echo >> "$output_objdir/$my_dlsyms" "\ {\"@INIT@\", (void *) <_syminit}," fi case $need_lib_prefix in no) eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; *) eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" ;; esac echo >> "$output_objdir/$my_dlsyms" "\ {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt_${my_prefix}_LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif\ " } # !$opt_dry_run pic_flag_for_symtable= case "$compile_command " in *" -static "*) ;; *) case $host in # compiling the symbol table file with pic_flag works around # a FreeBSD bug that causes programs to crash when -lm is # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; esac symtab_cflags= for arg in $LTCFLAGS; do case $arg in -pie | -fpie | -fPIE) ;; *) func_append symtab_cflags " $arg" ;; esac done # Now compile the dynamic symbol file. func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *windows* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` fi ;; *) compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` ;; esac ;; *) func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else # We keep going just in case the user didn't refer to # lt_preloaded_symbols. The linker will fail if global_symbol_pipe # really was required. # Nullify the symbol file. compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` fi } # func_cygming_gnu_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is a GNU/binutils-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_gnu_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` test -n "$func_cygming_gnu_implib_tmp" } # func_cygming_ms_implib_p ARG # This predicate returns with zero status (TRUE) if # ARG is an MS-style import library. Returns # with nonzero status (FALSE) otherwise. func_cygming_ms_implib_p () { $debug_cmd func_to_tool_file "$1" func_convert_file_msys_to_w32 func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` test -n "$func_cygming_ms_implib_tmp" } # func_win32_libid arg # return the library type of file 'arg' # # Need a lot of goo to handle *both* DLLs and import libs # Has to be a shell function in order to 'eat' the argument # that is supplied when $file_magic_command is called. # Despite the name, also deal with 64 bit binaries. func_win32_libid () { $debug_cmd win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import win32_libid_type="x86 archive import" ;; *ar\ archive*) # could be an import, or static # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64|pe-aarch64)' >/dev/null; then case $nm_interface in "MS dumpbin") if func_cygming_ms_implib_p "$1" || func_cygming_gnu_implib_p "$1" then win32_nmres=import else win32_nmres= fi ;; *) func_to_tool_file "$1" func_convert_file_msys_to_w32 win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | $SED -n -e ' 1,100{ / I /{ s|.*|import| p q } }'` ;; esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; esac fi ;; *DLL*) win32_libid_type="x86 DLL" ;; *executable*) # but shell scripts are "executable" too... case $win32_fileres in *MS\ Windows\ PE\ Intel*) win32_libid_type="x86 DLL" ;; esac ;; esac $ECHO "$win32_libid_type" } # func_cygming_dll_for_implib ARG # # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { $debug_cmd sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } # func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs # # The is the core of a fallback implementation of a # platform-specific function to extract the name of the # DLL associated with the specified import library LIBNAME. # # SECTION_NAME is either .idata$6 or .idata$7, depending # on the platform and compiler that created the implib. # # Echos the name of the DLL associated with the # specified import library. func_cygming_dll_for_implib_fallback_core () { $debug_cmd match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ # Place marker at beginning of archive member dllname section s/.*/====MARK====/ p d } # These lines can sometimes be longer than 43 characters, but # are always uninteresting /:[ ]*file format pe[i]\{,1\}-/d /^In archive [^:]*:/d # Ensure marker is printed /^====MARK====/p # Remove all lines with less than 43 characters /^.\{43\}/!d # From remaining lines, remove first 43 characters s/^.\{43\}//' | $SED -n ' # Join marker and all lines until next marker into a single line /^====MARK====/ b para H $ b para b :para x s/\n//g # Remove the marker s/^====MARK====// # Remove trailing dots and whitespace s/[\. \t]*$// # Print /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the # archive that possess that section. Heuristic: eliminate # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually # begins with a literal '.' or a single character followed by # a '.'. # # Of those that remain, print the first one. $SED -e '/^\./d;/^.\./d;q' } # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified # import library ARG. # # This fallback implementation is for use when $DLLTOOL # does not support the --identify-strict option. # Invoked by eval'ing the libtool variable # $sharedlib_from_linklib_cmd # Result is available in the variable # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { $debug_cmd if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown sharedlib_from_linklib_result= fi } # func_extract_an_archive dir oldlib func_extract_an_archive () { $debug_cmd f_ex_an_ar_dir=$1; shift f_ex_an_ar_oldlib=$1 if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then : else func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" fi } # func_extract_archives gentop oldlib ... func_extract_archives () { $debug_cmd my_gentop=$1; shift my_oldlibs=${1+"$@"} my_oldobjs= my_xlib= my_xabs= my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in *" $my_xlib_u "*) func_arith $extracted_serial + 1 extracted_serial=$func_arith_result my_xlib_u=lt$extracted_serial-$my_xlib ;; *) break ;; esac done extracted_archives="$extracted_archives $my_xlib_u" my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" case $host in *-darwin*) func_verbose "Extracting $my_xabs" # Do not bother doing anything if just a dry run $opt_dry_run || { darwin_orig_dir=`pwd` cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` func_basename "$darwin_archive" darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" for darwin_arch in $darwin_arches; do func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" cd "unfat-$$/$darwin_base_archive-$darwin_arch" func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` $LIPO -create -output "$darwin_file" $darwin_files done # $darwin_filelist $RM -rf unfat-$$ cd "$darwin_orig_dir" else cd $darwin_orig_dir func_extract_an_archive "$my_xdir" "$my_xabs" fi # $darwin_arches } # !$opt_dry_run ;; *) func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done func_extract_archives_result=$my_oldobjs } # func_emit_wrapper [arg=no] # # Emit a libtool wrapper script on stdout. # Don't directly open a file because we may want to # incorporate the script contents within a cygwin/mingw/windows # wrapper executable. Must ONLY be called from within # func_mode_link because it depends on a number of variables # set therein. # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script # will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw/windows-specific # behavior. func_emit_wrapper () { func_emit_wrapper_arg1=${1-no} $ECHO "\ #! $SHELL # $output - temporary wrapper script for $objdir/$outputname # Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. # # This wrapper script should never be moved out of the build directory. # If it is, it will not operate correctly. # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. sed_quote_subst='$sed_quote_subst' # Be Bourne compatible if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac fi BIN_SH=xpg4; export BIN_SH # for Tru64 DUALCASE=1; export DUALCASE # for MKS sh # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH relink_command=\"$relink_command\" # This environment variable determines our operation mode. if test \"\$libtool_install_magic\" = \"$magic\"; then # install mode needs the following variables: generated_by_libtool_version='$macro_version' notinst_deplibs='$notinst_deplibs' else # When we are sourced in execute mode, \$file and \$ECHO are already set. if test \"\$libtool_execute_magic\" != \"$magic\"; then file=\"\$0\"" func_quote_arg pretty "$ECHO" qECHO=$func_quote_arg_result $ECHO "\ # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } ECHO=$qECHO fi # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper # /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" # (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and # --lt-dump-script. There is, deliberately, no --lt-help. # # The first argument to this parsing function should be the # script's $0 value, followed by "$@". lt_option_debug= func_parse_lt_options () { lt_script_arg0=\$0 shift for lt_opt do case \"\$lt_opt\" in --lt-debug) lt_option_debug=1 ;; --lt-dump-script) lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` cat \"\$lt_dump_D/\$lt_dump_F\" exit 0 ;; --lt-*) \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 exit 1 ;; esac done # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } # Used when --lt-debug. Prints its arguments to stdout # (redirection is the responsibility of the caller) func_lt_dump_args () { lt_dump_args_N=1; for lt_arg do \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } # Core function for launching the target application func_exec_program_core () { " case $host in # Backslashes separate directories on plain windows *-*-mingw* | *-*-windows* | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} " ;; *) $ECHO "\ if test -n \"\$lt_option_debug\"; then \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} " ;; esac $ECHO "\ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 exit 1 } # A function to encapsulate launching the target application # Strips options in the --lt-* namespace from \$@ and # launches target application with the remaining arguments. func_exec_program () { case \" \$* \" in *\\ --lt-*) for lt_wr_arg do case \$lt_wr_arg in --lt-*) ;; *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; esac shift done ;; esac func_exec_program_core \${1+\"\$@\"} } # Parse options func_parse_lt_options \"\$0\" \${1+\"\$@\"} # Find the directory that this script lives in. thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` test \"x\$thisdir\" = \"x\$file\" && thisdir=. # Follow symbolic links until we get to the real thisdir. file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` while test -n \"\$file\"; do destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` # If there was a directory component, then change thisdir. if test \"x\$destdir\" != \"x\$file\"; then case \"\$destdir\" in [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; *) thisdir=\"\$thisdir/\$destdir\" ;; esac fi file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` done # Usually 'no', except on cygwin/mingw/windows when embedded into # the cwrapper. WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then # special case for '.' if test \"\$thisdir\" = \".\"; then thisdir=\`pwd\` fi # remove .libs from thisdir case \"\$thisdir\" in *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; $objdir ) thisdir=. ;; esac fi # Try to get the absolute directory name. absdir=\`cd \"\$thisdir\" && pwd\` test -n \"\$absdir\" && thisdir=\"\$absdir\" " if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" if test ! -d \"\$progdir\"; then $MKDIR \"\$progdir\" else $RM \"\$progdir/\$file\" fi" $ECHO "\ # relink executable if necessary if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi fi $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || { $RM \"\$progdir/\$program\"; $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } $RM \"\$progdir/\$file\" fi" else $ECHO "\ program='$outputname' progdir=\"\$thisdir/$objdir\" " fi $ECHO "\ if test -f \"\$progdir/\$program\"; then" # fixup the dll searchpath if we need to. # # Fix the DLL searchpath if we need to. Do this before prepending # to shlibpath, because on Windows, both are PATH and uninstalled # libraries must come first. if test -n "$dllsearchpath"; then $ECHO "\ # Add the dll search path components to the executable PATH PATH=$dllsearchpath:\$PATH " fi # Export our shlibpath_var if we have one. if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" # Some systems cannot cope with colon-terminated $shlibpath_var # The second colon is a workaround for a bug in BeOS R4 sed $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` export $shlibpath_var " fi $ECHO "\ if test \"\$libtool_execute_magic\" != \"$magic\"; then # Run the actual program with our arguments. func_exec_program \${1+\"\$@\"} fi else # The program doesn't exist. \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 fi fi\ " } # func_emit_cwrapperexe_src # emit the source code for a wrapper executable on stdout # Must ONLY be called from within func_mode_link because # it depends on a number of variable set therein. func_emit_cwrapperexe_src () { cat < #include #if defined _WIN32 && !defined __GNUC__ # include # include # include #else # include # include # ifdef __CYGWIN__ # include # endif #endif #include #include #include #include #include #include #include #include #define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) /* declarations of non-ANSI functions */ #if defined __MINGW32__ # ifdef __STRICT_ANSI__ _CRTIMP int __cdecl _putenv (const char *); # endif #elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif /* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ #if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC #elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv #elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" /* #elif defined other platforms ... */ #endif #if defined PATH_MAX # define LT_PATHMAX PATH_MAX #elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 #endif #ifndef S_IXOTH # define S_IXOTH 0 #endif #ifndef S_IXGRP # define S_IXGRP 0 #endif /* path handling portability macros */ #ifndef DIR_SEPARATOR # define DIR_SEPARATOR '/' # define PATH_SEPARATOR ':' #endif #if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 # define DIR_SEPARATOR_2 '\\' # endif # ifndef PATH_SEPARATOR_2 # define PATH_SEPARATOR_2 ';' # endif #endif #ifndef DIR_SEPARATOR_2 # define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) #else /* DIR_SEPARATOR_2 */ # define IS_DIR_SEPARATOR(ch) \ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) #endif /* DIR_SEPARATOR_2 */ #ifndef PATH_SEPARATOR_2 # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) #else /* PATH_SEPARATOR_2 */ # define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) #endif /* PATH_SEPARATOR_2 */ #ifndef FOPEN_WB # define FOPEN_WB "w" #endif #ifndef _O_BINARY # define _O_BINARY 0 #endif #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ if (stale) { free (stale); stale = 0; } \ } while (0) #if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; #endif const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ void *xmalloc (size_t num); char *xstrdup (const char *string); const char *base_name (const char *name); char *find_executable (const char *wrapper); char *chase_symlinks (const char *pathspec); int make_executable (const char *path); int check_executable (const char *path); char *strendzap (char *str, const char *pat); void lt_debugprintf (const char *file, int line, const char *fmt, ...); void lt_fatal (const char *file, int line, const char *message, ...); static const char *nonnull (const char *s); static const char *nonempty (const char *s); void lt_setenv (const char *name, const char *value); char *lt_extend_str (const char *orig_value, const char *add, int to_end); void lt_update_exe_path (const char *name, const char *value); void lt_update_lib_path (const char *name, const char *value); char **prepare_spawn (char **argv); void lt_dump_script (FILE *f); EOF cat <= 0) && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) return 1; else return 0; } int make_executable (const char *path) { int rval = 0; struct stat st; lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", nonempty (path)); if ((!path) || (!*path)) return 0; if (stat (path, &st) >= 0) { rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); } return rval; } /* Searches for the full path of the wrapper. Returns newly allocated full path name if found, NULL otherwise Does not chase symlinks, even on platforms that support them. */ char * find_executable (const char *wrapper) { int has_slash = 0; const char *p; const char *p_next; /* static buffer for getcwd */ char tmp[LT_PATHMAX + 1]; size_t tmp_len; char *concat_name; lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", nonempty (wrapper)); if ((wrapper == NULL) || (*wrapper == '\0')) return NULL; /* Absolute path? */ #if defined HAVE_DOS_BASED_FILE_SYSTEM if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } else { #endif if (IS_DIR_SEPARATOR (wrapper[0])) { concat_name = xstrdup (wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } #if defined HAVE_DOS_BASED_FILE_SYSTEM } #endif for (p = wrapper; *p; p++) if (*p == '/') { has_slash = 1; break; } if (!has_slash) { /* no slashes; search PATH */ const char *path = getenv ("PATH"); if (path != NULL) { for (p = path; *p; p = p_next) { const char *q; size_t p_len; for (q = p; *q; q++) if (IS_PATH_SEPARATOR (*q)) break; p_len = (size_t) (q - p); p_next = (*q == '\0' ? q : q + 1); if (p_len == 0) { /* empty path: current directory */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); } else { concat_name = XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, p, p_len); concat_name[p_len] = '/'; strcpy (concat_name + p_len + 1, wrapper); } if (check_executable (concat_name)) return concat_name; XFREE (concat_name); } } /* not found in PATH; assume curdir */ } /* Relative path | not found in path: prepend cwd */ if (getcwd (tmp, LT_PATHMAX) == NULL) lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", nonnull (strerror (errno))); tmp_len = strlen (tmp); concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); memcpy (concat_name, tmp, tmp_len); concat_name[tmp_len] = '/'; strcpy (concat_name + tmp_len + 1, wrapper); if (check_executable (concat_name)) return concat_name; XFREE (concat_name); return NULL; } char * chase_symlinks (const char *pathspec) { #ifndef S_ISLNK return xstrdup (pathspec); #else char buf[LT_PATHMAX]; struct stat s; char *tmp_pathspec = xstrdup (pathspec); char *p; int has_symlinks = 0; while (strlen (tmp_pathspec) && !has_symlinks) { lt_debugprintf (__FILE__, __LINE__, "checking path component for symlinks: %s\n", tmp_pathspec); if (lstat (tmp_pathspec, &s) == 0) { if (S_ISLNK (s.st_mode) != 0) { has_symlinks = 1; break; } /* search backwards for last DIR_SEPARATOR */ p = tmp_pathspec + strlen (tmp_pathspec) - 1; while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) p--; if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) { /* no more DIR_SEPARATORS left */ break; } *p = '\0'; } else { lt_fatal (__FILE__, __LINE__, "error accessing file \"%s\": %s", tmp_pathspec, nonnull (strerror (errno))); } } XFREE (tmp_pathspec); if (!has_symlinks) { return xstrdup (pathspec); } tmp_pathspec = realpath (pathspec, buf); if (tmp_pathspec == 0) { lt_fatal (__FILE__, __LINE__, "could not follow symlinks for %s", pathspec); } return xstrdup (tmp_pathspec); #endif } char * strendzap (char *str, const char *pat) { size_t len, patlen; assert (str != NULL); assert (pat != NULL); len = strlen (str); patlen = strlen (pat); if (patlen <= len) { str += len - patlen; if (STREQ (str, pat)) *str = '\0'; } return str; } void lt_debugprintf (const char *file, int line, const char *fmt, ...) { va_list args; if (lt_debug) { (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); va_start (args, fmt); (void) vfprintf (stderr, fmt, args); va_end (args); } } static void lt_error_core (int exit_status, const char *file, int line, const char *mode, const char *message, va_list ap) { fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); vfprintf (stderr, message, ap); fprintf (stderr, ".\n"); if (exit_status >= 0) exit (exit_status); } void lt_fatal (const char *file, int line, const char *message, ...) { va_list ap; va_start (ap, message); lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); va_end (ap); } static const char * nonnull (const char *s) { return s ? s : "(null)"; } static const char * nonempty (const char *s) { return (s && !*s) ? "(empty)" : nonnull (s); } void lt_setenv (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_setenv) setting '%s' to '%s'\n", nonnull (name), nonnull (value)); { #ifdef HAVE_SETENV /* always make a copy, for consistency with !HAVE_SETENV */ char *str = xstrdup (value); setenv (name, str, 1); #else size_t len = strlen (name) + 1 + strlen (value) + 1; char *str = XMALLOC (char, len); sprintf (str, "%s=%s", name, value); if (putenv (str) != EXIT_SUCCESS) { XFREE (str); } #endif } } char * lt_extend_str (const char *orig_value, const char *add, int to_end) { char *new_value; if (orig_value && *orig_value) { size_t orig_value_len = strlen (orig_value); size_t add_len = strlen (add); new_value = XMALLOC (char, add_len + orig_value_len + 1); if (to_end) { strcpy (new_value, orig_value); strcpy (new_value + orig_value_len, add); } else { strcpy (new_value, add); strcpy (new_value + add_len, orig_value); } } else { new_value = xstrdup (add); } return new_value; } void lt_update_exe_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); /* some systems can't cope with a ':'-terminated path #' */ size_t len = strlen (new_value); while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); } } void lt_update_lib_path (const char *name, const char *value) { lt_debugprintf (__FILE__, __LINE__, "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", nonnull (name), nonnull (value)); if (name && *name && value && *value) { char *new_value = lt_extend_str (getenv (name), value, 0); lt_setenv (name, new_value); XFREE (new_value); } } EOF case $host_os in mingw* | windows*) cat <<"EOF" /* Prepares an argument vector before calling spawn(). Note that spawn() does not by itself call the command interpreter (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&v); v.dwPlatformId == VER_PLATFORM_WIN32_NT; }) ? "cmd.exe" : "command.com"). Instead it simply concatenates the arguments, separated by ' ', and calls CreateProcess(). We must quote the arguments since Win32 CreateProcess() interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a special way: - Space and tab are interpreted as delimiters. They are not treated as delimiters if they are surrounded by double quotes: "...". - Unescaped double quotes are removed from the input. Their only effect is that within double quotes, space and tab are treated like normal characters. - Backslashes not followed by double quotes are not special. - But 2*n+1 backslashes followed by a double quote become n backslashes followed by a double quote (n >= 0): \" -> " \\\" -> \" \\\\\" -> \\" */ #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" char ** prepare_spawn (char **argv) { size_t argc; char **new_argv; size_t i; /* Count number of arguments. */ for (argc = 0; argv[argc] != NULL; argc++) ; /* Allocate new argument vector. */ new_argv = XMALLOC (char *, argc + 1); /* Put quoted arguments into the new argument vector. */ for (i = 0; i < argc; i++) { const char *string = argv[i]; if (string[0] == '\0') new_argv[i] = xstrdup ("\"\""); else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) { int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); size_t length; unsigned int backslashes; const char *s; char *quoted_string; char *p; length = 0; backslashes = 0; if (quote_around) length++; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') length += backslashes + 1; length++; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) length += backslashes + 1; quoted_string = XMALLOC (char, length + 1); p = quoted_string; backslashes = 0; if (quote_around) *p++ = '"'; for (s = string; *s != '\0'; s++) { char c = *s; if (c == '"') { unsigned int j; for (j = backslashes + 1; j > 0; j--) *p++ = '\\'; } *p++ = c; if (c == '\\') backslashes++; else backslashes = 0; } if (quote_around) { unsigned int j; for (j = backslashes; j > 0; j--) *p++ = '\\'; *p++ = '"'; } *p = '\0'; new_argv[i] = quoted_string; } else new_argv[i] = (char *) string; } new_argv[argc] = NULL; return new_argv; } EOF ;; esac cat <<"EOF" void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | $SED -n -e ' s/^\(.\{79\}\)\(..*\)/\1\ \2/ h s/\([\\"]\)/\\\1/g s/$/\\n/ s/\([^\n]*\).*/ fputs ("\1", f);/p g D' cat <<"EOF" } EOF } # end: func_emit_cwrapperexe_src # func_win32_import_lib_p ARG # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { $debug_cmd case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } # func_suncc_cstd_abi # !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! # Several compiler flags select an ABI that is incompatible with the # Cstd library. Avoid specifying it if any are in CXXFLAGS. func_suncc_cstd_abi () { $debug_cmd case " $compile_command " in *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) suncc_use_cstd_abi=no ;; *) suncc_use_cstd_abi=yes ;; esac } # func_mode_link arg... func_mode_link () { $debug_cmd case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. allow_undefined=yes ;; *) allow_undefined=yes ;; esac libtool_args=$nonopt base_compile="$nonopt $@" compile_command=$nonopt finalize_command=$nonopt compile_rpath= compile_rpath_tail= finalize_rpath= compile_shlibpath= finalize_shlibpath= convenience= old_convenience= deplibs= old_deplibs= compiler_flags= linker_flags= dllsearchpath= lib_search_path=`pwd` inst_prefix_dir= new_inherited_linker_flags= avoid_version=no bindir= dlfiles= dlprefiles= dlself=no export_dynamic=no export_symbols= export_symbols_regex= generated= libobjs= ltlibs= module=no no_install=no objs= os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no preload=false prev= prevarg= release= rpath= xrpath= perm_rpath= temp_rpath= temp_rpath_tail= thread_safe=no vinfo= vinfo_number=no weak_libs= rpath_arg= single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. for arg do case $arg in -shared) test yes != "$build_libtool_libs" \ && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; -static) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=built ;; -static-libtool-libs) if test -z "$pic_flag" && test -n "$link_static_flag"; then dlopen_self=$dlopen_self_static fi prefer_static_libs=yes ;; esac build_libtool_libs=no build_old_libs=yes break ;; esac done # See if our shared archives depend on static archives. test -n "$old_archive_from_new_cmds" && build_old_libs=yes # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do arg=$1 shift func_quote_arg pretty,unquoted "$arg" qarg=$func_quote_arg_unquoted_result func_append libtool_args " $func_quote_arg_result" # If the previous option needs an argument, assign it. if test -n "$prev"; then case $prev in output) func_append compile_command " @OUTPUT@" func_append finalize_command " @OUTPUT@" ;; esac case $prev in bindir) bindir=$arg prev= continue ;; dlfiles|dlprefiles) $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" preload=: } case $arg in *.la | *.lo) ;; # We handle these cases below. force) if test no = "$dlself"; then dlself=needless export_dynamic=yes fi prev= continue ;; self) if test dlprefiles = "$prev"; then dlself=yes elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless export_dynamic=yes fi prev= continue ;; *) if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" fi prev= continue ;; esac ;; expsyms) export_symbols=$arg test -f "$arg" \ || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) export_symbols_regex=$arg prev= continue ;; framework) case $host in *-*-darwin*) case "$deplibs " in *" $qarg.ltframework "*) ;; *) func_append deplibs " $qarg.ltframework" # this is fixed later ;; esac ;; esac prev= continue ;; inst_prefix) inst_prefix_dir=$arg prev= continue ;; mllvm) # Clang does not use LLVM to link, so we can simply discard any # '-mllvm $arg' options when doing the link step. prev= continue ;; objectlist) if test -f "$arg"; then save_arg=$arg moreargs= for fil in `cat "$save_arg"` do # func_append moreargs " $fil" arg=$fil # A libtool-controlled object. # Check to see that this really is a libtool object. if func_lalib_unsafe_p "$arg"; then pic_object= non_pic_object= # Read the .lo file func_source "$arg" if test -z "$pic_object" || test -z "$non_pic_object" || test none = "$pic_object" && test none = "$non_pic_object"; then func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. pic_object=$xdir$pic_object if test dlfiles = "$prev"; then if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue else # If libtool objects are unsupported, then we need to preload. prev=dlprefiles fi fi # CHECK ME: I think I busted this. -Ossama if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= fi # A PIC object. func_append libobjs " $pic_object" arg=$pic_object fi # Non-PIC object. if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" if test -z "$pic_object" || test none = "$pic_object"; then arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else # Only an error if not doing a dry-run. if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result non_pic_object=$xdir$func_lo2o_result func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else func_fatal_error "'$arg' is not a valid libtool object" fi fi done else func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; os2dllname) os2dllname=$arg prev= continue ;; precious_regex) precious_files_regex=$arg prev= continue ;; release) release=-$arg prev= continue ;; rpath | xrpath) # We need an absolute path. case $arg in [\\/]* | [A-Za-z]:[\\/]*) ;; *) func_fatal_error "argument to -rpath is not absolute: $arg" ;; esac if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; esac else case "$xrpath " in *" $arg "*) ;; *) func_append xrpath " $arg" ;; esac fi prev= continue ;; shrext) shrext_cmds=$arg prev= continue ;; weak) func_append weak_libs " $arg" prev= continue ;; xassembler) func_append compiler_flags " -Xassembler $qarg" prev= func_append compile_command " -Xassembler $qarg" func_append finalize_command " -Xassembler $qarg" continue ;; xcclinker) func_append linker_flags " $qarg" func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xcompiler) func_append compiler_flags " $qarg" prev= func_append compile_command " $qarg" func_append finalize_command " $qarg" continue ;; xlinker) func_append linker_flags " $qarg" func_append compiler_flags " $wl$qarg" prev= func_append compile_command " $wl$qarg" func_append finalize_command " $wl$qarg" continue ;; *) eval "$prev=\"\$arg\"" prev= continue ;; esac fi # test -n "$prev" prevarg=$arg case $arg in -all-static) if test -n "$link_static_flag"; then # See comment for -static flag below, for more details. func_append compile_command " $link_static_flag" func_append finalize_command " $link_static_flag" fi continue ;; -allow-undefined) # FIXME: remove this flag sometime in the future. func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) avoid_version=yes continue ;; -bindir) prev=bindir continue ;; -dlopen) prev=dlfiles continue ;; -dlpreopen) prev=dlprefiles continue ;; -export-dynamic) export_dynamic=yes continue ;; -export-symbols | -export-symbols-regex) if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex fi continue ;; -framework) prev=framework continue ;; -inst-prefix-dir) prev=inst_prefix continue ;; # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* # so, if we see these flags be careful not to treat them like -L -L[A-Z][A-Z]*:*) case $with_gcc/$host in no/*-*-irix* | /*-*-irix*) func_append compile_command " $arg" func_append finalize_command " $arg" ;; esac continue ;; -L*) func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then func_fatal_error "require no space between '-L' and '$1'" else func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result # We need an absolute path. case $dir in [\\/]* | [A-Za-z]:[\\/]*) ;; *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ func_fatal_error "cannot determine absolute directory name of '$dir'" dir=$absdir ;; esac case "$deplibs " in *" -L$dir "* | *" $arg "*) # Will only happen for absolute or sysroot arguments ;; *) # Preserve sysroot, but never include relative directories case $dir in [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; *) func_append deplibs " -L$dir" ;; esac func_append lib_search_path " $dir" ;; esac case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*) testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` case :$dllsearchpath: in *":$dir:"*) ;; ::) dllsearchpath=$dir;; *) func_append dllsearchpath ":$dir";; esac case :$dllsearchpath: in *":$testbindir:"*) ;; ::) dllsearchpath=$testbindir;; *) func_append dllsearchpath ":$testbindir";; esac ;; esac continue ;; -l*) if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) continue ;; *-*-os2*) # These systems don't actually have a C library (as such) test X-lc = "X$arg" && continue ;; *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc due to us having libc/libc_r. test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework func_append deplibs " System.ltframework" continue ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work test X-lc = "X$arg" && continue ;; esac elif test X-lc_r = "X$arg"; then case $host in *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*) # Do not include libc_r directly, use -pthread flag. continue ;; esac fi func_append deplibs " $arg" continue ;; -mllvm) prev=mllvm continue ;; -module) module=yes continue ;; # Tru64 UNIX uses -model [arg] to determine the layout of C++ # classes, name mangling, and exception handling. # Darwin uses the -arch flag to determine output architecture. # -q