ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/external/subpack/net/nginx-util/Makefile b/external/subpack/net/nginx-util/Makefile
new file mode 100644
index 0000000..b1d9c5e
--- /dev/null
+++ b/external/subpack/net/nginx-util/Makefile
@@ -0,0 +1,135 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nginx-util
+PKG_VERSION:=1.6
+PKG_RELEASE:=1
+PKG_MAINTAINER:=Peter Stadler <peter.stadler@student.uibk.ac.at>
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+CMAKE_OPTIONS+= -DUBUS=y
+CMAKE_OPTIONS+= -DVERSION=$(PKG_VERSION)
+
+
+define Package/nginx-ssl-util/default
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Web Servers/Proxies
+ TITLE:=Nginx configurator including SSL
+ DEPENDS:=+libstdcpp +libuci +libubus +libubox +libpthread +libopenssl
+ # TODO: remove after a transition period (together with below and pkg nginx):
+ # It actually removes nginx-util (replacing it by a dummy pkg) to avoid
+ # conflicts with nginx-ssl-util*
+ DEPENDS+= +nginx-util
+ EXTRA_DEPENDS:=nginx-util (>=1.4-2)
+endef
+
+
+define Package/nginx-ssl-util
+ $(Package/nginx-ssl-util/default)
+ TITLE+= (using PCRE)
+ DEPENDS+= +libpcre
+ CONFLICTS:=nginx-ssl-util-nopcre,
+endef
+
+
+define Package/nginx-ssl-util-nopcre
+ $(Package/nginx-ssl-util/default)
+ TITLE+= (using <regex>)
+ CONFLICTS:=nginx-ssl-util
+endef
+
+
+define Package/nginx-ssl-util/default/description
+ Utility that builds dynamically LAN listen directives for Nginx.
+ Furthermore, it manages SSL directives for its server parts and can create
+ corresponding (self-signed) certificates.
+endef
+
+
+Package/nginx-ssl-util/description = \
+ $(Package/nginx-ssl-util/default/description) \
+ It uses the PCRE library for performance.
+
+
+Package/nginx-ssl-util-nopcre/description = \
+ $(Package/nginx-ssl-util/default/description) \
+ It uses the standard regex library of C++.
+
+
+define Package/nginx-ssl-util/install/default
+ $(INSTALL_DIR) $(1)/etc/nginx/conf.d/
+
+ $(INSTALL_CONF) ./files/uci.conf.template $(1)/etc/nginx/
+ $(LN) /var/lib/nginx/uci.conf $(1)/etc/nginx/uci.conf
+
+ $(INSTALL_CONF) ./files/restrict_locally $(1)/etc/nginx/
+
+ $(INSTALL_DIR) $(1)/etc/config/
+ $(INSTALL_CONF) ./files/nginx.config $(1)/etc/config/nginx
+
+ifneq ($(CONFIG_IPV6),y) # the used IPv6 directives have `::` in them:
+ $(SED) "/::/d" $(1)/etc/nginx/restrict_locally
+ $(SED) "/::/d" $(1)/etc/config/nginx
+endif
+endef
+
+
+define Package/nginx-ssl-util/install
+ $(call Package/nginx-ssl-util/install/default, $(1))
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util $(1)/usr/bin/nginx-util
+endef
+
+
+define Package/nginx-ssl-util-nopcre/install
+ $(call Package/nginx-ssl-util/install/default, $(1))
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/nginx-ssl-util-nopcre \
+ $(1)/usr/bin/nginx-util
+endef
+
+
+define Package/nginx-ssl-util/prerm
+#!/bin/sh
+
+[ -n "$${IPKG_INSTROOT}" ] && exit 0
+[ "$${PKG_UPGRADE}" = "1" ] && exit 0
+case "$$(/sbin/uci get "nginx.global.uci_enable" 2>/dev/null)" in
+ 1|on|true|yes|enabled) ;;
+ *) exit 0;;
+esac
+
+eval "$$(/usr/bin/nginx-util get_env)" &&
+[ "$$(/sbin/uci get "nginx.$${LAN_NAME}.$${MANAGE_SSL}" 2>/dev/null)" = \
+ "self-signed" ] &&
+cd "/etc/nginx" &&
+rm -f "$$(/sbin/uci get "nginx.$${LAN_NAME}.ssl_certificate")" \
+ "$$(/sbin/uci get "nginx.$${LAN_NAME}.ssl_certificate_key")"
+
+exit 0
+endef
+
+
+Package/nginx-ssl-util-nopcre/prerm = $(Package/nginx-ssl-util/prerm)
+
+
+$(eval $(call BuildPackage,nginx-ssl-util))
+$(eval $(call BuildPackage,nginx-ssl-util-nopcre))
+
+
+# TODO: remove after a transition period (together with above and pkg nginx):
+# It replaces nginx-util by a dummy pkg for a smooth upgrade of nginx*
+
+define Package/nginx-util
+ TITLE:=Dummy package for removing nginx-util when upgrading.
+ DEPENDS:=+libstdcpp +libubus +libubox +libpthread
+ PKGARCH:=all
+endef
+
+define Package/nginx-util/install
+ $(INSTALL_DIR) $(1)/usr/bin
+endef
+
+$(eval $(call BuildPackage,nginx-util))
diff --git a/external/subpack/net/nginx-util/files/README.sh b/external/subpack/net/nginx-util/files/README.sh
new file mode 100755
index 0000000..7ca1729
--- /dev/null
+++ b/external/subpack/net/nginx-util/files/README.sh
@@ -0,0 +1,475 @@
+#!/bin/sh
+# This is a template copy it by: ./README.sh | xclip -selection c
+# to https://openwrt.org/docs/guide-user/services/webserver/nginx#configuration
+
+
+NGINX_UTIL="/usr/bin/nginx-util"
+
+EXAMPLE_COM="example.com"
+
+MSG="
+/* Created by the following bash script that includes the source of some files:
+ * https://github.com/openwrt/packages/net/nginx-util/files/README.sh
+ */"
+
+eval $("${NGINX_UTIL}" get_env)
+
+code() {
+ local file
+ [ $# -gt 1 ] && file="$2" || file="$(basename "$1")"
+ printf "<file nginx %s>\n%s</file>" "$1" "$(cat "${file}")";
+}
+
+ifConfEcho() {
+ sed -nE "s/^\s*$1=\s*(\S*)\s*\\\\$/\n$2 \"\1\";/p" ../../nginx/Makefile;
+}
+
+cat <<EOF
+
+
+
+
+
+===== Configuration =====${MSG}
+
+
+
+The official Documentation contains a
+[[https://docs.nginx.com/nginx/admin-guide/|Admin Guide]].
+Here we will look at some often used configuration parts and how we handle them
+at OpenWrt.
+At different places there are references to the official
+[[https://docs.nginx.com/nginx/technical-specs/|Technical Specs]]
+for further reading.
+
+**tl;dr:** When starting Nginx by ''/etc/init.d/nginx'', it creates its main
+configuration dynamically based on a minimal template and the
+[[docs:guide-user:base-system:uci|🡒UCI]] configuration.
+
+The UCI ''/etc/config/nginx'' contains initially:
+| ''config server '${LAN_NAME}''' | \
+Default server for the LAN, which includes all ''${CONF_DIR}*.locations''. |
+| ''config server '_redirect2ssl''' | \
+Redirects inexistent URLs to HTTPS. |
+
+It enables also the ''${CONF_DIR}'' directory for further configuration:
+| ''${CONF_DIR}\$NAME.conf'' | \
+Is included in the main configuration. \
+It is prioritized over a UCI ''config server '\$NAME' ''. |
+| ''${CONF_DIR}\$NAME.locations'' | \
+Is include in the ''${LAN_NAME}'' server and can be re-used for others, too. |
+| ''$(dirname "${CONF_DIR}")/restrict_locally'' | \
+Is include in the ''${LAN_NAME}'' server and allows only accesses from LAN. |
+
+Setup configuration (for a server ''\$NAME''):
+| ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$NAME'' | \
+Add/remove a self-signed certificate and corresponding directives. |
+| ''uci set nginx.\$NAME.access_log='logd openwrt''' | \
+Writes accesses to Openwrt’s \
+[[docs:guide-user:base-system:log.essentials|🡒logd]]. |
+| ''uci set nginx.\$NAME.error_log='logd' '' | \
+Writes errors to Openwrt’s \
+[[docs:guide-user:base-system:log.essentials|🡒logd]]. |
+| ''uci [set|add_list] nginx.\$NAME.key='value' '' | \
+Becomes a ''key value;'' directive if the //key// does not start with //uci_//. |
+| ''uci set nginx.\$NAME=[disable|server]'' |\
+Disable/enable inclusion in the dynamic conf.|
+| ''uci set nginx.global.uci_enable=false'' | \
+Use a custom ''${NGINX_CONF}'' rather than a dynamic conf. |
+
+
+
+==== Basic ====${MSG}
+
+
+We modify the configuration by changing servers saved in the UCI configuration
+at ''/etc/config/nginx'' and/or by creating different configuration files in the
+''${CONF_DIR}'' directory.
+These files use the file extensions ''.locations'' and ''.conf'' plus ''.crt''
+and ''.key'' for SSL certificates and keys.((
+We can disable a single configuration file in ''${CONF_DIR}'' by giving it
+another extension, e.g., by adding ''.disabled''.))
+For the new configuration to take effect, we must reload it by:
+
+<code bash>service nginx reload</code>
+
+For OpenWrt we use a special initial configuration, which is explained in the
+section [[#openwrt_s_defaults|🡓OpenWrt’s Defaults]].
+So, we can make a site available at a specific URL in the **LAN** by creating a
+''.locations'' file in the directory ''${CONF_DIR}''.
+Such a file consists just of some
+[[https://nginx.org/en/docs/http/ngx_http_core_module.html#location|
+location blocks]].
+Under the latter link, you can find also the official documentation for all
+available directives of the HTTP core of Nginx.
+Look for //location// in the Context list.
+
+The following example provides a simple template, see at the end for
+different [[#locations_for_apps|🡓Locations for Apps]]((look for
+[[https://github.com/search?utf8=%E2%9C%93&q=repo%3Aopenwrt%2Fpackages
++extension%3Alocations&type=Code&ref=advsearch&l=&l=|
+other packages using a .locations file]], too.)):
+
+<code nginx ${CONF_DIR}example.locations>
+location /ex/am/ple {
+ access_log off; # default: not logging accesses.
+ # access_log /proc/self/fd/1 openwrt; # use logd (init forwards stdout).
+ # error_log stderr; # default: logging to logd (init forwards stderr).
+ error_log /dev/null; # disable error logging after config file is read.
+ # (state path of a file for access_log/error_log to the file instead.)
+ index index.html;
+}
+# location /eg/static { … }
+</code>
+
+All location blocks in all ''.locations'' files must use different URLs,
+since they are all included in the ''${LAN_NAME}'' server that is part of the
+[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]].((
+We reserve the ''location /'' for making LuCI available under the root URL,
+e.g. [[https://192.168.1.1/|192.168.1.1/]].
+All other sites shouldn’t use the root ''location /'' without suffix.))
+We should use the root URL for other sites than LuCI only on **other** domain
+names, e.g., we could make a site available at https://${EXAMPLE_COM}/.
+In order to do that, we create [[#new_server_parts|🡓New Server Parts]] for all
+domain names.
+We can also activate SSL thereby, see
+[[#ssl_server_parts|🡓SSL Server Parts]].
+We use such server parts also for publishing sites to the internet (WAN)
+instead of making them available just locally (in the LAN).
+
+Via ''${CONF_DIR}*.conf'' files we can add directives to the //http// part of
+the configuration.
+If you would change the configuration ''$(basename "${UCI_CONF}").template''
+instead, it is not updated to new package's versions anymore.
+Although it is not recommended, you can also disable the whole UCI config and
+create your own ''${NGINX_CONF}''; then invoke:
+
+<code bash>uci set nginx.global.uci_enable=false</code>
+
+
+
+==== New Server Parts ====${MSG}
+
+
+For making the router reachable from the WAN at a registered domain name,
+it is not enough letting the
+[[docs:guide-user:firewall:firewall_configuration|🡒firewall]] accept requests
+(typically on ports 80 and 443) and giving the name server the internet IP
+address of the router (maybe updated automatically by a
+[[docs:guide-user:services:ddns:client|🡒DDNS Client]]).
+
+We also need to set up virtual hosting for this domain name by creating an
+appropriate server section in ''/etc/config/nginx''
+(or in a ''${CONF_DIR}*.conf'' file, which cannot be changed using UCI).
+All such parts are included in the main configuration of OpenWrt
+([[#openwrt_s_defaults|🡓OpenWrt’s Defaults]]).
+
+In the server part, we state the domain as
+[[https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name|
+server_name]].
+The link points to the same document as for the location blocks in the
+[[#basic|🡑Basic Configuration]]: the official documentation for all available
+directives of the HTTP core of Nginx.
+This time look for //server// in the Context list, too.
+The server part should also contain similar location blocks as
+++before.|
+We can re-include a ''.locations'' file that is included in the server part for
+the LAN by default.
+Then the site is reachable under the same path at both domains, e.g. by
+https://192.168.1.1/ex/am/ple as well as by https://${EXAMPLE_COM}/ex/am/ple.
+++
+
+We can add directives to a server in the UCI configuration by invoking
+''uci [set|add_list] nginx.${EXAMPLE_COM//./_}.key=value''.
+If the //key// is not starting with //uci_//, it becomes a ''key value;''
+++directive.|
+Although the UCI config does not support nesting like Nginx, we can add a whole
+block as //value//.
+++
+
+We cannot use dots in a //key// name other than in the //value//.
+In the following example we replace the dot in //${EXAMPLE_COM}// by an
+underscore for the UCI name of the server, but not for Nginx's //server_name//:
+
+<code bash>
+uci add nginx server &&
+uci rename nginx.@server[-1]=${EXAMPLE_COM//./_} &&
+uci add_list nginx.${EXAMPLE_COM//./_}.listen='80' &&
+uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' &&
+uci set nginx.${EXAMPLE_COM//./_}.server_name='${EXAMPLE_COM}' &&
+uci add_list nginx.${EXAMPLE_COM//./_}.include=\
+'$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations'
+# uci add_list nginx.${EXAMPLE_COM//./_}.location='/ { … }' \
+# root location for this server.
+</code>
+
+We can disable respective re-enable this server again by:
+
+<code bash>
+uci set nginx.${EXAMPLE_COM//./_}=disable # respective: \
+uci set nginx.${EXAMPLE_COM//./_}=server
+</code>
+
+These changes are made in the RAM (and can be used until a reboot), we can save
+them permanently by:
+
+<code bash>uci commit nginx</code>
+
+For creating a similar ''${CONF_DIR}${EXAMPLE_COM}.conf'', we can adopt the
+following:
+
+<code nginx ${CONF_DIR}${EXAMPLE_COM}.conf>
+server {
+ listen 80;
+ listen [::]:80;
+ server_name ${EXAMPLE_COM};
+ include '$(basename ${CONF_DIR})/${EXAMPLE_COM}.locations';
+ # location / { … } # root location for this server.
+}
+</code>
+
+[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include the UCI server
+''config server '_redirect2ssl' ''.
+It acts as //default_server// for HTTP and redirects requests for inexistent
+URLs to HTTPS.
+For making another domain name accessible to all addresses, the corresponding
+server part should listen on port //80// and contain the FQDN as
+//server_name//, cf. the official documentation on
+[[https://nginx.org/en/docs/http/request_processing.html|request_processing]].
+
+Furthermore, there is a UCI server named ''${LAN_NAME}''.
+It is the //default_server// for HTTPS and allows connections from LAN only.
+It includes the file ''$(dirname "${CONF_DIR}")/restrict_locally'' with
+appropriate //allow/deny// directives, cf. the official documentation on
+[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]].
+
+
+
+==== SSL Server Parts ====${MSG}
+
+
+For enabling HTTPS for a domain we need a SSL certificate as well as its key and
+add them by the directives //ssl_certificate// respective
+//ssl_certificate_key// to the server part of the domain
+([[https://nginx.org/en/docs/http/configuring_https_servers.html#sni|TLS SNI]]
+is supported by default).
+The rest of the configuration is similar as for general
+[[#new_server_parts|🡑New Server Parts]].
+We only have to adjust the listen directives by adding the //ssl// parameter and
+changing the port from //80// to //443//.
+
+The official documentation of the SSL module contains an
+[[https://nginx.org/en/docs/http/ngx_http_ssl_module.html#example|
+example]] with some optimizations.
+We can extend an existing UCI server section similarly, e.g., for the above
+''config server '${EXAMPLE_COM//./_}' '' we invoke:
+
+<code bash>
+# Instead of 'del_list' the listen* entries, we could use '443 ssl' beforehand.
+uci del_list nginx.${EXAMPLE_COM//./_}.listen='80' &&
+uci del_list nginx.${EXAMPLE_COM//./_}.listen='[::]:80' &&
+uci add_list nginx.${EXAMPLE_COM//./_}.listen='443 ssl' &&
+uci add_list nginx.${EXAMPLE_COM//./_}.listen='[::]:443 ssl' &&
+uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate=\
+'${CONF_DIR}${EXAMPLE_COM}.crt' &&
+uci set nginx.${EXAMPLE_COM//./_}.ssl_certificate_key=\
+'${CONF_DIR}${EXAMPLE_COM}.key' &&
+uci set nginx.${EXAMPLE_COM//./_}.ssl_session_cache=\
+'${SSL_SESSION_CACHE_ARG}' &&
+uci set nginx.${EXAMPLE_COM//./_}.ssl_session_timeout=\
+'${SSL_SESSION_TIMEOUT_ARG}' &&
+uci commit nginx
+</code>
+
+For making the server in ''${CONF_DIR}${EXAMPLE_COM}.conf'' available
+via SSL, we can make similar changes there.
+
+The following command creates a **self-signed** SSL certificate and changes the
+corresponding configuration:
+
+<code bash>$(basename "${NGINX_UTIL}") ${ADD_SSL_FCT} ${EXAMPLE_COM}</code>
+
+ - If a ''$(basename "${CONF_DIR}")/${EXAMPLE_COM}.conf'' file exists, it\
+ adds //ssl_*// directives and changes the //listen// directives there.\
+ Else it does that similarly to the example above for a ++selected UCI\
+ server.| Hereby it searches the UCI config first for a server with the\
+ given name and then for a server whose //server_name// contains the name.\
+ For //${EXAMPLE_COM}// it is the latter as a UCI key cannot have dots.++
+ - It checks if there is a certificate with key for '${EXAMPLE_COM}' that is\
+ valid for at least 13 months or tries to create a self-signed one.
+ - When cron is activated, it installs a cron job for renewing the self-signed\
+ certificate every year if needed, too. We can activate cron by: \
+ <code bash>service cron enable && service cron start</code>
+
+This can be undone by invoking:
+
+<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM}</code>
+
+For using an SSL certificate and key that are managed otherwise, there is:
+
+<code bash>$(basename "${NGINX_UTIL}") add_ssl ${EXAMPLE_COM} "\$MANAGER" \
+"/absolute/path/to/crt" "/absolute/path/to/key"</code>
+
+It only adds //ssl_*// directives and changes the //listen// directives in
+the appropriate configuration, but does not create or change the certificate
+or its key. This can be reverted by:
+
+<code bash>$(basename "${NGINX_UTIL}") del_ssl ${EXAMPLE_COM} "\$MANAGER"</code>
+
+For example [[https://github.com/ndilieto/uacme|uacme]] or
+[[https://github.com/Neilpang/acme.sh|acme.sh]] can be used for creating an SSL
+certificate signed by Let’s Encrypt and changing the config
+++accordingly.|
+They call ''$(basename "${NGINX_UTIL}") add_ssl \$FQDN acme \$CRT \$KEY''
+internally.++
+We can install them by:
+
+<code bash>
+opkg update && opkg install uacme #or: acme #and for LuCI: luci-app-acme
+</code>
+
+[[#openwrt_s_defaults|🡓OpenWrt’s Defaults]] include a UCI server for the LAN:
+''config server '${LAN_NAME}' ''.
+It has //ssl_*// directives prepared for a self-signed((Let’s Encrypt (and other
+CAs) cannot sign certificates of a **local** server.))
+SSL certificate, which is created on the first start of Nginx.
+The server listens on all addresses, is the //default_server// for HTTPS and
+allows connections from LAN only (by including the file ''restrict_locally''
+with //allow/deny// directives, cf. the official documentation on
+[[https://nginx.org/en/docs/http/ngx_http_access_module.html|limiting access]]).
+
+For making another domain name accessible to all addresses, the corresponding
+SSL server part should listen on port //443// and contain the FQDN as
+//server_name//, cf. the official documentation on
+[[https://nginx.org/en/docs/http/request_processing.html|request_processing]].
+
+Furthermore, there is also a UCI server named ''_redirect2ssl'', which listens
+on all addresses, acts as //default_server// for HTTP and redirects requests for
+inexistent URLs to HTTPS.
+
+
+
+==== OpenWrt’s Defaults ====${MSG}
+
+
+Since Nginx is compiled with these presets, we can pretend that the main
+configuration will always contain the following directives
+(though we can overwrite them):
+
+<code nginx>$(ifConfEcho --pid-path pid)\
+$(ifConfEcho --lock-path lock_file)\
+$(ifConfEcho --error-log-path error_log)\
+$(false && ifConfEcho --http-log-path access_log)\
+$(ifConfEcho --http-proxy-temp-path proxy_temp_path)\
+$(ifConfEcho --http-client-body-temp-path client_body_temp_path)\
+$(ifConfEcho --http-fastcgi-temp-path fastcgi_temp_path)\
+</code>
+
+When starting or reloading the Nginx service, the ''/etc/init.d/nginx'' script
+sets also the following directives
+(so we cannot change them in the used configuration file):
+
+<code nginx>
+daemon off; # procd expects services to run in the foreground
+</code>
+
+Then, the init sript creates the main configuration
+''$(basename "${UCI_CONF}")'' dynamically from the template:
+
+$(code "${UCI_CONF}.template")
+
+So, the access log is turned off by default and we can look at the error log
+by ''logread'', as init.d script forwards stderr and stdout to the
+[[docs:guide-user:base-system:log.essentials|🡒runtime log]].
+We can set the //error_log// and //access_log// to files, where the log
+messages are forwarded to instead (after the configuration is read).
+And for redirecting the access log of a //server// or //location// to the logd,
+too, we insert the following directive in the corresponding block:
+
+<code nginx> access_log /proc/self/fd/1 openwrt;</code>
+
+If we setup a server through UCI, we can use the options //error_log// and/or
+//access_log// also with the special path
+++'logd'.|
+When initializing the Nginx service, this special path is replaced by //stderr//
+respective ///proc/self/fd/1// (which are forwarded to the runtime log).
+++
+
+For creating the configuration from the template shown above, Nginx’s init
+script replaces the comment ''#UCI_HTTP_CONFIG'' by all UCI servers.
+For each server section in the the UCI configuration, it basically copies all
+options into a Nginx //server { … }// part, in detail:
+ * Options starting with ''uci_'' are skipped. Currently there is only\
+ the ''option ${MANAGE_SSL}=…'' in ++usage.| It is set to\
+ //'self-signed'// when invoking\
+ ''$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} \$NAME''.\
+ Then the corresponding certificate is re-newed if it is about to expire.\
+ All those certificates are checked on the initialization of the Nginx service\
+ and if Cron is available, it is deployed for checking them annually, too.++
+ * All other lists or options of the form ''key='value' '' are written\
+ one-to-one as ''key value;'' directives to the configuration file.\
+ Just the path //logd// has a special meaning for the logging directives\
+ (described in the previous paragraph).
+
+The init.d script of Nginx uses the //$(basename ${NGINX_UTIL})// for creating
+the configuration file
+++in RAM.|
+The main configuration ''${UCI_CONF}'' is a symbolic link to this place
+(it is a dead link if the Nginx service is not running).
+++
+
+We could use a custom configuration created at ''${NGINX_CONF}'' instead of the
+dynamic configuration, too.((
+For using a custom configuration at ''${NGINX_CONF}'', we execute
+<code bash>uci set nginx.global.uci_enable='false' </code>
+Then the rest of the UCI config is ignored and //init.d// will not create the
+main configuration dynamically from the template anymore.
+Invoking ''$(basename ${NGINX_UTIL}) [${ADD_SSL_FCT}|del_ssl] \$FQDN''
+will still try to change a server in ''$(basename "${CONF_DIR}")/\$FQDN.conf''
+(this is less reliable than for a UCI config as it uses regular expressions, not
+a complete parser for the Nginx configuration).))
+This is not encouraged since you cannot setup servers using UCI anymore.
+Rather, we can put custom configuration parts to ''.conf'' files in the
+''${CONF_DIR}'' directory.
+The main configuration pulls in all ''$(basename "${CONF_DIR}")/*.conf'' files
+into the //http {…}// block behind the created UCI servers.
+
+The initial UCI config is enabled and contains two server section:
+
+$(code "/etc/config/nginx" "nginx.config")
+
+While the LAN server is the //default_server// for HTTPS, the server
+redirecting requests for an inexistent ''server_name'' from HTTP to HTTPS acts
+as //default_server// if there is ++no other|;
+it uses an invalid name for that, more in the official documentation on
+[[https://nginx.org/en/docs/http/request_processing.html|request_processing]]
+++.
+
+The LAN server pulls in all ''.locations'' files from the directory
+''${CONF_DIR}''.
+We can install the location parts of different sites there (see
+[[#basic|🡑Basic Configuration]]) and re-include them into other servers.
+This is needed especially for making them available to the WAN
+([[#new_server_parts|🡑New Server Parts]]).
+The LAN server listens for all addresses on port //443// and restricts the
+access to local addresses by including:
+$(code "$(dirname "${CONF_DIR}")/restrict_locally")
+
+When starting or reloading the Nginx service, the init.d looks which UCI servers
+have set ''option ${MANAGE_SSL} 'self-signed' '', e.g. the LAN server.
+For all those servers it checks if there is a certificate that is still valid
+for 13 months or (re-)creates a self-signed one.
+If there is any such server, it installs also a cron job that checks the
+corresponding certificates once a year.
+The option ''${MANAGE_SSL}'' is set to //'self-signed'// respectively removed
+from a UCI server named ''${EXAMPLE_COM//./_}'' by the following
+(see [[#ssl_server_parts|🡑SSL Server Parts]], too):
+
+<code bash>
+$(basename ${NGINX_UTIL}) ${ADD_SSL_FCT} ${EXAMPLE_COM//./_} \
+# respectively: \
+$(basename ${NGINX_UTIL}) del_ssl ${EXAMPLE_COM//./_}
+</code>
+
+
+EOF
diff --git a/external/subpack/net/nginx-util/files/nginx.config b/external/subpack/net/nginx-util/files/nginx.config
new file mode 100644
index 0000000..4f07ae1
--- /dev/null
+++ b/external/subpack/net/nginx-util/files/nginx.config
@@ -0,0 +1,22 @@
+
+config main global
+ option uci_enable 'true'
+
+config server '_lan'
+ list listen '443 ssl default_server'
+ list listen '[::]:443 ssl default_server'
+ option server_name '_lan'
+ list include 'restrict_locally'
+ list include 'conf.d/*.locations'
+ option uci_manage_ssl 'self-signed'
+ option ssl_certificate '/etc/nginx/conf.d/_lan.crt'
+ option ssl_certificate_key '/etc/nginx/conf.d/_lan.key'
+ option ssl_session_cache 'shared:SSL:32k'
+ option ssl_session_timeout '64m'
+ option access_log 'off; # logd openwrt'
+
+config server '_redirect2ssl'
+ list listen '80'
+ list listen '[::]:80'
+ option server_name '_redirect2ssl'
+ option return '302 https://$host$request_uri'
diff --git a/external/subpack/net/nginx-util/files/restrict_locally b/external/subpack/net/nginx-util/files/restrict_locally
new file mode 100644
index 0000000..0b791cd
--- /dev/null
+++ b/external/subpack/net/nginx-util/files/restrict_locally
@@ -0,0 +1,10 @@
+ allow ::1;
+ allow fc00::/7;
+ allow fec0::/10;
+ allow fe80::/10;
+ allow 127.0.0.0/8;
+ allow 10.0.0.0/8;
+ allow 172.16.0.0/12;
+ allow 192.168.0.0/16;
+ allow 169.254.0.0/16;
+ deny all;
diff --git a/external/subpack/net/nginx-util/files/uci.conf.template b/external/subpack/net/nginx-util/files/uci.conf.template
new file mode 100644
index 0000000..1c611d9
--- /dev/null
+++ b/external/subpack/net/nginx-util/files/uci.conf.template
@@ -0,0 +1,32 @@
+# Consider using UCI or creating files in /etc/nginx/conf.d/ for configuration.
+# Parsing UCI configuration is skipped if uci set nginx.global.uci_enable=false
+# For details see: https://openwrt.org/docs/guide-user/services/webserver/nginx
+
+worker_processes auto;
+
+user root;
+
+events {}
+
+http {
+ access_log off;
+ log_format openwrt
+ '$request_method $scheme://$host$request_uri => $status'
+ ' (${body_bytes_sent}B in ${request_time}s) <- $http_referer';
+
+ include mime.types;
+ default_type application/octet-stream;
+ sendfile on;
+
+ client_max_body_size 128M;
+ large_client_header_buffers 2 1k;
+
+ gzip on;
+ gzip_vary on;
+ gzip_proxied any;
+
+ root /www;
+
+ #UCI_HTTP_CONFIG
+ include conf.d/*.conf;
+}
diff --git a/external/subpack/net/nginx-util/src/.clang-format b/external/subpack/net/nginx-util/src/.clang-format
new file mode 100644
index 0000000..3c1ba73
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/.clang-format
@@ -0,0 +1,171 @@
+---
+Language: Cpp
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveMacros: false
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Always
+AllowShortCaseLabelsOnASingleLine: true
+# AllowShortEnumsOnASingleLine: true
+AllowShortLambdasOnASingleLine: All
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Always
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: true
+BinPackParameters: false
+# BitFieldColonSpacing: After
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: MultiLine
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: true
+ BeforeElse: true
+ # BeforeLambdaBody: true
+ # BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+BreakBeforeBinaryOperators: None
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 100
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^<ext/.*\.h>'
+ Priority: 2
+ SortPriority: 0
+ - Regex: '^<.*\.h>'
+ Priority: 1
+ SortPriority: 0
+ - Regex: '^<.*'
+ Priority: 2
+ SortPriority: 0
+ - Regex: '.*'
+ Priority: 3
+ SortPriority: 0
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IncludeIsMainSourceRegex: ''
+IndentCaseLabels: true
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+RawStringFormats:
+ - Language: Cpp
+ Delimiters:
+ - cc
+ - CC
+ - cpp
+ - Cpp
+ - CPP
+ - 'c++'
+ - 'C++'
+ CanonicalDelimiter: ''
+ BasedOnStyle: google
+ - Language: TextProto
+ Delimiters:
+ - pb
+ - PB
+ - proto
+ - PROTO
+ EnclosingFunctions:
+ - EqualsProto
+ - EquivToProto
+ - PARSE_PARTIAL_TEXT_PROTO
+ - PARSE_TEST_PROTO
+ - PARSE_TEXT_PROTO
+ - ParseTextOrDie
+ - ParseTextProtoOrDie
+ CanonicalDelimiter: ''
+ BasedOnStyle: google
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+Standard: Auto
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 4
+UseCRLF: false
+UseTab: Never
+...
+
diff --git a/external/subpack/net/nginx-util/src/.clang-tidy b/external/subpack/net/nginx-util/src/.clang-tidy
new file mode 100644
index 0000000..c34ab6a
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/.clang-tidy
@@ -0,0 +1,407 @@
+---
+Checks: 'clang-diagnostic-*,clang-analyzer-*,*,-fuchsia-*,-misc-definitions-in-headers,-llvm-header-guard,-*-qualified-auto,-llvm-include-order'
+WarningsAsErrors: ''
+HeaderFilterRegex: '.*'
+AnalyzeTemporaryDtors: false
+FormatStyle: file
+CheckOptions:
+ - key: abseil-string-find-startswith.AbseilStringsMatchHeader
+ value: 'absl/strings/match.h'
+ - key: abseil-string-find-startswith.IncludeStyle
+ value: llvm
+ - key: abseil-string-find-startswith.StringLikeClasses
+ value: '::std::basic_string'
+ - key: bugprone-argument-comment.CommentBoolLiterals
+ value: '0'
+ - key: bugprone-argument-comment.CommentCharacterLiterals
+ value: '0'
+ - key: bugprone-argument-comment.CommentFloatLiterals
+ value: '0'
+ - key: bugprone-argument-comment.CommentIntegerLiterals
+ value: '0'
+ - key: bugprone-argument-comment.CommentNullPtrs
+ value: '0'
+ - key: bugprone-argument-comment.CommentStringLiterals
+ value: '0'
+ - key: bugprone-argument-comment.CommentUserDefinedLiterals
+ value: '0'
+ - key: bugprone-argument-comment.IgnoreSingleArgument
+ value: '0'
+ - key: bugprone-argument-comment.StrictMode
+ value: '0'
+ - key: bugprone-assert-side-effect.AssertMacros
+ value: assert
+ - key: bugprone-assert-side-effect.CheckFunctionCalls
+ value: '0'
+ - key: bugprone-dangling-handle.HandleClasses
+ value: 'std::basic_string_view;std::experimental::basic_string_view'
+ - key: bugprone-dynamic-static-initializers.HeaderFileExtensions
+ value: ',h,hh,hpp,hxx'
+ - key: bugprone-exception-escape.FunctionsThatShouldNotThrow
+ value: ''
+ - key: bugprone-exception-escape.IgnoredExceptions
+ value: ''
+ - key: bugprone-misplaced-widening-cast.CheckImplicitCasts
+ value: '0'
+ - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions
+ value: '1'
+ - key: bugprone-signed-char-misuse.CharTypdefsToIgnore
+ value: ''
+ - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant
+ value: '1'
+ - key: bugprone-sizeof-expression.WarnOnSizeOfConstant
+ value: '1'
+ - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression
+ value: '0'
+ - key: bugprone-sizeof-expression.WarnOnSizeOfThis
+ value: '1'
+ - key: bugprone-string-constructor.LargeLengthThreshold
+ value: '8388608'
+ - key: bugprone-string-constructor.WarnOnLargeLength
+ value: '1'
+ - key: bugprone-suspicious-enum-usage.StrictMode
+ value: '0'
+ - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens
+ value: '5'
+ - key: bugprone-suspicious-missing-comma.RatioThreshold
+ value: '0.200000'
+ - key: bugprone-suspicious-missing-comma.SizeThreshold
+ value: '5'
+ - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions
+ value: ''
+ - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison
+ value: '1'
+ - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison
+ value: '0'
+ - key: bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit
+ value: '16'
+ - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField
+ value: '1'
+ - key: bugprone-unused-return-value.CheckedFunctions
+ value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty'
+ - key: cert-dcl16-c.IgnoreMacros
+ value: '1'
+ - key: cert-dcl16-c.NewSuffixes
+ value: 'L;LL;LU;LLU'
+ - key: cert-dcl59-cpp.HeaderFileExtensions
+ value: ',h,hh,hpp,hxx'
+ - key: cert-err09-cpp.CheckThrowTemporaries
+ value: '1'
+ - key: cert-err61-cpp.CheckThrowTemporaries
+ value: '1'
+ - key: cert-msc32-c.DisallowedSeedTypes
+ value: 'time_t,std::time_t'
+ - key: cert-msc51-cpp.DisallowedSeedTypes
+ value: 'time_t,std::time_t'
+ - key: cert-oop11-cpp.IncludeStyle
+ value: llvm
+ - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
+ value: '0'
+ - key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues
+ value: '1.0;100.0;'
+ - key: cppcoreguidelines-avoid-magic-numbers.IgnoredIntegerValues
+ value: '1;2;3;4;'
+ - key: cppcoreguidelines-explicit-virtual-functions.AllowOverrideAndFinal
+ value: '0'
+ - key: cppcoreguidelines-explicit-virtual-functions.FinalSpelling
+ value: final
+ - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
+ value: '1'
+ - key: cppcoreguidelines-explicit-virtual-functions.OverrideSpelling
+ value: override
+ - key: cppcoreguidelines-macro-usage.AllowedRegexp
+ value: '^DEBUG_*'
+ - key: cppcoreguidelines-macro-usage.CheckCapsOnly
+ value: '0'
+ - key: cppcoreguidelines-macro-usage.IgnoreCommandLineMacros
+ value: '1'
+ - key: cppcoreguidelines-no-malloc.Allocations
+ value: '::malloc;::calloc'
+ - key: cppcoreguidelines-no-malloc.Deallocations
+ value: '::free'
+ - key: cppcoreguidelines-no-malloc.Reallocations
+ value: '::realloc'
+ - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
+ value: '1'
+ - key: cppcoreguidelines-owning-memory.LegacyResourceConsumers
+ value: '::free;::realloc;::freopen;::fclose'
+ - key: cppcoreguidelines-owning-memory.LegacyResourceProducers
+ value: '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile'
+ - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader
+ value: ''
+ - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle
+ value: '0'
+ - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays
+ value: '0'
+ - key: cppcoreguidelines-pro-type-member-init.UseAssignment
+ value: '0'
+ - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
+ value: '0'
+ - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
+ value: '0'
+ - key: google-build-namespaces.HeaderFileExtensions
+ value: ',h,hh,hpp,hxx'
+ - key: google-global-names-in-headers.HeaderFileExtensions
+ value: ',h,hh,hpp,hxx'
+ - key: google-readability-braces-around-statements.ShortStatementLines
+ value: '1'
+ - key: google-readability-function-size.BranchThreshold
+ value: '4294967295'
+ - key: google-readability-function-size.LineThreshold
+ value: '4294967295'
+ - key: google-readability-function-size.NestingThreshold
+ value: '4294967295'
+ - key: google-readability-function-size.ParameterThreshold
+ value: '4294967295'
+ - key: google-readability-function-size.StatementThreshold
+ value: '800'
+ - key: google-readability-function-size.VariableThreshold
+ value: '4294967295'
+ - key: google-readability-namespace-comments.ShortNamespaceLines
+ value: '10'
+ - key: google-readability-namespace-comments.SpacesBeforeComments
+ value: '2'
+ - key: google-runtime-int.SignedTypePrefix
+ value: int
+ - key: google-runtime-int.TypeSuffix
+ value: ''
+ - key: google-runtime-int.UnsignedTypePrefix
+ value: uint
+ - key: google-runtime-references.WhiteListTypes
+ value: ''
+ - key: hicpp-braces-around-statements.ShortStatementLines
+ value: '0'
+ - key: hicpp-function-size.BranchThreshold
+ value: '4294967295'
+ - key: hicpp-function-size.LineThreshold
+ value: '4294967295'
+ - key: hicpp-function-size.NestingThreshold
+ value: '4294967295'
+ - key: hicpp-function-size.ParameterThreshold
+ value: '4294967295'
+ - key: hicpp-function-size.StatementThreshold
+ value: '800'
+ - key: hicpp-function-size.VariableThreshold
+ value: '4294967295'
+ - key: hicpp-member-init.IgnoreArrays
+ value: '0'
+ - key: hicpp-member-init.UseAssignment
+ value: '0'
+ - key: hicpp-move-const-arg.CheckTriviallyCopyableMove
+ value: '1'
+ - key: hicpp-multiway-paths-covered.WarnOnMissingElse
+ value: '0'
+ - key: hicpp-named-parameter.IgnoreFailedSplit
+ value: '0'
+ - key: hicpp-no-malloc.Allocations
+ value: '::malloc;::calloc'
+ - key: hicpp-no-malloc.Deallocations
+ value: '::free'
+ - key: hicpp-no-malloc.Reallocations
+ value: '::realloc'
+ - key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals
+ value: '0'
+ - key: hicpp-special-member-functions.AllowMissingMoveFunctions
+ value: '0'
+ - key: hicpp-special-member-functions.AllowSoleDefaultDtor
+ value: '0'
+ - key: hicpp-uppercase-literal-suffix.IgnoreMacros
+ value: '1'
+ - key: hicpp-uppercase-literal-suffix.NewSuffixes
+ value: ''
+ - key: hicpp-use-auto.MinTypeNameLength
+ value: '5'
+ - key: hicpp-use-auto.RemoveStars
+ value: '0'
+ - key: hicpp-use-emplace.ContainersWithPushBack
+ value: '::std::vector;::std::list;::std::deque'
+ - key: hicpp-use-emplace.SmartPointers
+ value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
+ - key: hicpp-use-emplace.TupleMakeFunctions
+ value: '::std::make_pair;::std::make_tuple'
+ - key: hicpp-use-emplace.TupleTypes
+ value: '::std::pair;::std::tuple'
+ - key: hicpp-use-equals-default.IgnoreMacros
+ value: '1'
+ - key: hicpp-use-equals-delete.IgnoreMacros
+ value: '1'
+ - key: hicpp-use-noexcept.ReplacementString
+ value: ''
+ - key: hicpp-use-noexcept.UseNoexceptFalse
+ value: '1'
+ - key: hicpp-use-nullptr.NullMacros
+ value: ''
+ - key: hicpp-use-override.AllowOverrideAndFinal
+ value: '0'
+ - key: hicpp-use-override.FinalSpelling
+ value: final
+ - key: hicpp-use-override.IgnoreDestructors
+ value: '0'
+ - key: hicpp-use-override.OverrideSpelling
+ value: override
+ - key: llvm-namespace-comment.ShortNamespaceLines
+ value: '1'
+ - key: llvm-namespace-comment.SpacesBeforeComments
+ value: '1'
+ - key: misc-throw-by-value-catch-by-reference.CheckThrowTemporaries
+ value: '1'
+ - key: misc-unused-parameters.StrictMode
+ value: '0'
+ - key: modernize-loop-convert.MaxCopySize
+ value: '16'
+ - key: modernize-loop-convert.MinConfidence
+ value: reasonable
+ - key: modernize-loop-convert.NamingStyle
+ value: CamelCase
+ - key: modernize-make-shared.IgnoreMacros
+ value: '1'
+ - key: modernize-make-shared.IncludeStyle
+ value: '0'
+ - key: modernize-make-shared.MakeSmartPtrFunction
+ value: 'std::make_shared'
+ - key: modernize-make-shared.MakeSmartPtrFunctionHeader
+ value: memory
+ - key: modernize-make-unique.IgnoreMacros
+ value: '1'
+ - key: modernize-make-unique.IncludeStyle
+ value: '0'
+ - key: modernize-make-unique.MakeSmartPtrFunction
+ value: 'std::make_unique'
+ - key: modernize-make-unique.MakeSmartPtrFunctionHeader
+ value: memory
+ - key: modernize-pass-by-value.IncludeStyle
+ value: llvm
+ - key: modernize-pass-by-value.ValuesOnly
+ value: '0'
+ - key: modernize-raw-string-literal.ReplaceShorterLiterals
+ value: '0'
+ - key: modernize-replace-auto-ptr.IncludeStyle
+ value: llvm
+ - key: modernize-replace-random-shuffle.IncludeStyle
+ value: llvm
+ - key: modernize-use-auto.MinTypeNameLength
+ value: '5'
+ - key: modernize-use-auto.RemoveStars
+ value: '0'
+ - key: modernize-use-default-member-init.IgnoreMacros
+ value: '1'
+ - key: modernize-use-default-member-init.UseAssignment
+ value: '0'
+ - key: modernize-use-emplace.ContainersWithPushBack
+ value: '::std::vector;::std::list;::std::deque'
+ - key: modernize-use-emplace.SmartPointers
+ value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
+ - key: modernize-use-emplace.TupleMakeFunctions
+ value: '::std::make_pair;::std::make_tuple'
+ - key: modernize-use-emplace.TupleTypes
+ value: '::std::pair;::std::tuple'
+ - key: modernize-use-equals-default.IgnoreMacros
+ value: '1'
+ - key: modernize-use-equals-delete.IgnoreMacros
+ value: '1'
+ - key: modernize-use-nodiscard.ReplacementString
+ value: '[[nodiscard]]'
+ - key: modernize-use-noexcept.ReplacementString
+ value: ''
+ - key: modernize-use-noexcept.UseNoexceptFalse
+ value: '1'
+ - key: modernize-use-nullptr.NullMacros
+ value: 'NULL'
+ - key: modernize-use-override.AllowOverrideAndFinal
+ value: '0'
+ - key: modernize-use-override.FinalSpelling
+ value: final
+ - key: modernize-use-override.IgnoreDestructors
+ value: '0'
+ - key: modernize-use-override.OverrideSpelling
+ value: override
+ - key: modernize-use-transparent-functors.SafeMode
+ value: '0'
+ - key: modernize-use-using.IgnoreMacros
+ value: '1'
+ - key: objc-forbidden-subclassing.ForbiddenSuperClassNames
+ value: 'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView'
+ - key: openmp-exception-escape.IgnoredExceptions
+ value: ''
+ - key: performance-faster-string-find.StringLikeClasses
+ value: 'std::basic_string'
+ - key: performance-for-range-copy.AllowedTypes
+ value: ''
+ - key: performance-for-range-copy.WarnOnAllAutoCopies
+ value: '0'
+ - key: performance-inefficient-string-concatenation.StrictMode
+ value: '0'
+ - key: performance-inefficient-vector-operation.EnableProto
+ value: '0'
+ - key: performance-inefficient-vector-operation.VectorLikeClasses
+ value: '::std::vector'
+ - key: performance-move-const-arg.CheckTriviallyCopyableMove
+ value: '1'
+ - key: performance-move-constructor-init.IncludeStyle
+ value: llvm
+ - key: performance-no-automatic-move.AllowedTypes
+ value: ''
+ - key: performance-type-promotion-in-math-fn.IncludeStyle
+ value: llvm
+ - key: performance-unnecessary-copy-initialization.AllowedTypes
+ value: ''
+ - key: performance-unnecessary-value-param.AllowedTypes
+ value: ''
+ - key: performance-unnecessary-value-param.IncludeStyle
+ value: llvm
+ - key: portability-simd-intrinsics.Std
+ value: ''
+ - key: portability-simd-intrinsics.Suggest
+ value: '0'
+ - key: readability-braces-around-statements.ShortStatementLines
+ value: '0'
+ - key: readability-else-after-return.WarnOnUnfixable
+ value: '1'
+ - key: readability-function-size.BranchThreshold
+ value: '4294967295'
+ - key: readability-function-size.LineThreshold
+ value: '4294967295'
+ - key: readability-function-size.NestingThreshold
+ value: '4294967295'
+ - key: readability-function-size.ParameterThreshold
+ value: '4294967295'
+ - key: readability-function-size.StatementThreshold
+ value: '800'
+ - key: readability-function-size.VariableThreshold
+ value: '4294967295'
+ - key: readability-identifier-naming.IgnoreFailedSplit
+ value: '0'
+ - key: readability-implicit-bool-conversion.AllowIntegerConditions
+ value: '0'
+ - key: readability-implicit-bool-conversion.AllowPointerConditions
+ value: '0'
+ - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros
+ value: '1'
+ - key: readability-inconsistent-declaration-parameter-name.Strict
+ value: '0'
+ - key: readability-magic-numbers.IgnoredFloatingPointValues
+ value: '1.0;100.0;'
+ - key: readability-magic-numbers.IgnoredIntegerValues
+ value: '1;2;3;4;'
+ - key: readability-redundant-member-init.IgnoreBaseInCopyConstructors
+ value: '0'
+ - key: readability-redundant-smartptr-get.IgnoreMacros
+ value: '1'
+ - key: readability-redundant-string-init.StringNames
+ value: '::std::basic_string'
+ - key: readability-simplify-boolean-expr.ChainedConditionalAssignment
+ value: '0'
+ - key: readability-simplify-boolean-expr.ChainedConditionalReturn
+ value: '0'
+ - key: readability-simplify-subscript-expr.Types
+ value: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array'
+ - key: readability-static-accessed-through-instance.NameSpecifierNestingThreshold
+ value: '3'
+ - key: readability-uppercase-literal-suffix.IgnoreMacros
+ value: '1'
+ - key: readability-uppercase-literal-suffix.NewSuffixes
+ value: ''
+ - key: zircon-temporary-objects.Names
+ value: ''
+...
+
diff --git a/external/subpack/net/nginx-util/src/CMakeLists.txt b/external/subpack/net/nginx-util/src/CMakeLists.txt
new file mode 100644
index 0000000..2adff1c
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/CMakeLists.txt
@@ -0,0 +1,62 @@
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(nginx-util CXX)
+SET(CMAKE_CXX_STANDARD 17)
+
+INCLUDE(CheckFunctionExists)
+
+ADD_DEFINITIONS(-Os -Wall -Werror -Wextra -g3)
+ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations -Wshadow)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+FIND_PATH(uci_include_dir uci.h)
+FIND_LIBRARY(uci NAMES uci)
+INCLUDE_DIRECTORIES(${uci_include_dir})
+
+FIND_PATH(ubox_include_dir libubox/blobmsg.h)
+FIND_LIBRARY(ubox NAMES ubox)
+INCLUDE_DIRECTORIES(${ubox_include_dir})
+
+IF(UBUS)
+
+ADD_COMPILE_DEFINITIONS(VERSION=${VERSION})
+
+FIND_PATH(ubus_include_dir libubus.h)
+FIND_LIBRARY(ubus NAMES ubus)
+INCLUDE_DIRECTORIES(${ubus_include_dir})
+
+ADD_EXECUTABLE(nginx-ssl-util nginx-util.cpp)
+TARGET_LINK_LIBRARIES(nginx-ssl-util ${uci} ${ubox} ${ubus} pthread ssl crypto pcre)
+INSTALL(TARGETS nginx-ssl-util RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(nginx-ssl-util-nopcre nginx-util.cpp)
+TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-nopcre PUBLIC -DNO_PCRE)
+TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre ${uci} ${ubox} ${ubus} pthread ssl crypto)
+INSTALL(TARGETS nginx-ssl-util-nopcre RUNTIME DESTINATION bin)
+
+ELSE()
+
+ADD_COMPILE_DEFINITIONS(VERSION=0)
+
+CONFIGURE_FILE(test-px5g.sh test-px5g.sh COPYONLY)
+CONFIGURE_FILE(test-nginx-util.sh test-nginx-util.sh COPYONLY)
+CONFIGURE_FILE(test-nginx-util-root.sh test-nginx-util-root.sh COPYONLY)
+CONFIGURE_FILE(../files/nginx.config config-nginx-ssl COPYONLY)
+CONFIGURE_FILE(../files/uci.conf.template uci.conf.template COPYONLY)
+
+ADD_EXECUTABLE(px5g px5g.cpp)
+TARGET_LINK_LIBRARIES(px5g ssl crypto)
+INSTALL(TARGETS px5g RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(nginx-ssl-util-noubus nginx-util.cpp)
+TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-noubus PUBLIC -DNO_UBUS)
+TARGET_LINK_LIBRARIES(nginx-ssl-util-noubus ${uci} ${ubox} pthread ssl crypto pcre)
+INSTALL(TARGETS nginx-ssl-util-noubus RUNTIME DESTINATION bin)
+
+ADD_EXECUTABLE(nginx-ssl-util-nopcre-noubus nginx-util.cpp)
+TARGET_COMPILE_DEFINITIONS(nginx-ssl-util-nopcre-noubus PUBLIC -DNO_PCRE -DNO_UBUS)
+TARGET_LINK_LIBRARIES(nginx-ssl-util-nopcre-noubus ${uci} ${ubox} pthread ssl crypto)
+INSTALL(TARGETS nginx-ssl-util-nopcre-noubus RUNTIME DESTINATION bin)
+
+ENDIF()
diff --git a/external/subpack/net/nginx-util/src/LICENSE b/external/subpack/net/nginx-util/src/LICENSE
new file mode 100644
index 0000000..bddd690
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/LICENSE
@@ -0,0 +1,23 @@
+/* Copyright 2020 Peter Stadler
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/external/subpack/net/nginx-util/src/nginx-ssl-util.hpp b/external/subpack/net/nginx-util/src/nginx-ssl-util.hpp
new file mode 100644
index 0000000..5a64b00
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/nginx-ssl-util.hpp
@@ -0,0 +1,1124 @@
+#ifndef __NGINX_SSL_UTIL_HPP
+#define __NGINX_SSL_UTIL_HPP
+
+#ifdef NO_PCRE
+#include <regex>
+namespace rgx = std;
+#else
+#include "regex-pcre.hpp"
+#endif
+
+#include "nginx-util.hpp"
+#include "px5g-openssl.hpp"
+
+#ifndef NO_UBUS
+static constexpr auto UBUS_TIMEOUT = 1000;
+#endif
+
+// once a year:
+static constexpr auto CRON_INTERVAL = std::string_view{"3 3 12 12 *"};
+
+static constexpr auto LAN_SSL_LISTEN = std::string_view{"/var/lib/nginx/lan_ssl.listen"};
+
+static constexpr auto LAN_SSL_LISTEN_DEFAULT = // TODO(pst) deprecate
+ std::string_view{"/var/lib/nginx/lan_ssl.listen.default"};
+
+static constexpr auto ADD_SSL_FCT = std::string_view{"add_ssl"};
+
+static constexpr auto SSL_SESSION_CACHE_ARG = [](const std::string_view & /*name*/) -> std::string {
+ return "shared:SSL:32k";
+};
+
+static constexpr auto SSL_SESSION_TIMEOUT_ARG = std::string_view{"64m"};
+
+using _Line = std::array<std::string (*)(const std::string&, const std::string&), 2>;
+
+class Line {
+ private:
+ _Line _line;
+
+ public:
+ explicit Line(const _Line& line) noexcept : _line{line} {}
+
+ template <const _Line&... xn>
+ static auto build() noexcept -> Line
+ {
+ return Line{_Line{[](const std::string& p, const std::string& b) -> std::string {
+ return (... + xn[0](p, b));
+ },
+ [](const std::string& p, const std::string& b) -> std::string {
+ return (... + xn[1](p, b));
+ }}};
+ }
+
+ [[nodiscard]] auto STR(const std::string& param, const std::string& begin) const -> std::string
+ {
+ return _line[0](param, begin);
+ }
+
+ [[nodiscard]] auto RGX() const -> rgx::regex
+ {
+ return rgx::regex{_line[1]("", "")};
+ }
+};
+
+auto get_if_missed(const std::string& conf,
+ const Line& LINE,
+ const std::string& val,
+ const std::string& indent = "\n ",
+ bool compare = true) -> std::string;
+
+auto replace_if(const std::string& conf,
+ const rgx::regex& rgx,
+ const std::string& val,
+ const std::string& insert) -> std::string;
+
+auto replace_listen(const std::string& conf, const std::array<const char*, 2>& ngx_port)
+ -> std::string;
+
+auto check_ssl_certificate(const std::string& crtpath, const std::string& keypath) -> bool;
+
+auto contains(const std::string& sentence, const std::string& word) -> bool;
+
+auto get_uci_section_for_name(const std::string& name) -> uci::section;
+
+void add_ssl_if_needed(const std::string& name);
+
+void add_ssl_if_needed(const std::string& name,
+ std::string_view manage,
+ std::string_view crt,
+ std::string_view key);
+
+void install_cron_job(const Line& CRON_LINE, const std::string& name = "");
+
+void remove_cron_job(const Line& CRON_LINE, const std::string& name = "");
+
+auto del_ssl_legacy(const std::string& name) -> bool;
+
+void del_ssl(const std::string& name);
+
+void del_ssl(const std::string& name, std::string_view manage);
+
+auto check_ssl(const uci::package& pkg, bool is_enabled) -> bool;
+
+inline void check_ssl(const uci::package& pkg)
+{
+ if (!check_ssl(pkg, is_enabled(pkg))) {
+#ifndef NO_UBUS
+ if (ubus::call("service", "list", UBUS_TIMEOUT).filter("nginx")) {
+ call("/etc/init.d/nginx", "reload");
+ std::cerr << "Reload Nginx.\n";
+ }
+#endif
+ }
+}
+
+constexpr auto _begin = _Line{
+ [](const std::string& /*param*/, const std::string& begin) -> std::string { return begin; },
+
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return R"([{;](?:\s*#[^\n]*(?=\n))*(\s*))";
+ }};
+
+constexpr auto _space = _Line{[](const std::string& /*param*/, const std::string &
+ /*begin*/) -> std::string { return std::string{" "}; },
+
+ [](const std::string& /*param*/, const std::string &
+ /*begin*/) -> std::string { return R"(\s+)"; }};
+
+constexpr auto _newline = _Line{
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return std::string{"\n"};
+ },
+
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return std::string{"(\n)"};
+ } // capture it as _end captures it, too.
+};
+
+constexpr auto _end =
+ _Line{[](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return std::string{";"};
+ },
+
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return std::string{R"(\s*(;(?:[\t ]*#[^\n]*)?))"};
+ }};
+
+template <char clim = '\0'>
+static constexpr auto _capture = _Line{
+ [](const std::string& param, const std::string & /*begin*/) -> std::string {
+ return '\'' + param + '\'';
+ },
+
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ const auto lim = clim == '\0' ? std::string{"\\s"} : std::string{clim};
+ return std::string{R"(((?:(?:"[^"]*")|(?:[^'")"} + lim + "][^" + lim + "]*)|(?:'[^']*'))+)";
+ }};
+
+template <const std::string_view& strptr, char clim = '\0'>
+static constexpr auto _escape = _Line{
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ return clim == '\0' ? std::string{strptr.data()} : clim + std::string{strptr.data()} + clim;
+ },
+
+ [](const std::string& /*param*/, const std::string & /*begin*/) -> std::string {
+ std::string ret{};
+ for (char c : strptr) {
+ switch (c) {
+ case '^':
+ ret += '\\';
+ ret += c;
+ break;
+ case '_':
+ case '-':
+ ret += c;
+ break;
+ default:
+ if ((isalpha(c) != 0) || (isdigit(c) != 0)) {
+ ret += c;
+ }
+ else {
+ ret += std::string{"["} + c + "]";
+ }
+ }
+ }
+ return "(?:" + ret + "|'" + ret + "'" + "|\"" + ret + "\"" + ")";
+ }};
+
+constexpr std::string_view _check_ssl = "check_ssl";
+
+constexpr std::string_view _server_name = "server_name";
+
+constexpr std::string_view _listen = "listen";
+
+constexpr std::string_view _include = "include";
+
+constexpr std::string_view _ssl_certificate = "ssl_certificate";
+
+constexpr std::string_view _ssl_certificate_key = "ssl_certificate_key";
+
+constexpr std::string_view _ssl_session_cache = "ssl_session_cache";
+
+constexpr std::string_view _ssl_session_timeout = "ssl_session_timeout";
+
+// For a compile time regex lib, this must be fixed, use one of these options:
+// * Hand craft or macro concat them (loosing more or less flexibility).
+// * Use Macro concatenation of __VA_ARGS__ with the help of:
+// https://p99.gforge.inria.fr/p99-html/group__preprocessor__for.html
+// * Use constexpr---not available for strings or char * for now---look at lib.
+
+static const auto CRON_CHECK =
+ Line::build<_space, _escape<NGINX_UTIL>, _space, _escape<_check_ssl, '\''>, _newline>();
+
+static const auto CRON_CMD = Line::build<_space,
+ _escape<NGINX_UTIL>,
+ _space,
+ _escape<ADD_SSL_FCT, '\''>,
+ _space,
+ _capture<>,
+ _newline>();
+
+static const auto NGX_SERVER_NAME =
+ Line::build<_begin, _escape<_server_name>, _space, _capture<';'>, _end>();
+
+static const auto NGX_INCLUDE_LAN_LISTEN =
+ Line::build<_begin, _escape<_include>, _space, _escape<LAN_LISTEN, '\''>, _end>();
+
+static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT =
+ Line::build<_begin, _escape<_include>, _space, _escape<LAN_LISTEN_DEFAULT, '\''>, _end>();
+
+static const auto NGX_INCLUDE_LAN_SSL_LISTEN =
+ Line::build<_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN, '\''>, _end>();
+
+static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT =
+ Line::build<_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end>();
+
+static const auto NGX_SSL_CRT =
+ Line::build<_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_KEY =
+ Line::build<_begin, _escape<_ssl_certificate_key>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_SESSION_CACHE =
+ Line::build<_begin, _escape<_ssl_session_cache>, _space, _capture<';'>, _end>();
+
+static const auto NGX_SSL_SESSION_TIMEOUT =
+ Line::build<_begin, _escape<_ssl_session_timeout>, _space, _capture<';'>, _end>();
+
+static const auto NGX_LISTEN = Line::build<_begin, _escape<_listen>, _space, _capture<';'>, _end>();
+
+static const auto NGX_PORT_80 = std::array<const char*, 2>{
+ R"(^\s*([^:]*:|\[[^\]]*\]:)?80(\s|$|;))",
+ "$01443 ssl$2",
+};
+
+static const auto NGX_PORT_443 = std::array<const char*, 2>{
+ R"(^\s*([^:]*:|\[[^\]]*\]:)?443(\s.*)?\sssl(\s|$|;))",
+ "$0180$2$3",
+};
+
+// ------------------------- implementation: ----------------------------------
+
+auto get_if_missed(const std::string& conf,
+ const Line& LINE,
+ const std::string& val,
+ const std::string& indent,
+ bool compare) -> std::string
+{
+ if (!compare || val.empty()) {
+ return rgx::regex_search(conf, LINE.RGX()) ? "" : LINE.STR(val, indent);
+ }
+
+ rgx::smatch match; // assuming last capture has the value!
+
+ for (auto pos = conf.begin(); rgx::regex_search(pos, conf.end(), match, LINE.RGX());
+ pos += match.position(0) + match.length(0))
+ {
+ const std::string value = match.str(match.size() - 2);
+
+ if (value == val || value == "'" + val + "'" || value == '"' + val + '"') {
+ return "";
+ }
+ }
+
+ return LINE.STR(val, indent);
+}
+
+auto replace_if(const std::string& conf,
+ const rgx::regex& rgx,
+ const std::string& val,
+ const std::string& insert) -> std::string
+{
+ std::string ret{};
+ auto pos = conf.begin();
+
+ auto skip = 0;
+ for (rgx::smatch match; rgx::regex_search(pos, conf.end(), match, rgx);
+ pos += match.position(match.size() - 1))
+ {
+ auto i = match.size() - 2;
+ const std::string value = match.str(i);
+
+ bool compare = !val.empty();
+ if (compare && value != val && value != "'" + val + "'" && value != '"' + val + '"') {
+ ret.append(pos + skip, pos + match.position(i) + match.length(i));
+ skip = 0;
+ }
+ else {
+ ret.append(pos + skip, pos + match.position(match.size() > 2 ? 1 : 0));
+ ret += insert;
+ skip = 1;
+ }
+ }
+
+ ret.append(pos + skip, conf.end());
+ return ret;
+}
+
+auto replace_listen(const std::string& conf, const std::array<const char*, 2>& ngx_port)
+ -> std::string
+{
+ std::string ret{};
+ auto pos = conf.begin();
+
+ for (rgx::smatch match; rgx::regex_search(pos, conf.end(), match, NGX_LISTEN.RGX());
+ pos += match.position(match.size() - 1))
+ {
+ auto i = match.size() - 2;
+ ret.append(pos, pos + match.position(i));
+ ret += rgx::regex_replace(match.str(i), rgx::regex{ngx_port[0]}, ngx_port[1]);
+ }
+
+ ret.append(pos, conf.end());
+ return ret;
+}
+
+inline void add_ssl_directives_to(const std::string& name)
+{
+ const std::string prefix = std::string{CONF_DIR} + name;
+
+ const std::string const_conf = read_file(prefix + ".conf");
+
+ rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
+ for (auto pos = const_conf.begin();
+ rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
+ pos += match.position(0) + match.length(0))
+ {
+ if (!contains(match.str(2), name)) {
+ continue;
+ } // else:
+
+ const std::string indent = match.str(1);
+
+ auto adds = std::string{};
+
+ adds += get_if_missed(const_conf, NGX_SSL_CRT, prefix + ".crt", indent);
+
+ adds += get_if_missed(const_conf, NGX_SSL_KEY, prefix + ".key", indent);
+
+ adds += get_if_missed(const_conf, NGX_SSL_SESSION_CACHE, SSL_SESSION_CACHE_ARG(name),
+ indent, false);
+
+ adds += get_if_missed(const_conf, NGX_SSL_SESSION_TIMEOUT,
+ std::string{SSL_SESSION_TIMEOUT_ARG}, indent, false);
+
+ pos += match.position(0) + match.length(0);
+ std::string conf =
+ std::string(const_conf.begin(), pos) + adds + std::string(pos, const_conf.end());
+
+ conf = replace_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX(), "",
+ NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.STR("", indent));
+
+ conf = replace_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX(), "",
+ NGX_INCLUDE_LAN_SSL_LISTEN.STR("", indent));
+
+ conf = replace_listen(conf, NGX_PORT_80);
+
+ if (conf != const_conf) {
+ write_file(prefix + ".conf", conf);
+ std::cerr << "Added SSL directives to " << prefix << ".conf\n";
+ }
+
+ return;
+ }
+
+ auto errmsg = std::string{"add_ssl_directives_to error: "};
+ errmsg += "cannot add SSL directives to " + name + ".conf, missing: ";
+ errmsg += NGX_SERVER_NAME.STR(name, "\n ") + "\n";
+ throw std::runtime_error(errmsg);
+}
+
+template <typename T>
+inline auto num2hex(T bytes) -> std::array<char, 2 * sizeof(bytes) + 1>
+{
+ constexpr auto n = 2 * sizeof(bytes);
+ std::array<char, n + 1> str{};
+
+ for (size_t i = 0; i < n; ++i) {
+ static const std::array<char, 17> hex{"0123456789ABCDEF"};
+ static constexpr auto get = 0x0fU;
+ str.at(i) = hex.at(bytes & get);
+
+ static constexpr auto move = 4U;
+ bytes >>= move;
+ }
+
+ str[n] = '\0';
+ return str;
+}
+
+template <typename T>
+inline auto get_nonce(const T salt = 0) -> T
+{
+ T nonce = 0;
+
+ std::ifstream urandom{"/dev/urandom"};
+
+ static constexpr auto move = 6U;
+
+ constexpr size_t steps = (sizeof(nonce) * 8 - 1) / move + 1;
+
+ for (size_t i = 0; i < steps; ++i) {
+ if (!urandom.good()) {
+ throw std::runtime_error("get_nonce error");
+ }
+ nonce = (nonce << move) + static_cast<unsigned>(urandom.get());
+ }
+
+ nonce ^= salt;
+
+ return nonce;
+}
+
+inline void create_ssl_certificate(const std::string& crtpath,
+ const std::string& keypath,
+ const int days = 792)
+{
+ size_t nonce = 0;
+
+ try {
+ nonce = get_nonce(nonce);
+ }
+
+ catch (...) { // the address of a variable should be random enough:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) sic:
+ nonce += reinterpret_cast<size_t>(&crtpath);
+ }
+
+ auto noncestr = num2hex(nonce);
+
+ const auto tmpcrtpath = crtpath + ".new-" + noncestr.data();
+ const auto tmpkeypath = keypath + ".new-" + noncestr.data();
+
+ try {
+ auto pkey = gen_eckey(NID_secp384r1);
+
+ write_key(pkey, tmpkeypath);
+
+ std::string subject{"/C=ZZ/ST=Somewhere/L=None/CN=OpenWrt/O=OpenWrt"};
+ subject += noncestr.data();
+
+ selfsigned(pkey, days, subject, tmpcrtpath);
+
+ static constexpr auto to_seconds = 24 * 60 * 60;
+ static constexpr auto leeway = 42;
+ if (!checkend(tmpcrtpath, days * to_seconds - leeway)) {
+ throw std::runtime_error("bug: created certificate is not valid!!");
+ }
+ }
+ catch (...) {
+ std::cerr << "create_ssl_certificate error: ";
+ std::cerr << "cannot create selfsigned certificate, ";
+ std::cerr << "removing temporary files ..." << std::endl;
+
+ if (remove(tmpcrtpath.c_str()) != 0) {
+ auto errmsg = "\t cannot remove " + tmpcrtpath;
+ perror(errmsg.c_str());
+ }
+
+ if (remove(tmpkeypath.c_str()) != 0) {
+ auto errmsg = "\t cannot remove " + tmpkeypath;
+ perror(errmsg.c_str());
+ }
+
+ throw;
+ }
+
+ if (rename(tmpcrtpath.c_str(), crtpath.c_str()) != 0 ||
+ rename(tmpkeypath.c_str(), keypath.c_str()) != 0)
+ {
+ auto errmsg = std::string{"create_ssl_certificate warning: "};
+ errmsg += "cannot move " + tmpcrtpath + " to " + crtpath;
+ errmsg += " or " + tmpkeypath + " to " + keypath + ", continuing ... ";
+ perror(errmsg.c_str());
+ }
+
+ std::cerr << "Created self-signed SSL certificate '" << crtpath;
+ std::cerr << "' with key '" << keypath << "'.\n";
+}
+
+auto check_ssl_certificate(const std::string& crtpath, const std::string& keypath) -> bool
+{
+ { // paths are relative to dir:
+ auto dir = std::string_view{"/etc/nginx"};
+ auto crt_rel = crtpath[0] != '/';
+ auto key_rel = keypath[0] != '/';
+ if ((crt_rel || key_rel) && (chdir(dir.data()) != 0)) {
+ auto errmsg = std::string{"check_ssl_certificate error: entering "};
+ errmsg += dir;
+ perror(errmsg.c_str());
+ errmsg += " (need to change directory since the given ";
+ errmsg += crt_rel ? "ssl_certificate '" + crtpath : std::string{};
+ errmsg += crt_rel && key_rel ? "' and " : "";
+ errmsg += key_rel ? "ssl_certificate_key '" + keypath : std::string{};
+ errmsg += crt_rel && key_rel ? "' are" : "' is a";
+ errmsg += " relative path";
+ errmsg += crt_rel && key_rel ? "s)" : ")";
+ throw std::runtime_error(errmsg);
+ }
+ }
+
+ constexpr auto remaining_seconds = (365 + 32) * 24 * 60 * 60;
+ constexpr auto validity_days = 3 * (365 + 31);
+
+ bool is_valid = true;
+
+ if (access(keypath.c_str(), R_OK) != 0 || access(crtpath.c_str(), R_OK) != 0) {
+ is_valid = false;
+ }
+
+ else {
+ try {
+ if (!checkend(crtpath, remaining_seconds)) {
+ is_valid = false;
+ }
+ }
+ catch (...) { // something went wrong, maybe it is in DER format:
+ try {
+ if (!checkend(crtpath, remaining_seconds, false)) {
+ is_valid = false;
+ }
+ }
+ catch (...) { // it has neither DER nor PEM format, rebuild.
+ is_valid = false;
+ }
+ }
+ }
+
+ if (!is_valid) {
+ create_ssl_certificate(crtpath, keypath, validity_days);
+ }
+
+ return is_valid;
+}
+
+auto contains(const std::string& sentence, const std::string& word) -> bool
+{
+ auto pos = sentence.find(word);
+ if (pos == std::string::npos) {
+ return false;
+ }
+ if (pos != 0 && (isgraph(sentence[pos - 1]) != 0)) {
+ return false;
+ }
+ if (isgraph(sentence[pos + word.size()]) != 0) {
+ return false;
+ }
+ // else:
+ return true;
+}
+
+auto get_uci_section_for_name(const std::string& name) -> uci::section
+{
+ auto pkg = uci::package{"nginx"}; // let it throw.
+
+ auto uci_enabled = is_enabled(pkg);
+
+ if (uci_enabled) {
+ for (auto sec : pkg) {
+ if (sec.name() == name) {
+ return sec;
+ }
+ }
+ // try interpreting 'name' as FQDN:
+ for (auto sec : pkg) {
+ for (auto opt : sec) {
+ if (opt.name() == "server_name") {
+ for (auto itm : opt) {
+ if (contains(itm.name(), name)) {
+ return sec;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ auto errmsg = std::string{"lookup error: neither there is a file named '"};
+ errmsg += std::string{CONF_DIR} + name + ".conf' nor the UCI config has ";
+ if (uci_enabled) {
+ errmsg += "a nginx server with section name or 'server_name': " + name;
+ }
+ else {
+ errmsg += "been enabled by:\n\tuci set nginx.global.uci_enable=true";
+ }
+ throw std::runtime_error(errmsg);
+}
+
+inline auto add_ssl_to_config(const std::string& name,
+ const std::string_view manage = "self-signed",
+ const std::string_view crt = "",
+ const std::string_view key = "")
+{
+ auto sec = get_uci_section_for_name(name); // let it throw.
+ auto secname = sec.name();
+
+ struct {
+ std::string crt;
+ std::string key;
+ } ret;
+
+ std::cerr << "Adding SSL directives to UCI server: nginx." << secname << "\n";
+
+ std::cerr << "\t" << MANAGE_SSL << "='" << manage << "'\n";
+ sec.set(MANAGE_SSL.data(), manage.data());
+
+ if (!crt.empty() && !key.empty()) {
+ sec.set("ssl_certificate", crt.data());
+ std::cerr << "\tssl_certificate='" << crt << "'\n";
+ sec.set("ssl_certificate_key", key.data());
+ std::cerr << "\tssl_certificate_key='" << key << "'\n";
+ }
+
+ auto cache = false;
+ auto timeout = false;
+ for (auto opt : sec) {
+ if (opt.name() == "ssl_session_cache") {
+ cache = true;
+ continue;
+ } // else:
+
+ if (opt.name() == "ssl_session_timeout") {
+ timeout = true;
+ continue;
+ }
+
+ // else:
+ for (auto itm : opt) {
+ if (opt.name() == "ssl_certificate_key") {
+ ret.key = itm.name();
+ }
+
+ else if (opt.name() == "ssl_certificate") {
+ ret.crt = itm.name();
+ }
+
+ else if (opt.name() == "listen") {
+ auto val = regex_replace(itm.name(), rgx::regex{NGX_PORT_80[0]}, NGX_PORT_80[1]);
+ if (val != itm.name()) {
+ std::cerr << "\t" << opt.name() << "='" << val << "' (replacing)\n";
+ itm.rename(val.c_str());
+ }
+ }
+ }
+ }
+
+ if (ret.crt.empty()) {
+ ret.crt = std::string{CONF_DIR} + name + ".crt";
+ std::cerr << "\tssl_certificate='" << ret.crt << "'\n";
+ sec.set("ssl_certificate", ret.crt.c_str());
+ }
+
+ if (ret.key.empty()) {
+ ret.key = std::string{CONF_DIR} + name + ".key";
+ std::cerr << "\tssl_certificate_key='" << ret.key << "'\n";
+ sec.set("ssl_certificate_key", ret.key.c_str());
+ }
+
+ if (!cache) {
+ std::cerr << "\tssl_session_cache='" << SSL_SESSION_CACHE_ARG(name) << "'\n";
+ sec.set("ssl_session_cache", SSL_SESSION_CACHE_ARG(name).data());
+ }
+
+ if (!timeout) {
+ std::cerr << "\tssl_session_timeout='" << SSL_SESSION_TIMEOUT_ARG << "'\n";
+ sec.set("ssl_session_timeout", SSL_SESSION_TIMEOUT_ARG.data());
+ }
+
+ sec.commit();
+
+ return ret;
+}
+
+void install_cron_job(const Line& CRON_LINE, const std::string& name)
+{
+ static const char* filename = "/etc/crontabs/root";
+
+ std::string conf{};
+ try {
+ conf = read_file(filename);
+ }
+ catch (const std::ifstream::failure&) { /* is ok if not found, create. */
+ }
+
+ const std::string add = get_if_missed(conf, CRON_LINE, name);
+
+ if (add.length() > 0) {
+#ifndef NO_UBUS
+ if (!ubus::call("service", "list", UBUS_TIMEOUT).filter("cron")) {
+ std::string errmsg{"install_cron_job error: "};
+ errmsg += "Cron unavailable to re-create the ssl certificate";
+ errmsg += (name.empty() ? std::string{"s\n"} : " for '" + name + "'\n");
+ throw std::runtime_error(errmsg);
+ } // else active with or without instances:
+#endif
+
+ const auto* pre = (conf.length() == 0 || conf.back() == '\n' ? "" : "\n");
+ write_file(filename, pre + std::string{CRON_INTERVAL} + add, std::ios::app);
+
+#ifndef NO_UBUS
+ call("/etc/init.d/cron", "reload");
+#endif
+
+ std::cerr << "Rebuild the self-signed SSL certificate";
+ std::cerr << (name.empty() ? std::string{"s"} : " for '" + name + "'");
+ std::cerr << " annually with cron." << std::endl;
+ }
+}
+
+void add_ssl_if_needed(const std::string& name)
+{
+ const auto legacypath = std::string{CONF_DIR} + name + ".conf";
+ if (access(legacypath.c_str(), R_OK) == 0) {
+ add_ssl_directives_to(name); // let it throw.
+
+ const auto crtpath = std::string{CONF_DIR} + name + ".crt";
+ const auto keypath = std::string{CONF_DIR} + name + ".key";
+ check_ssl_certificate(crtpath, keypath); // let it throw.
+
+ try {
+ install_cron_job(CRON_CMD, name);
+ }
+ catch (...) {
+ std::cerr << "add_ssl_if_needed warning: cannot use cron to rebuild ";
+ std::cerr << "the self-signed SSL certificate for " << name << "\n";
+ }
+ return;
+ } // else:
+
+ auto paths = add_ssl_to_config(name); // let it throw.
+
+ check_ssl_certificate(paths.crt, paths.key); // let it throw.
+
+ try {
+ install_cron_job(CRON_CHECK);
+ }
+ catch (...) {
+ std::cerr << "add_ssl_if_needed warning: cannot use cron to rebuild ";
+ std::cerr << "the self-signed SSL certificates.\n";
+ }
+}
+
+void add_ssl_if_needed(const std::string& name,
+ const std::string_view manage,
+ const std::string_view crt,
+ const std::string_view key)
+{
+ if (crt[0] != '/') {
+ auto errmsg = std::string{"add_ssl_if_needed error: ssl_certificate "};
+ errmsg += "path cannot be relative '" + std::string{crt} + "'";
+ throw std::runtime_error(errmsg);
+ }
+
+ if (key[0] != '/') {
+ auto errmsg = std::string{"add_ssl_if_needed error: path to ssl_key "};
+ errmsg += "cannot be relative '" + std::string{key} + "'";
+ throw std::runtime_error(errmsg);
+ }
+
+ const auto legacypath = std::string{CONF_DIR} + name + ".conf";
+
+ if (access(legacypath.c_str(), R_OK) != 0) {
+ add_ssl_to_config(name, manage, crt, key); // let it throw.
+ return;
+ } // else:
+
+ // symlink crt+key to the paths that add_ssl_directives_to uses (if needed):
+
+ auto crtpath = std::string{CONF_DIR} + name + ".crt";
+ if (crtpath != crt && /* then */ symlink(crt.data(), crtpath.c_str()) != 0) {
+ auto errmsg = std::string{"add_ssl_if_needed error: cannot link "};
+ errmsg += "ssl_certificate " + crtpath + " -> " + crt.data() + " (";
+ errmsg += std::to_string(errno) + "): " + std::strerror(errno);
+ throw std::runtime_error(errmsg);
+ }
+
+ auto keypath = std::string{CONF_DIR} + name + ".key";
+ if (keypath != key && /* then */ symlink(key.data(), keypath.c_str()) != 0) {
+ auto errmsg = std::string{"add_ssl_if_needed error: cannot link "};
+ errmsg += "ssl_certificate_key " + keypath + " -> " + key.data() + " (";
+ errmsg += std::to_string(errno) + "): " + std::strerror(errno);
+ throw std::runtime_error(errmsg);
+ }
+
+ add_ssl_directives_to(name); // let it throw.
+}
+
+void remove_cron_job(const Line& CRON_LINE, const std::string& name)
+{
+ static const char* filename = "/etc/crontabs/root";
+
+ const auto const_conf = read_file(filename);
+
+ bool changed = false;
+ auto conf = std::string{};
+
+ size_t prev = 0;
+ size_t curr = 0;
+ while ((curr = const_conf.find('\n', prev)) != std::string::npos) {
+ auto line = const_conf.substr(prev, curr - prev + 1);
+
+ if (line == replace_if(line, CRON_LINE.RGX(), name, "")) {
+ conf += line;
+ }
+ else {
+ changed = true;
+ }
+
+ prev = curr + 1;
+ }
+
+ if (changed) {
+ write_file(filename, conf);
+
+ std::cerr << "Do not rebuild the self-signed SSL certificate";
+ std::cerr << (name.empty() ? std::string{"s"} : " for '" + name + "'");
+ std::cerr << " annually with cron anymore." << std::endl;
+
+#ifndef NO_UBUS
+ if (ubus::call("service", "list", UBUS_TIMEOUT).filter("cron")) {
+ call("/etc/init.d/cron", "reload");
+ }
+#endif
+ }
+}
+
+inline void del_ssl_directives_from(const std::string& name)
+{
+ const std::string prefix = std::string{CONF_DIR} + name;
+
+ const std::string const_conf = read_file(prefix + ".conf");
+
+ rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
+ for (auto pos = const_conf.begin();
+ rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
+ pos += match.position(0) + match.length(0))
+ {
+ if (!contains(match.str(2), name)) {
+ continue;
+ } // else:
+
+ const std::string indent = match.str(1);
+
+ std::string conf = const_conf;
+
+ conf = replace_listen(conf, NGX_PORT_443);
+
+ conf = replace_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX(), "",
+ NGX_INCLUDE_LAN_LISTEN_DEFAULT.STR("", indent));
+
+ conf = replace_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX(), "",
+ NGX_INCLUDE_LAN_LISTEN.STR("", indent));
+
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation) prefix:
+ conf = replace_if(conf, NGX_SSL_CRT.RGX(), prefix + ".crt", "");
+
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation) prefix:
+ conf = replace_if(conf, NGX_SSL_KEY.RGX(), prefix + ".key", "");
+
+ conf = replace_if(conf, NGX_SSL_SESSION_CACHE.RGX(), "", "");
+
+ conf = replace_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX(), "", "");
+
+ if (conf != const_conf) {
+ write_file(prefix + ".conf", conf);
+ std::cerr << "Deleted SSL directives from " << prefix << ".conf\n";
+ }
+
+ return;
+ }
+
+ auto errmsg = std::string{"del_ssl_directives_from error: "};
+ errmsg += "cannot delete SSL directives from " + name + ".conf, missing: ";
+ errmsg += NGX_SERVER_NAME.STR(name, "\n ") + "\n";
+ throw std::runtime_error(errmsg);
+}
+
+inline auto del_ssl_from_config(const std::string& name,
+ const std::string_view manage = "self-signed")
+{
+ auto sec = get_uci_section_for_name(name); // let it throw.
+ auto secname = sec.name();
+
+ struct {
+ std::string crt;
+ std::string key;
+ } ret;
+
+ std::cerr << "Deleting SSL directives from UCI server: nginx." << secname << "\n";
+
+ auto manage_match = false;
+ for (auto opt : sec) {
+ for (auto itm : opt) {
+ if (opt.name() == "ssl_certificate_key") {
+ ret.key = itm.name();
+ }
+
+ else if (opt.name() == "ssl_certificate") {
+ ret.crt = itm.name();
+ }
+
+ else if (opt.name() == "ssl_session_cache" || opt.name() == "ssl_session_timeout") {
+ }
+
+ else if (opt.name() == MANAGE_SSL && itm.name() == manage) {
+ manage_match = true;
+ }
+
+ else if (opt.name() == "listen") {
+ auto val = regex_replace(itm.name(), rgx::regex{NGX_PORT_443[0]}, NGX_PORT_443[1]);
+ if (val != itm.name()) {
+ std::cerr << "\t" << opt.name() << " (set back to '" << val << "')\n";
+ itm.rename(val.c_str());
+ }
+ continue; /* not deleting opt, look at other itm : opt */
+ }
+
+ else {
+ continue; /* not deleting opt, look at other itm : opt */
+ }
+
+ // Delete matching opt (not skipped by continue):
+ std::cerr << "\t" << opt.name() << " (was '" << itm.name() << "')\n";
+ opt.del();
+ break;
+ }
+ }
+ if (manage_match) {
+ sec.commit();
+ return ret;
+ } // else:
+
+ auto errmsg = std::string{"del_ssl error: not changing config wihtout: "};
+ errmsg += "uci set nginx." + secname + "." + MANAGE_SSL.data() + "='" + manage.data();
+ errmsg += "'";
+ throw std::runtime_error(errmsg);
+}
+
+auto del_ssl_legacy(const std::string& name) -> bool
+{
+ const auto legacypath = std::string{CONF_DIR} + name + ".conf";
+
+ if (access(legacypath.c_str(), R_OK) != 0) {
+ return false;
+ }
+
+ try {
+ remove_cron_job(CRON_CMD, name);
+ }
+ catch (...) {
+ std::cerr << "del_ssl warning: cannot remove cron job rebuilding ";
+ std::cerr << "the self-signed SSL certificate for " << name << "\n";
+ }
+
+ try {
+ del_ssl_directives_from(name);
+ }
+ catch (...) {
+ std::cerr << "del_ssl error: ";
+ std::cerr << "cannot delete SSL directives from " << name << ".conf\n";
+ throw;
+ }
+
+ return true;
+}
+
+void del_ssl(const std::string& name)
+{
+ auto crtpath = std::string{};
+ auto keypath = std::string{};
+
+ if (del_ssl_legacy(name)) { // let it throw.
+ crtpath = std::string{CONF_DIR} + name + ".crt";
+ keypath = std::string{CONF_DIR} + name + ".key";
+ }
+
+ else {
+ auto paths = del_ssl_from_config(name); // let it throw.
+ crtpath = paths.crt;
+ keypath = paths.key;
+ }
+
+ if (remove(crtpath.c_str()) != 0) {
+ auto errmsg = "del_ssl warning: cannot remove " + crtpath;
+ perror(errmsg.c_str());
+ }
+
+ if (remove(keypath.c_str()) != 0) {
+ auto errmsg = "del_ssl warning: cannot remove " + keypath;
+ perror(errmsg.c_str());
+ }
+}
+
+void del_ssl(const std::string& name, const std::string_view manage)
+{
+ const auto legacypath = std::string{CONF_DIR} + name + ".conf";
+
+ if (access(legacypath.c_str(), R_OK) != 0) {
+ del_ssl_from_config(name, manage); // let it throw.
+ return;
+ } // else:
+
+ del_ssl_directives_from(name); // let it throw.
+
+ for (const auto* ext : {".crt", ".key"}) {
+ struct stat sb {};
+
+ auto path = std::string{CONF_DIR} + name + ext;
+
+ // managed version of add_ssl_if_needed created symlinks (if needed):
+ // NOLINTNEXTLINE(hicpp-signed-bitwise) S_ISLNK macro:
+ if (lstat(path.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) {
+ if (remove(path.c_str()) != 0) {
+ auto errmsg = "del_ssl warning: cannot remove " + path;
+ perror(errmsg.c_str());
+ }
+ }
+ }
+}
+
+auto check_ssl(const uci::package& pkg, bool is_enabled) -> bool
+{
+ auto are_valid = true;
+ auto is_enabled_and_at_least_one_has_manage_ssl = false;
+
+ if (is_enabled) {
+ for (auto sec : pkg) {
+ if (sec.anonymous() || sec.type() != "server") {
+ continue;
+ } // else:
+
+ const auto legacypath = std::string{CONF_DIR} + sec.name() + ".conf";
+ if (access(legacypath.c_str(), R_OK) == 0) {
+ continue;
+ } // else:
+
+ auto keypath = std::string{};
+ auto crtpath = std::string{};
+ auto self_signed = false;
+
+ for (auto opt : sec) {
+ for (auto itm : opt) {
+ if (opt.name() == "ssl_certificate_key") {
+ keypath = itm.name();
+ }
+
+ else if (opt.name() == "ssl_certificate") {
+ crtpath = itm.name();
+ }
+
+ else if (opt.name() == MANAGE_SSL) {
+ if (itm.name() == "self-signed") {
+ self_signed = true;
+ }
+
+ // else if (itm.name()=="???") { /* manage other */ }
+
+ else {
+ continue;
+ } // no supported manage_ssl string.
+
+ is_enabled_and_at_least_one_has_manage_ssl = true;
+ }
+ }
+ }
+
+ if (self_signed && !crtpath.empty() && !keypath.empty()) {
+ try {
+ if (!check_ssl_certificate(crtpath, keypath)) {
+ are_valid = false;
+ }
+ }
+ catch (...) {
+ std::cerr << "check_ssl warning: cannot build certificate '";
+ std::cerr << crtpath << "' or key '" << keypath << "'.\n";
+ }
+ }
+ }
+ }
+
+ auto suffix = std::string_view{" the cron job checking the managed SSL certificates.\n"};
+
+ if (is_enabled_and_at_least_one_has_manage_ssl) {
+ try {
+ install_cron_job(CRON_CHECK);
+ }
+ catch (...) {
+ std::cerr << "check_ssl warning: cannot install" << suffix;
+ }
+ }
+
+ else if (access("/etc/crontabs/root", R_OK) == 0) {
+ try {
+ remove_cron_job(CRON_CHECK);
+ }
+ catch (...) {
+ std::cerr << "check_ssl warning: cannot remove" << suffix;
+ }
+ } // else: do nothing
+
+ return are_valid;
+}
+
+#endif
diff --git a/external/subpack/net/nginx-util/src/nginx-util.cpp b/external/subpack/net/nginx-util/src/nginx-util.cpp
new file mode 100644
index 0000000..3b4ad8c
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/nginx-util.cpp
@@ -0,0 +1,378 @@
+#include <iostream>
+#include <numeric>
+
+#include "nginx-ssl-util.hpp"
+#include "nginx-util.hpp"
+
+static auto constexpr file_comment_auto_created =
+ std::string_view{"# This file is re-created when Nginx starts.\n"};
+
+// TODO(pst) replace it with blobmsg_get_string if upstream takes const:
+#ifndef NO_UBUS
+static inline auto _pst_get_string(const blob_attr* attr) -> char*
+{
+ return static_cast<char*>(blobmsg_data(attr));
+}
+#endif
+
+void create_lan_listen() // create empty files for compatibility:
+{
+ // TODO(pst): replace by dummies after transitioning nginx config to UCI:
+ std::vector<std::string> ips;
+
+#ifndef NO_UBUS
+ try {
+ auto loopback_status = ubus::call("network.interface.loopback", "status");
+
+ for (const auto* ip : loopback_status.filter("ipv4-address", "", "address")) {
+ ips.emplace_back(_pst_get_string(ip));
+ }
+
+ for (const auto* ip : loopback_status.filter("ipv6-address", "", "address")) {
+ ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]");
+ }
+ }
+ catch (const std::runtime_error&) { /* do nothing about it */
+ }
+
+ try {
+ auto lan_status = ubus::call("network.interface.lan", "status");
+
+ for (const auto* ip : lan_status.filter("ipv4-address", "", "address")) {
+ ips.emplace_back(_pst_get_string(ip));
+ }
+
+ for (const auto* ip : lan_status.filter("ipv6-address", "", "address")) {
+ ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]");
+ }
+
+ for (const auto* ip :
+ lan_status.filter("ipv6-prefix-assignment", "", "local-address", "address")) {
+ ips.emplace_back(std::string{"["} + _pst_get_string(ip) + "]");
+ }
+ }
+ catch (const std::runtime_error&) { /* do nothing about it */
+ }
+#else
+ ips.emplace_back("127.0.0.1");
+#endif
+
+ std::string listen = std::string{file_comment_auto_created};
+ std::string listen_default = std::string{file_comment_auto_created};
+ for (const auto& ip : ips) {
+ listen += "\tlisten " + ip + ":80;\n";
+ listen_default += "\tlisten " + ip + ":80 default_server;\n";
+ }
+ write_file(LAN_LISTEN, listen);
+ write_file(LAN_LISTEN_DEFAULT, listen_default);
+
+ std::string ssl_listen = std::string{file_comment_auto_created};
+ std::string ssl_listen_default = std::string{file_comment_auto_created};
+ for (const auto& ip : ips) {
+ ssl_listen += "\tlisten " + ip + ":443 ssl;\n";
+ ssl_listen_default += "\tlisten " + ip + ":443 ssl default_server;\n";
+ }
+ write_file(LAN_SSL_LISTEN, ssl_listen);
+ write_file(LAN_SSL_LISTEN_DEFAULT, ssl_listen_default);
+}
+
+inline auto change_if_starts_with(const std::string_view& subject,
+ const std::string_view& prefix,
+ const std::string_view& substitute,
+ const std::string_view& seperator = " \t\n;") -> std::string
+{
+ auto view = subject;
+ view = view.substr(view.find_first_not_of(seperator));
+ if (view.rfind(prefix, 0) == 0) {
+ if (view.size() == prefix.size()) {
+ return std::string{substitute};
+ }
+ view = view.substr(prefix.size());
+ if (seperator.find(view[0]) != std::string::npos) {
+ auto ret = std::string{substitute};
+ ret += view;
+ return ret;
+ }
+ }
+ return std::string{subject};
+}
+
+inline auto create_server_conf(const uci::section& sec, const std::string& indent = "")
+ -> std::string
+{
+ auto secname = sec.name();
+
+ auto legacypath = std::string{CONF_DIR} + secname + ".conf";
+ if (access(legacypath.c_str(), R_OK) == 0) {
+ auto message = std::string{"skipped UCI server 'nginx."} + secname;
+ message += "' as it could conflict with: " + legacypath + "\n";
+
+ // TODO(pst) std::cerr<<"create_server_conf notice: "<<message;
+
+ return indent + "# " + message;
+ } // else:
+
+ auto conf = indent + "server { #see uci show 'nginx." + secname + "'\n";
+
+ for (auto opt : sec) {
+ for (auto itm : opt) {
+ if (opt.name().rfind("uci_", 0) == 0) {
+ continue;
+ }
+ // else: standard opt.name()
+
+ auto val = itm.name();
+
+ if (opt.name() == "error_log") {
+ val = change_if_starts_with(val, "logd", "/proc/self/fd/1");
+ }
+
+ else if (opt.name() == "access_log") {
+ val = change_if_starts_with(val, "logd", "stderr");
+ }
+
+ conf += indent + "\t" + opt.name() + " " + itm.name() + ";\n";
+ }
+ }
+
+ conf += indent + "}\n";
+
+ return conf;
+}
+
+void init_uci(const uci::package& pkg)
+{
+ auto conf = std::string{file_comment_auto_created};
+
+ static const auto uci_http_config = std::string_view{"#UCI_HTTP_CONFIG\n"};
+
+ const auto tmpl = read_file(std::string{UCI_CONF} + ".template");
+ auto pos = tmpl.find(uci_http_config);
+
+ if (pos == std::string::npos) {
+ conf += tmpl;
+ }
+
+ else {
+ const auto index = tmpl.find_last_not_of(" \t", pos - 1);
+
+ const auto before = tmpl.begin() + index + 1;
+ const auto middle = tmpl.begin() + pos;
+ const auto after = middle + uci_http_config.length();
+
+ conf.append(tmpl.begin(), before);
+
+ const auto indent = std::string{before, middle};
+ for (auto sec : pkg) {
+ if (sec.type() == std::string_view{"server"}) {
+ conf += create_server_conf(sec, indent) + "\n";
+ }
+ }
+
+ conf.append(after, tmpl.end());
+ }
+
+ write_file(VAR_UCI_CONF, conf);
+}
+
+auto is_enabled(const uci::package& pkg) -> bool
+{
+ for (auto sec : pkg) {
+ if (sec.type() != std::string_view{"main"}) {
+ continue;
+ }
+ if (sec.name() != std::string_view{"global"}) {
+ continue;
+ }
+ for (auto opt : sec) {
+ if (opt.name() != "uci_enable") {
+ continue;
+ }
+ for (auto itm : opt) {
+ if (itm) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * ___________main_thread________________|______________thread_1________________
+ * create_lan_listen() or do nothing | config = uci::package("nginx")
+ * if config_enabled (set in thread_1): | config_enabled = is_enabled(config)
+ * then init_uci(config) | check_ssl(config, config_enabled)
+ */
+void init_lan()
+{
+ std::exception_ptr ex;
+ std::unique_ptr<uci::package> config;
+ bool config_enabled = false;
+ std::mutex configuring;
+
+ configuring.lock();
+ auto thrd = std::thread([&config, &config_enabled, &configuring, &ex] {
+ try {
+ config = std::make_unique<uci::package>("nginx");
+ config_enabled = is_enabled(*config);
+ configuring.unlock();
+ check_ssl(*config, config_enabled);
+ }
+ catch (...) {
+ std::cerr << "init_lan error: checking UCI file /etc/config/nginx\n";
+ ex = std::current_exception();
+ }
+ });
+
+ try {
+ create_lan_listen();
+ }
+ catch (...) {
+ std::cerr << "init_lan error: cannot create listen files of local IPs.\n";
+ ex = std::current_exception();
+ }
+
+ configuring.lock();
+ if (config_enabled) {
+ try {
+ init_uci(*config);
+ }
+ catch (...) {
+ std::cerr << "init_lan error: cannot create " << VAR_UCI_CONF << " from ";
+ std::cerr << UCI_CONF << ".template using UCI file /etc/config/nginx\n";
+ ex = std::current_exception();
+ }
+ }
+
+ thrd.join();
+ if (ex) {
+ std::rethrow_exception(ex);
+ }
+}
+
+void get_env()
+{
+ std::cout << "UCI_CONF="
+ << "'" << UCI_CONF << "'" << std::endl;
+ std::cout << "NGINX_CONF="
+ << "'" << NGINX_CONF << "'" << std::endl;
+ std::cout << "CONF_DIR="
+ << "'" << CONF_DIR << "'" << std::endl;
+ std::cout << "LAN_NAME="
+ << "'" << LAN_NAME << "'" << std::endl;
+ std::cout << "LAN_LISTEN="
+ << "'" << LAN_LISTEN << "'" << std::endl;
+ std::cout << "LAN_SSL_LISTEN="
+ << "'" << LAN_SSL_LISTEN << "'" << std::endl;
+ std::cout << "SSL_SESSION_CACHE_ARG="
+ << "'" << SSL_SESSION_CACHE_ARG(LAN_NAME) << "'" << std::endl;
+ std::cout << "SSL_SESSION_TIMEOUT_ARG="
+ << "'" << SSL_SESSION_TIMEOUT_ARG << "'\n";
+ std::cout << "ADD_SSL_FCT="
+ << "'" << ADD_SSL_FCT << "'" << std::endl;
+ std::cout << "MANAGE_SSL="
+ << "'" << MANAGE_SSL << "'" << std::endl;
+}
+
+auto main(int argc, char* argv[]) -> int
+{
+ // TODO(pst): use std::span when available:
+ auto args = std::basic_string_view<char*>{argv, static_cast<size_t>(argc)};
+
+ auto cmds = std::array{
+ std::array<std::string_view, 2>{"init_lan", ""},
+ std::array<std::string_view, 2>{"get_env", ""},
+ std::array<std::string_view, 2>{
+ ADD_SSL_FCT, "server_name [manager /path/to/ssl_certificate /path/to/ssl_key]"},
+ std::array<std::string_view, 2>{"del_ssl", "server_name [manager]"},
+ std::array<std::string_view, 2>{"check_ssl", ""},
+ };
+
+ try {
+ if (argc == 2 && args[1] == cmds[0][0]) {
+ init_lan();
+ }
+
+ else if (argc == 2 && args[1] == cmds[1][0]) {
+ get_env();
+ }
+
+ else if (argc == 3 && args[1] == cmds[2][0]) {
+ add_ssl_if_needed(std::string{args[2]});
+ }
+
+ // NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 6
+ else if (argc == 6 && args[1] == cmds[2][0]) {
+ // NOLINTNEXTLINE(readability-magic-numbers,cppcoreguidelines-avoid-magic-numbers): 5
+ add_ssl_if_needed(std::string{args[2]}, args[3], args[4], args[5]);
+ }
+
+ else if (argc == 3 && args[1] == cmds[3][0]) {
+ del_ssl(std::string{args[2]});
+ }
+
+ else if (argc == 4 && args[1] == cmds[3][0]) {
+ del_ssl(std::string{args[2]}, args[3]);
+ }
+
+ else if (argc == 2 && args[1] == cmds[3][0]) // TODO(pst) deprecate
+ {
+ try {
+ auto name = std::string{LAN_NAME};
+ if (del_ssl_legacy(name)) {
+ auto crtpath = std::string{CONF_DIR} + name + ".crt";
+ remove(crtpath.c_str());
+ auto keypath = std::string{CONF_DIR} + name + ".key";
+ remove(keypath.c_str());
+ }
+ }
+ catch (...) { /* do nothing. */
+ }
+ }
+
+ else if (argc == 2 && args[1] == cmds[4][0]) {
+ check_ssl(uci::package{"nginx"});
+ }
+
+ else {
+ std::cerr << "Tool for creating Nginx configuration files (";
+#ifdef VERSION
+ std::cerr << "version " << VERSION << " ";
+#endif
+ std::cerr << "with libuci, ";
+#ifndef NO_UBUS
+ std::cerr << "libubus, ";
+#endif
+ std::cerr << "libopenssl, ";
+#ifndef NO_PCRE
+ std::cerr << "PCRE, ";
+#endif
+ std::cerr << "pthread and libstdcpp)." << std::endl;
+
+ auto usage =
+ std::accumulate(cmds.begin(), cmds.end(), std::string{"usage: "} + *argv + " [",
+ [](const auto& use, const auto& cmd) {
+ return use + std::string{cmd[0]} + (cmd[1].empty() ? "" : " ") +
+ std::string{cmd[1]} + "|";
+ });
+ usage[usage.size() - 1] = ']';
+ std::cerr << usage << std::endl;
+
+ throw std::runtime_error("main error: argument not recognized");
+ }
+
+ return 0;
+ }
+
+ catch (const std::exception& e) {
+ std::cerr << " * " << *argv << " " << e.what() << "\n";
+ }
+
+ catch (...) {
+ std::cerr << " * * " << *argv;
+ perror(" main error");
+ }
+
+ return 1;
+}
diff --git a/external/subpack/net/nginx-util/src/nginx-util.hpp b/external/subpack/net/nginx-util/src/nginx-util.hpp
new file mode 100644
index 0000000..fb5119f
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/nginx-util.hpp
@@ -0,0 +1,143 @@
+#ifndef __NGINX_UTIL_H
+#define __NGINX_UTIL_H
+
+#include <array>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <string>
+#include <string_view>
+// #include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <thread>
+#include <vector>
+
+#ifndef NO_UBUS
+#include "ubus-cxx.hpp"
+#endif
+
+#include "uci-cxx.hpp"
+
+static constexpr auto NGINX_UTIL = std::string_view{"/usr/bin/nginx-util"};
+
+static constexpr auto VAR_UCI_CONF = std::string_view{"/var/lib/nginx/uci.conf"};
+
+static constexpr auto UCI_CONF = std::string_view{"/etc/nginx/uci.conf"};
+
+static constexpr auto NGINX_CONF = std::string_view{"/etc/nginx/nginx.conf"};
+
+static constexpr auto CONF_DIR = std::string_view{"/etc/nginx/conf.d/"};
+
+static constexpr auto LAN_NAME = std::string_view{"_lan"};
+
+static auto constexpr MANAGE_SSL = std::string_view{"uci_manage_ssl"};
+
+static constexpr auto LAN_LISTEN = std::string_view{"/var/lib/nginx/lan.listen"};
+
+static constexpr auto LAN_LISTEN_DEFAULT = // TODO(pst) deprecate
+ std::string_view{"/var/lib/nginx/lan.listen.default"};
+
+// mode: optional ios::binary and/or ios::app (default ios::trunc)
+void write_file(const std::string_view& name,
+ const std::string& str,
+ std::ios_base::openmode flag = std::ios::trunc);
+
+// mode: optional ios::binary (internally ios::ate|ios::in)
+auto read_file(const std::string_view& name, std::ios_base::openmode mode = std::ios::in)
+ -> std::string;
+
+// all S must be convertible to const char[]
+template <typename... S>
+auto call(const std::string& program, S... args) -> pid_t;
+
+void create_lan_listen();
+
+void init_uci(const uci::package& pkg);
+
+auto is_enabled(const uci::package& pkg) -> bool;
+
+void init_lan();
+
+void get_env();
+
+// --------------------- partial implementation: ------------------------------
+
+void write_file(const std::string_view& name,
+ const std::string& str,
+ const std::ios_base::openmode flag)
+{
+ auto tmp = std::string{name};
+
+ if ((flag & std::ios::ate) == 0 && (flag & std::ios::app) == 0) {
+ tmp += ".tmp-XXXXXX";
+ auto fd = mkstemp(&tmp[0]);
+ if (fd == -1 || close(fd) != 0) {
+ throw std::runtime_error("write_file error: cannot access " + tmp);
+ }
+ }
+
+ try {
+ std::ofstream file(tmp.data(), flag);
+ if (!file.good()) {
+ throw std::ofstream::failure("write_file error: cannot open " + std::string{tmp});
+ }
+
+ file << str << std::flush;
+
+ file.close();
+ }
+ catch (...) {
+ if (tmp != name) {
+ remove(tmp.c_str());
+ } // remove can fail.
+ throw;
+ }
+
+ if (rename(tmp.c_str(), name.data()) != 0) {
+ throw std::runtime_error("write_file error: cannot move " + tmp + " to " + name.data());
+ }
+}
+
+auto read_file(const std::string_view& name, const std::ios_base::openmode mode) -> std::string
+{
+ std::ifstream file(name.data(), mode | std::ios::ate);
+ if (!file.good()) {
+ throw std::ifstream::failure("read_file error: cannot open " + std::string{name});
+ }
+
+ std::string ret{};
+ const size_t size = file.tellg();
+ ret.reserve(size);
+
+ file.seekg(0);
+ ret.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
+
+ file.close();
+ return ret;
+}
+
+template <typename... S>
+auto call(const char* program, S... args) -> pid_t
+{
+ pid_t pid = fork();
+
+ if (pid == 0) { // child:
+ std::array<char*, sizeof...(args) + 2> argv = {strdup(program), strdup(args)..., nullptr};
+
+ execv(program, argv.data()); // argv cannot be const char * const[]!
+
+ _exit(EXIT_FAILURE); // exec never returns.
+ }
+ else if (pid > 0) { // parent:
+ return pid;
+ }
+
+ std::string errmsg = "call error: cannot fork (";
+ errmsg += std::to_string(errno) + "): " + std::strerror(errno);
+ throw std::runtime_error(errmsg);
+}
+
+#endif
diff --git a/external/subpack/net/nginx-util/src/px5g-openssl.hpp b/external/subpack/net/nginx-util/src/px5g-openssl.hpp
new file mode 100644
index 0000000..7c79bad
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/px5g-openssl.hpp
@@ -0,0 +1,410 @@
+#ifndef _PX5G_OPENSSL_HPP
+#define _PX5G_OPENSSL_HPP
+
+// #define OPENSSL_API_COMPAT 0x10102000L
+#include <fcntl.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+#include <unistd.h>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+static constexpr auto rsa_min_modulus_bits = 512;
+
+using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
+
+using X509_NAME_ptr = std::unique_ptr<X509_NAME, decltype(&::X509_NAME_free)>;
+
+auto checkend(const std::string& crtpath, time_t seconds = 0, bool use_pem = true) -> bool;
+
+auto gen_eckey(int curve) -> EVP_PKEY_ptr;
+
+auto gen_rsakey(int keysize, BN_ULONG exponent = RSA_F4) -> EVP_PKEY_ptr;
+
+void write_key(const EVP_PKEY_ptr& pkey, const std::string& keypath = "", bool use_pem = true);
+
+auto subject2name(const std::string& subject) -> X509_NAME_ptr;
+
+void selfsigned(const EVP_PKEY_ptr& pkey,
+ int days,
+ const std::string& subject = "",
+ const std::string& crtpath = "",
+ bool use_pem = true);
+
+// ------------------------- implementation: ----------------------------------
+
+inline auto print_error(const char* str, const size_t /*len*/, void* errmsg) -> int
+{
+ *static_cast<std::string*>(errmsg) += str;
+ return 0;
+}
+
+// wrapper for clang-tidy:
+inline auto _BIO_new_fp(FILE* stream, const bool use_pem, const bool close = false) -> BIO*
+{
+ return BIO_new_fp(stream, // NOLINTNEXTLINE(hicpp-signed-bitwise) macros:
+ (use_pem ? BIO_FP_TEXT : 0) | (close ? BIO_CLOSE : BIO_NOCLOSE));
+}
+
+auto checkend(const std::string& crtpath, const time_t seconds, const bool use_pem) -> bool
+{
+ BIO* bio = crtpath.empty() ? _BIO_new_fp(stdin, use_pem)
+ : BIO_new_file(crtpath.c_str(), (use_pem ? "r" : "rb"));
+
+ X509* x509 = nullptr;
+
+ if (bio != nullptr) {
+ x509 = use_pem ? PEM_read_bio_X509_AUX(bio, nullptr, nullptr, nullptr)
+ : d2i_X509_bio(bio, nullptr);
+ BIO_free(bio);
+ }
+
+ if (x509 == nullptr) {
+ std::string errmsg{"checkend error: unable to load certificate\n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ time_t checktime = time(nullptr) + seconds;
+ auto cmp = X509_cmp_time(X509_get0_notAfter(x509), &checktime);
+
+ X509_free(x509);
+
+ return (cmp >= 0);
+}
+
+auto gen_eckey(const int curve) -> EVP_PKEY_ptr
+{
+ EC_GROUP* group = curve != 0 ? EC_GROUP_new_by_curve_name(curve) : nullptr;
+
+ if (group == nullptr) {
+ std::string errmsg{"gen_eckey error: cannot build group for curve id "};
+ errmsg += std::to_string(curve) + "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+
+ EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
+
+ auto* eckey = EC_KEY_new();
+
+ if (eckey != nullptr) {
+ if ((EC_KEY_set_group(eckey, group) == 0) || (EC_KEY_generate_key(eckey) == 0)) {
+ EC_KEY_free(eckey);
+ eckey = nullptr;
+ }
+ }
+
+ EC_GROUP_free(group);
+
+ if (eckey == nullptr) {
+ std::string errmsg{"gen_eckey error: cannot build key with curve id "};
+ errmsg += std::to_string(curve) + "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free};
+
+ // EVP_PKEY_assign_EC_KEY is a macro casting eckey to char *:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+ if (!EVP_PKEY_assign_EC_KEY(pkey.get(), eckey)) {
+ EC_KEY_free(eckey);
+ std::string errmsg{"gen_eckey error: cannot assign EC key to EVP\n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ return pkey;
+}
+
+auto gen_rsakey(const int keysize, const BN_ULONG exponent) -> EVP_PKEY_ptr
+{
+ if (keysize < rsa_min_modulus_bits || keysize > OPENSSL_RSA_MAX_MODULUS_BITS) {
+ std::string errmsg{"gen_rsakey error: RSA keysize ("};
+ errmsg += std::to_string(keysize) + ") out of range [512..";
+ errmsg += std::to_string(OPENSSL_RSA_MAX_MODULUS_BITS) + "]";
+ throw std::runtime_error(errmsg);
+ }
+ auto* bignum = BN_new();
+
+ if (bignum == nullptr) {
+ std::string errmsg{"gen_rsakey error: cannot get big number struct\n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ auto* rsa = RSA_new();
+
+ if (rsa != nullptr) {
+ if ((BN_set_word(bignum, exponent) == 0) ||
+ (RSA_generate_key_ex(rsa, keysize, bignum, nullptr) == 0))
+ {
+ RSA_free(rsa);
+ rsa = nullptr;
+ }
+ }
+
+ BN_free(bignum);
+
+ if (rsa == nullptr) {
+ std::string errmsg{"gen_rsakey error: cannot create RSA key with size"};
+ errmsg += std::to_string(keysize) + " and exponent ";
+ errmsg += std::to_string(exponent) + "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ EVP_PKEY_ptr pkey{EVP_PKEY_new(), ::EVP_PKEY_free};
+
+ // EVP_PKEY_assign_RSA is a macro casting rsa to char *:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
+ if (!EVP_PKEY_assign_RSA(pkey.get(), rsa)) {
+ RSA_free(rsa);
+ std::string errmsg{"gen_rsakey error: cannot assign RSA key to EVP\n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ return pkey;
+}
+
+void write_key(const EVP_PKEY_ptr& pkey, const std::string& keypath, const bool use_pem)
+{
+ BIO* bio = nullptr;
+
+ if (keypath.empty()) {
+ bio = _BIO_new_fp(stdout, use_pem);
+ }
+
+ else { // BIO_new_file(keypath.c_str(), (use_pem ? "w" : "wb") );
+
+ static constexpr auto mask = 0600;
+ // auto fd = open(keypath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, mask);
+ // creat has no cloexec, alt. triggers cppcoreguidelines-pro-type-vararg
+ // NOLINTNEXTLINE(android-cloexec-creat)
+ auto fd = creat(keypath.c_str(), mask); // the same without va_args.
+
+ if (fd >= 0) {
+ auto* fp = fdopen(fd, (use_pem ? "w" : "wb"));
+
+ if (fp != nullptr) {
+ bio = _BIO_new_fp(fp, use_pem, true);
+ if (bio == nullptr) {
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) fp owns fd:
+ fclose(fp);
+ }
+ }
+ else {
+ close(fd);
+ }
+ }
+ }
+
+ if (bio == nullptr) {
+ std::string errmsg{"write_key error: cannot open "};
+ errmsg += keypath.empty() ? "stdout" : keypath;
+ errmsg += "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ int len = 0;
+
+ auto* key = pkey.get();
+ switch (EVP_PKEY_base_id(key)) { // use same format as px5g:
+ case EVP_PKEY_EC:
+ len = use_pem ? PEM_write_bio_ECPrivateKey(bio, EVP_PKEY_get0_EC_KEY(key), nullptr,
+ nullptr, 0, nullptr, nullptr)
+ : i2d_ECPrivateKey_bio(bio, EVP_PKEY_get0_EC_KEY(key));
+ break;
+ case EVP_PKEY_RSA:
+ len = use_pem ? PEM_write_bio_RSAPrivateKey(bio, EVP_PKEY_get0_RSA(key), nullptr,
+ nullptr, 0, nullptr, nullptr)
+ : i2d_RSAPrivateKey_bio(bio, EVP_PKEY_get0_RSA(key));
+ break;
+ default:
+ len = use_pem
+ ? PEM_write_bio_PrivateKey(bio, key, nullptr, nullptr, 0, nullptr, nullptr)
+ : i2d_PrivateKey_bio(bio, key);
+ }
+
+ BIO_free_all(bio);
+
+ if (len == 0) {
+ std::string errmsg{"write_key error: cannot write EVP pkey to "};
+ errmsg += keypath.empty() ? "stdout" : keypath;
+ errmsg += "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+}
+
+auto subject2name(const std::string& subject) -> X509_NAME_ptr
+{
+ if (!subject.empty() && subject[0] != '/') {
+ throw std::runtime_error("subject2name errror: not starting with /");
+ }
+
+ X509_NAME_ptr name = {X509_NAME_new(), ::X509_NAME_free};
+
+ if (!name) {
+ std::string errmsg{"subject2name error: cannot create X509 name \n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ if (subject.empty()) {
+ return name;
+ }
+
+ int prev = 1;
+ std::string type{};
+ char chr = '=';
+ for (int i = 0; subject[i] != 0;) {
+ ++i;
+ if (subject[i] == '\\' && subject[++i] == '\0') {
+ throw std::runtime_error("subject2name errror: escape at the end");
+ }
+ if (subject[i] != chr && subject[i] != '\0') {
+ continue;
+ }
+ if (chr == '=') {
+ type = subject.substr(prev, i - prev);
+ chr = '/';
+ }
+ else {
+ auto nid = OBJ_txt2nid(type.c_str());
+ if (nid == NID_undef) {
+ // skip unknown entries (silently?).
+ }
+ else {
+ const auto* val = // X509_NAME_add_entry_by_NID wants it unsigned:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ reinterpret_cast<const unsigned char*>(&subject[prev]);
+
+ int len = i - prev;
+
+ if (X509_NAME_add_entry_by_NID(
+ name.get(), nid,
+ MBSTRING_ASC, // NOLINT(hicpp-signed-bitwise) is macro
+ val, len, -1, 0) == 0)
+ {
+ std::string errmsg{"subject2name error: cannot add "};
+ errmsg += "/" + type + "=" + subject.substr(prev, len) + "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+ }
+ chr = '=';
+ }
+ prev = i + 1;
+ }
+
+ return name;
+}
+
+void selfsigned(const EVP_PKEY_ptr& pkey,
+ const int days,
+ const std::string& subject,
+ const std::string& crtpath,
+ const bool use_pem)
+{
+ auto* x509 = X509_new();
+
+ if (x509 == nullptr) {
+ std::string errmsg{"selfsigned error: cannot create X509 structure\n"};
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+
+ auto freeX509_and_throw = [&x509](const std::string& what) {
+ X509_free(x509);
+ std::string errmsg{"selfsigned error: cannot set "};
+ errmsg += what + " in X509 certificate\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ };
+
+ if (X509_set_version(x509, 2) == 0) {
+ freeX509_and_throw("version");
+ }
+
+ if (X509_set_pubkey(x509, pkey.get()) == 0) {
+ freeX509_and_throw("pubkey");
+ }
+
+ if ((X509_gmtime_adj(X509_getm_notBefore(x509), 0) == nullptr) ||
+ (X509_time_adj_ex(X509_getm_notAfter(x509), days, 0, nullptr) == nullptr))
+ {
+ freeX509_and_throw("times");
+ }
+
+ X509_NAME_ptr name{nullptr, ::X509_NAME_free};
+
+ try {
+ name = subject2name(subject);
+ }
+ catch (...) {
+ X509_free(x509);
+ throw;
+ }
+
+ if (X509_set_subject_name(x509, name.get()) == 0) {
+ freeX509_and_throw("subject");
+ }
+
+ if (X509_set_issuer_name(x509, name.get()) == 0) {
+ freeX509_and_throw("issuer");
+ }
+
+ auto* bignum = BN_new();
+
+ if (bignum == nullptr) {
+ freeX509_and_throw("serial (creating big number struct)");
+ }
+
+ static const auto BITS = 159;
+ if (BN_rand(bignum, BITS, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY) == 0) {
+ BN_free(bignum);
+ freeX509_and_throw("serial (creating random number)");
+ }
+
+ if (BN_to_ASN1_INTEGER(bignum, X509_get_serialNumber(x509)) == nullptr) {
+ BN_free(bignum);
+ freeX509_and_throw("random serial");
+ }
+
+ BN_free(bignum);
+
+ if (X509_sign(x509, pkey.get(), EVP_sha256()) == 0) {
+ freeX509_and_throw("signing digest");
+ }
+
+ BIO* bio = crtpath.empty() ? _BIO_new_fp(stdout, use_pem)
+ : BIO_new_file(crtpath.c_str(), (use_pem ? "w" : "wb"));
+
+ int len = 0;
+
+ if (bio != nullptr) {
+ len = use_pem ? PEM_write_bio_X509(bio, x509) : i2d_X509_bio(bio, x509);
+ BIO_free_all(bio);
+ }
+
+ X509_free(x509);
+
+ if (len == 0) {
+ std::string errmsg{"selfsigned error: cannot write certificate to "};
+ errmsg += crtpath.empty() ? "stdout" : crtpath;
+ errmsg += "\n";
+ ERR_print_errors_cb(print_error, &errmsg);
+ throw std::runtime_error(errmsg);
+ }
+}
+
+#endif
diff --git a/external/subpack/net/nginx-util/src/px5g.cpp b/external/subpack/net/nginx-util/src/px5g.cpp
new file mode 100644
index 0000000..4edfb3b
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/px5g.cpp
@@ -0,0 +1,458 @@
+#include <unistd.h>
+#include <array>
+#include <iostream>
+#include <numeric>
+#include <string>
+#include <string_view>
+#include "px5g-openssl.hpp"
+
+class argv_view { // TODO(pst): use std::span when available.
+
+ private:
+ std::basic_string_view<const char*> data;
+
+ public:
+ argv_view(const argv_view&) = delete;
+
+ argv_view(argv_view&&) = delete;
+
+ auto operator=(const argv_view&) -> argv_view& = delete;
+
+ auto operator=(argv_view &&) -> argv_view& = delete;
+
+ argv_view(const char** argv, int argc) : data{argv, static_cast<size_t>(argc)} {}
+
+ inline auto operator[](size_t pos) const -> std::string_view
+ {
+ return std::string_view{data[pos]};
+ }
+
+ [[nodiscard]] inline constexpr auto size() const noexcept -> size_t
+ {
+ return data.size();
+ }
+
+ ~argv_view() = default;
+};
+
+static const auto default_validity = 30;
+
+auto checkend(const argv_view& argv) -> int;
+
+void eckey(const argv_view& argv);
+
+void rsakey(const argv_view& argv);
+
+void selfsigned(const argv_view& argv);
+
+inline auto parse_int(const std::string_view& arg) -> int
+{
+ size_t pos = 0;
+ int ret = stoi(std::string{arg}, &pos);
+ if (pos < arg.size()) {
+ throw std::runtime_error("number has trailing char");
+ }
+ return ret;
+}
+
+inline auto parse_curve(const std::string_view& name) -> int
+{
+ if (name == "P-384") {
+ return NID_secp384r1;
+ }
+ if (name == "P-521") {
+ return NID_secp521r1;
+ }
+ if (name == "P-256" || name == "secp256r1") {
+ return NID_X9_62_prime256v1;
+ }
+ if (name == "secp192r1") {
+ return NID_X9_62_prime192v1;
+ }
+ return OBJ_sn2nid(name.data());
+ // not: if (curve == 0) { curve = EC_curve_nist2nid(name.c_str()); }
+}
+
+auto checkend(const argv_view& argv) -> int
+{
+ bool use_pem = true;
+ std::string crtpath{};
+ time_t seconds = 0;
+
+ for (size_t i = 2; i < argv.size(); ++i) {
+ if (argv[i] == "-der") {
+ use_pem = false;
+ }
+ else if (argv[i] == "-in") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("checkend error: -in misses filename");
+ }
+
+ if (!crtpath.empty()) {
+ if (argv[i] == crtpath) {
+ std::cerr << "checkend warning: repeated same -in file\n";
+ }
+ else {
+ throw std::runtime_error("checkend error: more than one -in file");
+ }
+ }
+
+ crtpath = argv[i];
+ }
+
+ else if (argv[i][0] == '-') {
+ std::cerr << "checkend warning: skipping option " << argv[i] << std::endl;
+ }
+ else { // main option:
+ intmax_t num = 0;
+
+ try {
+ num = parse_int(argv[i]);
+ }
+ catch (...) {
+ auto errmsg = std::string{"checkend error: invalid time "};
+ errmsg += argv[i];
+ std::throw_with_nested(std::runtime_error(errmsg));
+ }
+
+ seconds = static_cast<time_t>(num);
+
+ if (num != static_cast<intmax_t>(seconds)) {
+ auto errmsg = std::string{"checkend error: time too big "};
+ errmsg += argv[i];
+ throw std::runtime_error(errmsg);
+ }
+ }
+ }
+
+ bool valid = checkend(crtpath, seconds, use_pem);
+ std::cout << "Certificate will" << (valid ? " not " : " ") << "expire" << std::endl;
+
+ return (valid ? 0 : 1);
+}
+
+void eckey(const argv_view& argv)
+{
+ bool has_main_option = false;
+ bool use_pem = true;
+ std::string keypath{};
+ int curve = NID_X9_62_prime256v1;
+
+ for (size_t i = 2; i < argv.size(); ++i) {
+ if (argv[i] == "-der") {
+ use_pem = false;
+ }
+ else if (argv[i] == "-out") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("eckey error: -out misses filename");
+ }
+
+ if (!keypath.empty()) {
+ if (argv[i] == keypath) {
+ std::cerr << "eckey warning: repeated same -out file\n";
+ }
+ else {
+ throw std::runtime_error("eckey error: more than one -out file");
+ }
+ }
+
+ keypath = argv[i];
+ }
+
+ else if (argv[i][0] == '-') {
+ std::cerr << "eckey warning: skipping option " << argv[i] << std::endl;
+ }
+ else { // main option:
+
+ if (has_main_option) {
+ throw std::runtime_error("eckey error: more than one main option");
+ } // else:
+ has_main_option = true;
+
+ curve = parse_curve(argv[i]);
+ }
+ }
+
+ write_key(gen_eckey(curve), keypath, use_pem);
+}
+
+void rsakey(const argv_view& argv)
+{
+ bool has_main_option = false;
+ bool use_pem = true;
+ std::string keypath{};
+ BN_ULONG exponent = RSA_F4;
+ int keysize = rsa_min_modulus_bits;
+
+ for (size_t i = 2; i < argv.size(); ++i) {
+ if (argv[i] == "-der") {
+ use_pem = false;
+ }
+ else if (argv[i] == "-3") {
+ exponent = 3;
+ }
+ else if (argv[i] == "-out") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("rsakey error: -out misses filename");
+ }
+
+ if (!keypath.empty()) {
+ if (argv[i] == keypath) {
+ std::cerr << "rsakey warning: repeated -out file" << std::endl;
+ }
+ else {
+ throw std::runtime_error("rsakey error: more than one -out file");
+ }
+ }
+
+ keypath = argv[i];
+ }
+
+ else if (argv[i][0] == '-') {
+ std::cerr << "rsakey warning: skipping option " << argv[i] << std::endl;
+ }
+ else { // main option:
+
+ if (has_main_option) {
+ throw std::runtime_error("rsakey error: more than one keysize");
+ } // else:
+ has_main_option = true;
+
+ try {
+ keysize = parse_int(argv[i]);
+ }
+ catch (...) {
+ std::string errmsg{"rsakey error: invalid keysize "};
+ errmsg += argv[i];
+ std::throw_with_nested(std::runtime_error(errmsg));
+ }
+ }
+ }
+
+ write_key(gen_rsakey(keysize, exponent), keypath, use_pem);
+}
+
+void selfsigned(const argv_view& argv)
+{
+ bool use_pem = true;
+ int days = default_validity;
+ std::string keypath{};
+ std::string crtpath{};
+ std::string subject{};
+
+ bool use_rsa = true;
+ int keysize = rsa_min_modulus_bits;
+ BN_ULONG exponent = RSA_F4;
+
+ int curve = NID_X9_62_prime256v1;
+
+ for (size_t i = 2; i < argv.size(); ++i) {
+ if (argv[i] == "-der") {
+ use_pem = false;
+ }
+ else if (argv[i] == "-days") {
+ ++i;
+ try {
+ days = parse_int(argv[i]);
+ }
+ catch (...) {
+ std::string errmsg{"selfsigned error: not a number for -days "};
+ errmsg += argv[i].substr(4);
+ std::throw_with_nested(std::runtime_error(errmsg));
+ }
+ }
+
+ else if (argv[i] == "-newkey") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("selfsigned error: -newkey misses algorithm option");
+ }
+
+ static constexpr auto rsa_prefix = std::string_view{"rsa:"};
+
+ if (argv[i] == "ec") {
+ use_rsa = false;
+ }
+ else if (argv[i].rfind(rsa_prefix, 0) == 0) {
+ use_rsa = true;
+ try {
+ keysize = parse_int(argv[i].substr(rsa_prefix.size()));
+ }
+ catch (...) {
+ std::string errmsg{"selfsigned error: invalid keysize "};
+ errmsg += argv[i].substr(4);
+ std::throw_with_nested(std::runtime_error(errmsg));
+ }
+ }
+ else {
+ throw std::runtime_error("selfsigned error: invalid algorithm");
+ }
+ }
+
+ else if (argv[i] == "-pkeyopt") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("selfsigned error: -pkeyopt misses value");
+ }
+
+ static constexpr auto curve_prefix = std::string_view{"ec_paramgen_curve:"};
+
+ if (argv[i].rfind(curve_prefix, 0) != 0) {
+ throw std::runtime_error("selfsigned error: -pkeyopt invalid");
+ }
+
+ curve = parse_curve(argv[i].substr(curve_prefix.size()));
+ }
+
+ else if (argv[i] == "-keyout") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("selfsigned error: -keyout misses path");
+ }
+
+ if (!keypath.empty()) {
+ if (argv[i] == keypath) {
+ std::cerr << "selfsigned warning: repeated -keyout file\n";
+ }
+ else {
+ throw std::runtime_error("selfsigned error: more than one -keyout file");
+ }
+ }
+
+ keypath = argv[i];
+ }
+
+ else if (argv[i] == "-out") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("selfsigned error: -out misses filename");
+ }
+
+ if (!crtpath.empty()) {
+ if (argv[i] == crtpath) {
+ std::cerr << "selfsigned warning: repeated same -out file\n";
+ }
+ else {
+ throw std::runtime_error("selfsigned error: more than one -out file");
+ }
+ }
+
+ crtpath = argv[i];
+ }
+
+ else if (argv[i] == "-subj") {
+ ++i;
+
+ if (i >= argv.size()) {
+ throw std::runtime_error("selfsigned error: -subj misses value");
+ }
+
+ if (!subject.empty()) {
+ if (argv[i] == subject) {
+ std::cerr << "selfsigned warning: repeated same -subj\n";
+ }
+ else {
+ throw std::runtime_error("selfsigned error: more than one -subj value");
+ }
+ }
+
+ subject = argv[i];
+ }
+
+ else {
+ std::cerr << "selfsigned warning: skipping option " << argv[i] << std::endl;
+ }
+ }
+
+ auto pkey = use_rsa ? gen_rsakey(keysize, exponent) : gen_eckey(curve);
+
+ selfsigned(pkey, days, subject, crtpath, use_pem);
+
+ if (!keypath.empty()) {
+ write_key(pkey, keypath, use_pem);
+ }
+}
+
+auto main(int argc, const char** argv) -> int
+{
+ auto args = argv_view{argv, argc};
+
+ auto cmds = std::array{
+ std::array<std::string, 2>{"checkend",
+ " [-der] [-in certificate_path] [seconds_remaining]"},
+ std::array<std::string, 2>{"eckey", " [-der] [-out key_path] [curve_name]"},
+ std::array<std::string, 2>{"rsakey", " [-der] [-out key_path] [-3] [key_size]"},
+ std::array<std::string, 2>{
+ "selfsigned",
+ " [-der] [-keyout key_path] [-out certificate_path]"
+ " [-newkey ec|rsa:key_size] [-pkeyopt ec_paramgen_curve:name]"
+ " [-days validity] [-subj /C=.../ST=.../L=.../O=.../CN=.../... ]"},
+ };
+
+ try {
+ if (argc < 2) {
+ throw std::runtime_error("error: no argument");
+ }
+
+ if (args[1] == cmds[0][0]) {
+ return checkend(args);
+ }
+
+ if (args[1] == cmds[1][0]) {
+ eckey(args);
+ }
+
+ else if (args[1] == cmds[2][0]) {
+ rsakey(args);
+ }
+
+ else if (args[1] == cmds[3][0]) {
+ selfsigned(args);
+ }
+
+ else {
+ throw std::runtime_error("error: argument not recognized");
+ }
+ }
+
+ catch (const std::exception& e) {
+ auto usage = std::accumulate(
+ cmds.begin(), cmds.end(), std::string{"usage: \n"},
+ [=](const auto& use, const auto& cmd) {
+ return use + std::string{4, ' '} + *argv + " " + cmd[0] + cmd[1] + "\n";
+ });
+
+ std::cerr << usage << std::flush;
+
+ auto print_nested = [](auto&& self, const std::exception& outer, int depth = 0) -> void {
+ std::cerr << std::string(depth, '\t') << outer.what() << std::endl;
+ try {
+ std::rethrow_if_nested(outer);
+ }
+ catch (const std::exception& inner) {
+ self(self, inner, depth + 1);
+ }
+ };
+
+ print_nested(print_nested, e);
+
+ return 1;
+ }
+
+ catch (...) {
+ std::cerr << *argv << " unknown error." << std::endl;
+ return 2;
+ }
+
+ return 0;
+}
diff --git a/external/subpack/net/nginx-util/src/regex-pcre.hpp b/external/subpack/net/nginx-util/src/regex-pcre.hpp
new file mode 100644
index 0000000..f63d5f9
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/regex-pcre.hpp
@@ -0,0 +1,483 @@
+#ifndef __REGEXP_PCRE_HPP
+#define __REGEXP_PCRE_HPP
+
+#include <pcre.h>
+#include <array>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace rgx {
+/* partially implement the std::regex interface using PCRE for performance
+ * (=> pass "match" as non-const reference)
+ */
+
+namespace regex_constants {
+enum error_type {
+ _enum_error_collate,
+ _enum_error_ctype,
+ _enum_error_escape,
+ _enum_error_backref,
+ _enum_error_brack,
+ _enum_error_paren,
+ _enum_error_brace,
+ _enum_error_badbrace,
+ _enum_error_range,
+ _enum_error_space,
+ _enum_error_badrepeat,
+ _enum_error_complexity,
+ _enum_error_stack,
+ _enum_error_last
+};
+static const error_type error_collate(_enum_error_collate);
+static const error_type error_ctype(_enum_error_ctype);
+static const error_type error_escape(_enum_error_escape);
+static const error_type error_backref(_enum_error_backref);
+static const error_type error_brack(_enum_error_brack);
+static const error_type error_paren(_enum_error_paren);
+static const error_type error_brace(_enum_error_brace);
+static const error_type error_badbrace(_enum_error_badbrace);
+static const error_type error_range(_enum_error_range);
+static const error_type error_space(_enum_error_space);
+static const error_type error_badrepeat(_enum_error_badrepeat);
+static const error_type error_complexity(_enum_error_complexity);
+static const error_type error_stack(_enum_error_stack);
+} // namespace regex_constants
+
+class regex_error : public std::runtime_error {
+ private:
+ regex_constants::error_type errcode;
+
+ public:
+ explicit regex_error(regex_constants::error_type code, const char* what = "regex error")
+ : runtime_error(what), errcode(code)
+ {}
+
+ [[nodiscard]] auto virtual code() const -> regex_constants::error_type;
+};
+
+[[nodiscard]] auto regex_error::code() const -> regex_constants::error_type
+{
+ return errcode;
+}
+
+class regex {
+ private:
+ int errcode = 0;
+
+ const char* errptr = nullptr;
+
+ int erroffset = 0;
+
+ pcre* const re = nullptr;
+
+ static const std::array<regex_constants::error_type, 86> errcode_pcre2regex;
+
+ static const auto BASE = 10;
+
+ public:
+ inline regex() = default;
+
+ inline regex(const regex&) = delete;
+
+ inline regex(regex&&) = default;
+
+ inline auto operator=(const regex&) -> regex& = delete;
+
+ inline auto operator=(regex &&) -> regex& = delete;
+
+ explicit regex(const std::string& str) : regex(str.c_str()) {}
+
+ explicit regex(const char* const str)
+ : re{pcre_compile2(str, 0, &errcode, &errptr, &erroffset, nullptr)}
+ {
+ if (re == nullptr) {
+ std::string what = std::string("regex error: ") + errptr + '\n';
+ what += " '" + std::string{str} + "'\n";
+ what += " " + std::string(erroffset, ' ') + '^';
+
+ throw regex_error(errcode_pcre2regex.at(errcode), what.c_str());
+ }
+ }
+
+ ~regex()
+ {
+ if (re != nullptr) {
+ pcre_free(re);
+ }
+ }
+
+ inline auto operator()() const -> const pcre*
+ {
+ return re;
+ }
+};
+
+class smatch {
+ friend auto regex_search(std::string::const_iterator begin,
+ std::string::const_iterator end,
+ smatch& match, // NOLINT(google-runtime-references)
+ const regex& rgx); // match std::regex interface.
+
+ private:
+ std::string::const_iterator begin;
+
+ std::string::const_iterator end;
+
+ std::vector<int> vec{};
+
+ int n = 0;
+
+ public:
+ [[nodiscard]] inline auto position(int i = 0) const
+ {
+ return (i < 0 || i >= n) ? std::string::npos : vec[2 * i];
+ }
+
+ [[nodiscard]] inline auto length(int i = 0) const
+ {
+ return (i < 0 || i >= n) ? 0 : vec[2 * i + 1] - vec[2 * i];
+ }
+
+ [[nodiscard]] auto str(int i = 0) const -> std::string
+ { // should we throw?
+ if (i < 0 || i >= n) {
+ return "";
+ }
+ int x = vec[2 * i];
+ if (x < 0) {
+ return "";
+ }
+ int y = vec[2 * i + 1];
+ return std::string{begin + x, begin + y};
+ }
+
+ [[nodiscard]] auto format(const std::string& fmt) const;
+
+ [[nodiscard]] auto size() const -> int
+ {
+ return n;
+ }
+
+ [[nodiscard]] inline auto empty() const
+ {
+ return n < 0;
+ }
+
+ [[nodiscard]] inline auto ready() const
+ {
+ return !vec.empty();
+ }
+};
+
+inline auto regex_search(const std::string& subj, const regex& rgx);
+
+auto regex_replace(const std::string& subj, const regex& rgx, const std::string& insert);
+
+inline auto regex_search(const std::string& subj,
+ smatch& match, // NOLINT(google-runtime-references)
+ const regex& rgx); // match std::regex interface.
+
+auto regex_search(std::string::const_iterator begin,
+ std::string::const_iterator end,
+ smatch& match, // NOLINT(google-runtime-references)
+ const regex& rgx); // match std::regex interface.
+
+// ------------------------- implementation: ----------------------------------
+
+inline auto regex_search(const std::string& subj, const regex& rgx)
+{
+ if (rgx() == nullptr) {
+ throw std::runtime_error("regex_search error: no regex given");
+ }
+ int n =
+ pcre_exec(rgx(), nullptr, subj.c_str(), static_cast<int>(subj.length()), 0, 0, nullptr, 0);
+ return n >= 0;
+}
+
+auto regex_search(const std::string::const_iterator begin,
+ const std::string::const_iterator end,
+ smatch& match,
+ const regex& rgx)
+{
+ if (rgx() == nullptr) {
+ throw std::runtime_error("regex_search error: no regex given");
+ }
+
+ int sz = 0;
+ pcre_fullinfo(rgx(), nullptr, PCRE_INFO_CAPTURECOUNT, &sz);
+ sz = 3 * (sz + 1);
+
+ match.vec.reserve(sz);
+
+ const char* subj = &*begin;
+ int len = static_cast<int>(&*end - subj);
+
+ match.begin = begin;
+ match.end = end;
+
+ match.n = pcre_exec(rgx(), nullptr, subj, len, 0, 0, &match.vec[0], sz);
+
+ if (match.n < 0) {
+ return false;
+ }
+ if (match.n == 0) {
+ match.n = sz / 3;
+ }
+
+ return true;
+}
+
+inline auto regex_search(const std::string& subj, smatch& match, const regex& rgx)
+{
+ return regex_search(subj.begin(), subj.end(), match, rgx);
+}
+
+auto smatch::format(const std::string& fmt) const
+{
+ std::string ret{};
+ size_t index = 0;
+
+ size_t pos = 0;
+ while ((pos = fmt.find('$', index)) != std::string::npos) {
+ ret.append(fmt, index, pos - index);
+ index = pos + 1;
+
+ char chr = fmt[index++];
+ switch (chr) {
+ case '&': // match
+ ret += str(0);
+ break;
+
+ case '`': // prefix
+ ret.append(begin, begin + vec[0]);
+ break;
+
+ case '\'': // suffix
+ ret.append(begin + vec[1], end);
+ break;
+
+ default:
+ if (isdigit(chr) != 0) { // one or two digits => submatch:
+ int num = chr - '0';
+ chr = fmt[index];
+ if (isdigit(chr) != 0) { // second digit:
+ ++index;
+ static const auto base = 10;
+ num = num * base + chr - '0';
+ }
+ ret += str(num);
+ break;
+ } // else:
+
+ ret += '$';
+ [[fallthrough]];
+
+ case '$': // escaped
+ ret += chr;
+ }
+ }
+ ret.append(fmt, index);
+ return ret;
+}
+
+auto regex_replace(const std::string& subj, const regex& rgx, const std::string& insert)
+{
+ if (rgx() == nullptr) {
+ throw std::runtime_error("regex_replace error: no regex given");
+ }
+
+ std::string ret{};
+ auto pos = subj.begin();
+
+ for (smatch match; regex_search(pos, subj.end(), match, rgx);
+ pos += match.position(0) + match.length(0))
+ {
+ ret.append(pos, pos + match.position(0));
+ ret.append(match.format(insert));
+ }
+
+ ret.append(pos, subj.end());
+ return ret;
+}
+
+// ------------ There is only the translation table below : -------------------
+
+const std::array<regex_constants::error_type, 86> regex::errcode_pcre2regex = {
+ // 0 no error
+ regex_constants::error_type::_enum_error_last,
+ // 1 \ at end of pattern
+ regex_constants::error_escape,
+ // 2 \c at end of pattern
+ regex_constants::error_escape,
+ // 3 unrecognized character follows \ .
+ regex_constants::error_escape,
+ // 4 numbers out of order in {} quantifier
+ regex_constants::error_badbrace,
+ // 5 number too big in {} quantifier
+ regex_constants::error_badbrace,
+ // 6 missing terminating for character class
+ regex_constants::error_brack,
+ // 7 invalid escape sequence in character class
+ regex_constants::error_escape,
+ // 8 range out of order in character class
+ regex_constants::error_range,
+ // 9 nothing to repeat
+ regex_constants::error_badrepeat,
+ // 10 [this code is not in use
+ regex_constants::error_type::_enum_error_last,
+ // 11 internal error: unexpected repeat
+ regex_constants::error_badrepeat,
+ // 12 unrecognized character after (? or (?-
+ regex_constants::error_backref,
+ // 13 POSIX named classes are supported only within a class
+ regex_constants::error_range,
+ // 14 missing )
+ regex_constants::error_paren,
+ // 15 reference to non-existent subpattern
+ regex_constants::error_backref,
+ // 16 erroffset passed as NULL
+ regex_constants::error_type::_enum_error_last,
+ // 17 unknown option bit(s) set
+ regex_constants::error_type::_enum_error_last,
+ // 18 missing ) after comment
+ regex_constants::error_paren,
+ // 19 [this code is not in use
+ regex_constants::error_type::_enum_error_last,
+ // 20 regular expression is too large
+ regex_constants::error_space,
+ // 21 failed to get memory
+ regex_constants::error_stack,
+ // 22 unmatched parentheses
+ regex_constants::error_paren,
+ // 23 internal error: code overflow
+ regex_constants::error_stack,
+ // 24 unrecognized character after (?<
+ regex_constants::error_backref,
+ // 25 lookbehind assertion is not fixed length
+ regex_constants::error_backref,
+ // 26 malformed number or name after (?(
+ regex_constants::error_backref,
+ // 27 conditional group contains more than two branches
+ regex_constants::error_backref,
+ // 28 assertion expected after (?(
+ regex_constants::error_backref,
+ // 29 (?R or (?[+-digits must be followed by )
+ regex_constants::error_backref,
+ // 30 unknown POSIX class name
+ regex_constants::error_ctype,
+ // 31 POSIX collating elements are not supported
+ regex_constants::error_collate,
+ // 32 this version of PCRE is compiled without UTF support
+ regex_constants::error_collate,
+ // 33 [this code is not in use
+ regex_constants::error_type::_enum_error_last,
+ // 34 character value in \x{} or \o{} is too large
+ regex_constants::error_escape,
+ // 35 invalid condition (?(0)
+ regex_constants::error_backref,
+ // 36 \C not allowed in lookbehind assertion
+ regex_constants::error_escape,
+ // 37 PCRE does not support \L, \l, \N{name}, \U, or \u
+ regex_constants::error_escape,
+ // 38 number after (?C is > 255
+ regex_constants::error_backref,
+ // 39 closing ) for (?C expected
+ regex_constants::error_paren,
+ // 40 recursive call could loop indefinitely
+ regex_constants::error_complexity,
+ // 41 unrecognized character after (?P
+ regex_constants::error_backref,
+ // 42 syntax error in subpattern name (missing terminator)
+ regex_constants::error_paren,
+ // 43 two named subpatterns have the same name
+ regex_constants::error_backref,
+ // 44 invalid UTF-8 string (specifically UTF-8)
+ regex_constants::error_collate,
+ // 45 support for \P, \p, and \X has not been compiled
+ regex_constants::error_escape,
+ // 46 malformed \P or \p sequence
+ regex_constants::error_escape,
+ // 47 unknown property name after \P or \p
+ regex_constants::error_escape,
+ // 48 subpattern name is too long (maximum 32 characters)
+ regex_constants::error_backref,
+ // 49 too many named subpatterns (maximum 10000)
+ regex_constants::error_complexity,
+ // 50 [this code is not in use
+ regex_constants::error_type::_enum_error_last,
+ // 51 octal value is greater than \377 in 8-bit non-UTF-8 mode
+ regex_constants::error_escape,
+ // 52 internal error: overran compiling workspace
+ regex_constants::error_type::_enum_error_last,
+ // 53 internal error: previously-checked referenced subpattern not found
+ regex_constants::error_type::_enum_error_last,
+ // 54 DEFINE group contains more than one branch
+ regex_constants::error_backref,
+ // 55 repeating a DEFINE group is not allowed
+ regex_constants::error_backref,
+ // 56 inconsistent NEWLINE options
+ regex_constants::error_escape,
+ // 57 \g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain
+ // number
+ regex_constants::error_backref,
+ // 58 a numbered reference must not be zero
+ regex_constants::error_backref,
+ // 59 an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)
+ regex_constants::error_complexity,
+ // 60 (*VERB) not recognized or malformed
+ regex_constants::error_complexity,
+ // 61 number is too big
+ regex_constants::error_complexity,
+ // 62 subpattern name expected
+ regex_constants::error_backref,
+ // 63 digit expected after (?+
+ regex_constants::error_backref,
+ // 64 is an invalid data character in JavaScript compatibility mode
+ regex_constants::error_escape,
+ // 65 different names for subpatterns of the same number are not allowed
+ regex_constants::error_backref,
+ // 66 (*MARK) must have an argument
+ regex_constants::error_complexity,
+ // 67 this version of PCRE is not compiled with Unicode property support
+ regex_constants::error_collate,
+ // 68 \c must be followed by an ASCII character
+ regex_constants::error_escape,
+ // 69 \k is not followed by a braced, angle-bracketed, or quoted name
+ regex_constants::error_backref,
+ // 70 internal error: unknown opcode in find_fixedlength()
+ regex_constants::error_type::_enum_error_last,
+ // 71 \N is not supported in a class
+ regex_constants::error_ctype,
+ // 72 too many forward references
+ regex_constants::error_backref,
+ // 73 disallowed Unicode code point (>= 0xd800 && <= 0xdfff)
+ regex_constants::error_escape,
+ // 74 invalid UTF-16 string (specifically UTF-16)
+ regex_constants::error_collate,
+ // 75 name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)
+ regex_constants::error_complexity,
+ // 76 character value in \u.... sequence is too large
+ regex_constants::error_escape,
+ // 77 invalid UTF-32 string (specifically UTF-32)
+ regex_constants::error_collate,
+ // 78 setting UTF is disabled by the application
+ regex_constants::error_collate,
+ // 79 non-hex character in \x{} (closing brace missing?)
+ regex_constants::error_escape,
+ // 80 non-octal character in \o{} (closing brace missing?)
+ regex_constants::error_escape,
+ // 81 missing opening brace after \o
+ regex_constants::error_brace,
+ // 82 parentheses are too deeply nested
+ regex_constants::error_complexity,
+ // 83 invalid range in character class
+ regex_constants::error_range,
+ // 84 group name must start with a non-digit
+ regex_constants::error_backref,
+ // 85 parentheses are too deeply nested (stack check)
+ regex_constants::error_stack};
+
+} // namespace rgx
+
+#endif
diff --git a/external/subpack/net/nginx-util/src/test-nginx-util-root.sh b/external/subpack/net/nginx-util/src/test-nginx-util-root.sh
new file mode 100644
index 0000000..eb8ca56
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/test-nginx-util-root.sh
@@ -0,0 +1,609 @@
+#!/bin/sh
+
+PRINT_PASSED=2
+
+NGINX_UTIL="/usr/bin/nginx-util"
+
+ORIG=".original-test-nginx-util-root"
+
+mkdir -p /tmp/.uci/
+
+uci commit nginx || { printf "Error invoking: uci commit\n Exit."; exit 2; }
+
+
+pst_exit() {
+ printf "\nExit: Recovering original settings ... "
+
+ uci revert nginx
+
+ cd "/etc/config/" && rm "nginx" && mv "nginx.${ORIG}" "nginx" ||
+ printf "\n%s: not moved %s to %s\n" "/etc/config/" "nginx${ORIG}" "nginx"
+
+ cd "/etc/crontabs/" && rm "root" && mv "root${ORIG}" "root" ||
+ printf "\n%s: not moved %s to %s\n" "/etc/crontabs/" "root${ORIG}" "root"
+
+ cd "$(dirname "${CONF_DIR}")" && rm -r "${CONF_DIR}" &&
+ mv "$(basename "${CONF_DIR}")${ORIG}" "$(basename "${CONF_DIR}")" ||
+ printf "\n%s: not moved %s to %s\n" "$(dirname "${CONF_DIR}")" \
+ "$(basename "${CONF_DIR}")${ORIG}" "$(basename "${CONF_DIR}")"
+
+ printf "done.\n"
+
+ exit "$1"
+}
+
+
+mkdir -p "/etc/config/" && touch "/etc/config/nginx"
+
+cd "/etc/config/" && [ ! -e "nginx${ORIG}" ] && cp "nginx" "nginx.${ORIG}" || {
+ printf "\n%s: not copied %s to %s\n" "/etc/config/" "nginx" "nginx${ORIG}"
+ pst_exit 3
+}
+
+uci set nginx.global.uci_enable=1
+
+
+mkdir -p "/etc/crontabs/" && touch "/etc/crontabs/root"
+
+cd "/etc/crontabs/" && [ ! -e "root${ORIG}" ] && mv "root" "root${ORIG}" || {
+ printf "\n%s: not moved %s to %s\n" "/etc/crontabs/" "root${ORIG}" "root"
+ pst_exit 4
+}
+
+touch "/etc/crontabs/root"
+
+
+# ----------------------------------------------------------------------------
+
+__esc_newlines() {
+ echo "${1}" | sed -E 's/$/\\n/' | tr -d '\n' | sed -E 's/\\n$/\n/'
+}
+
+__esc_sed_rhs() {
+ __esc_newlines "${1}" | sed -E 's/[&/\]/\\&/g'
+}
+
+_sed_rhs() {
+ __esc_sed_rhs "$(echo "${1}" | sed -E "s/[$]/$(__esc_sed_rhs "${2}")/g")"
+}
+
+__esc_regex() {
+ __esc_newlines "${1}" | sed -E 's/[^^_a-zA-Z0-9-]/[&]/g; s/\^/\\^/g'
+}
+
+_regex() {
+ __esc_regex "${1}" | sed -E -e 's/^(\[\s])*/^\\s*/' \
+ -e 's/(\[\s])+\[[*]]/(\\s.*)?/g' \
+ -e 's/(\[\s])+/\\s+/g' \
+ -e 's/(\[\s])*\[[;]]/\\s*;/g' \
+ -e "s/\[['\"]]/['\"]?/g" \
+ -e "s/\[[$]]/$(__esc_sed_rhs "$(__esc_regex "${2}")")/g"
+}
+
+_echo_sed() {
+ echo "" | sed -E "c${1}"
+}
+
+
+fileauto="# This file is re-created when Nginx starts."
+
+setpoint_init_lan() {
+ echo "${fileauto}"
+
+ sed -n -E '/^\s*#UCI_HTTP_CONFIG\s*$/q;p' "${UCI_CONF}.template"
+
+ local rhs="\t}\n\n\tserver { #see uci show 'nginx.\1'"
+ uci -n export nginx \
+ | sed -E -e "s/'//g" \
+ -e '/^\s*package\s+nginx\s*$/d' \
+ -e '/^\s*config\s+main\s/d' \
+ -e "s/^\s*config\s+server\s+(.*)$/$rhs/g" \
+ -e 's/^\s*list\s/\t\t/g' \
+ -e 's/^\s*option\s/\t\t/g' \
+ -e 's/^\s*uci_listen_locally\s+/\t\tlisten 127.0.0.1:/g' \
+ -e '/^\s*uci_/d' \
+ -e '/^$/d' -e "s/[^'\n]$/&;/g" \
+ | sed "1,2d"
+ printf "\t}\n\n"
+
+ sed -E '1,/^\s*#UCI_HTTP_CONFIG\s*$/ d' "${UCI_CONF}.template"
+}
+
+
+setpoint_add_ssl() {
+ local indent="\n$1"
+ local name="$2"
+ local default=""
+ [ "${name}" = "${LAN_NAME}" ] && default=".default"
+ local prefix="${CONF_DIR}${name}"
+
+ local ADDS=""
+ local CONF
+ CONF="$(sed -E \
+ -e "s/$(_regex "${NGX_INCLUDE}" "${LAN_LISTEN}${default}")/$1$(\
+ _sed_rhs "${NGX_INCLUDE}" "${LAN_SSL_LISTEN}${default}")/g" \
+ -e "s/^(\s*listen\s+)([^:]*:|\[[^]]*\]:)?80(\s|$|;)/\1\2443 ssl\3/g" \
+ "${prefix}.sans" 2>/dev/null)"
+ echo "${CONF}" | grep -qE "$(_regex "${NGX_SSL_CRT}" "${prefix}")" \
+ || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_CRT}" "${prefix}")"
+ echo "${CONF}" | grep -qE "$(_regex "${NGX_SSL_KEY}" "${prefix}")" \
+ || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_KEY}" "${prefix}")"
+ echo "${CONF}" | grep -qE "^\s*ssl_session_cache\s" \
+ || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_SESSION_CACHE}" "${name}")"
+ echo "${CONF}" | grep -qE "^\s*ssl_session_timeout\s" \
+ || ADDS="${ADDS}${indent}$(_sed_rhs "${NGX_SSL_SESSION_TIMEOUT}" "")"
+
+ if [ -n "${ADDS}" ]
+ then
+ ADDS="$(echo "${ADDS}" | sed -E 's/^\\n//')"
+ echo "${CONF}" | grep -qE "$(_regex "${NGX_SERVER_NAME}" "${name}")" \
+ && echo "${CONF}" \
+ | sed -E "/$(_regex "${NGX_SERVER_NAME}" "${name}")/a\\${ADDS}" \
+ > "${prefix}.with" \
+ && _echo_sed "Added directives to ${prefix}.with:\n${ADDS}" \
+ && return 0 \
+ || _echo_sed "Cannot add directives to ${prefix}.sans, missing:\
+ \n$(_sed_rhs "${NGX_SERVER_NAME}" "${name}")\n${ADDS}"
+ return 1
+ fi
+ return 0
+}
+
+# ----------------------------------------------------------------------------
+
+test_setpoint() {
+ [ "$(cat "$1")" = "$2" ] && return
+ echo "$1:"; cat "$1"
+ echo "differs from setpoint:"; echo "$2"
+ [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1
+}
+
+
+test_existence() {
+ if [ "$2" -eq "0" ]
+ then
+ [ ! -f "$1" ] && echo "$1 missing!" &&
+ [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1
+ else
+ [ -f "$1" ] && echo "$1 existing!" &&
+ [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1
+ fi
+}
+
+
+test() {
+ eval "$1 2>/dev/null >/dev/null"
+ if [ "$?" -eq "$2" ]
+ then
+ [ "${PRINT_PASSED}" -gt 0 ] \
+ && printf "%-72s%-1s\n" "$1" "2>/dev/null >/dev/null (-> $2?) passed."
+ else
+ printf "%-72s%-1s\n" "$1" "2>/dev/null >/dev/null (-> $2?) failed!!!"
+ [ "${PRINT_PASSED}" -gt 0 ] && printf "\n### Snip:\n" && eval "$1"
+ [ "${PRINT_PASSED}" -gt 0 ] && printf "### Snap.\n"
+ [ "${PRINT_PASSED}" -gt 1 ] && pst_exit 1
+ fi
+}
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s get_env ...\n" "${NGINX_UTIL}"
+
+
+eval $("${NGINX_UTIL}" get_env)
+test '[ -n "${UCI_CONF}" ]' 0
+test '[ -n "${NGINX_CONF}" ]' 0
+test '[ -n "${CONF_DIR}" ]' 0
+test '[ -n "${LAN_NAME}" ]' 0
+test '[ -n "${LAN_LISTEN}" ]' 0
+test '[ -n "${LAN_SSL_LISTEN}" ]' 0
+test '[ -n "${SSL_SESSION_CACHE_ARG}" ]' 0
+test '[ -n "${SSL_SESSION_TIMEOUT_ARG}" ]' 0
+test '[ -n "${ADD_SSL_FCT}" ]' 0
+test '[ -n "${MANAGE_SSL}" ]' 0
+
+mkdir -p "$(dirname "${LAN_LISTEN}")"
+
+mkdir -p "${CONF_DIR}"
+
+cd "$(dirname "${CONF_DIR}")" && [ ! -e "$(basename "${CONF_DIR}")${ORIG}" ] &&
+mv "$(basename "${CONF_DIR}")" "$(basename "${CONF_DIR}")${ORIG}" ||
+{
+ printf "\n%s: not moved %s to %s\n" "$(dirname "${CONF_DIR}")" \
+ "$(basename "${CONF_DIR}")" "$(basename "${CONF_DIR}")${ORIG}"
+ pst_exit 3
+}
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nPrepare files in %s ...\n" "${CONF_DIR}"
+
+mkdir -p "${CONF_DIR}"
+
+cd "${CONF_DIR}" || pst_exit 2
+
+NGX_INCLUDE="include '\$';"
+NGX_SERVER_NAME="server_name * '\$' *;"
+NGX_SSL_CRT="ssl_certificate '\$.crt';"
+NGX_SSL_KEY="ssl_certificate_key '\$.key';"
+NGX_SSL_SESSION_CACHE="ssl_session_cache '$(echo "${SSL_SESSION_CACHE_ARG}" \
+ | sed -E "s/$(__esc_regex "${LAN_NAME}")/\$/")';"
+NGX_SSL_SESSION_TIMEOUT="ssl_session_timeout '${SSL_SESSION_TIMEOUT_ARG}';"
+
+cat > "${LAN_NAME}.sans" <<EOF
+# default_server for the LAN addresses getting the IPs by:
+# ifstatus lan | jsonfilter -e '@["ipv4-address","ipv6-address"].*.address'
+server {
+ include '${LAN_LISTEN}.default';
+ server_name ${LAN_NAME};
+ include conf.d/*.locations;
+}
+EOF
+CONFS="${CONFS} ${LAN_NAME}:0"
+
+cat > minimal.sans <<EOF
+server {
+ server_name minimal;
+}
+EOF
+CONFS="${CONFS} minimal:0"
+
+cat > listens.sans <<EOF
+server {
+ listen 80;
+ listen 81;
+ listen hostname:80;
+ listen hostname:81;
+ listen [::]:80;
+ listen [::]:81;
+ listen 1.3:80;
+# listen 1.3:80;
+ listen 1.3:81;
+ listen [1::3]:80;
+ listen [1::3]:81;
+ server_name listens;
+}
+EOF
+CONFS="${CONFS} listens:0"
+
+cat > normal.sans <<EOF
+server {
+ include '${LAN_LISTEN}';
+ server_name normal;
+}
+EOF
+CONFS="${CONFS} normal:0"
+
+cat > acme.sans <<EOF
+server {
+ listen 80;
+ include '${LAN_LISTEN}';
+ server_name acme;
+}
+EOF
+CONFS="${CONFS} acme:0"
+
+cat > more_server.sans <<EOF
+server {
+ # include '${LAN_LISTEN}';
+ server_name normal;
+}
+server {
+ include '${LAN_LISTEN}';
+ server_name more_server;
+}
+EOF
+CONFS="${CONFS} more_server:0"
+
+cat > more_names.sans <<EOF
+server {
+ include '${LAN_LISTEN}';
+ include '${LAN_LISTEN}';
+ include '${LAN_LISTEN}';
+ not include '${LAN_LISTEN}';
+ server_name example.com more_names example.org;
+}
+EOF
+CONFS="${CONFS} more_names:0"
+
+cat > different_name.sans <<EOF
+server {
+ include '${LAN_LISTEN}';
+ server_name minimal;
+}
+EOF
+CONFS="${CONFS} different_name:1"
+
+cat > comments.sans <<EOF
+server { # comment1
+ # comment2
+ include '${LAN_LISTEN}';
+ server_name comments;
+ # comment3
+} # comment4
+EOF
+CONFS="${CONFS} comments:0"
+
+cat > name_comment.sans <<EOF
+server {
+ include '${LAN_LISTEN}';
+ server_name name_comment; # comment
+}
+EOF
+CONFS="${CONFS} name_comment:0"
+
+cat > tab.sans <<EOF
+server {
+ include '${LAN_LISTEN}';
+ server_name tab;
+}
+EOF
+CONFS="${CONFS} tab:0"
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nSetup files in %s ...\n" "${CONF_DIR}"
+
+
+for conf in ${CONFS}
+do test 'setpoint_add_ssl " " '"${conf%:*}" "${conf#*:}"
+done
+
+test 'setpoint_add_ssl "\t" tab' 0 # fixes wrong indentation.
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting Cron ... \n"
+
+
+echo -n "prefix" >"/etc/crontabs/root"
+test '"${NGINX_UTIL}" add_ssl _lan' 0
+echo "postfix" >>"/etc/crontabs/root"
+test_setpoint "/etc/crontabs/root" "prefix
+3 3 12 12 * ${NGINX_UTIL} 'check_ssl'
+postfix"
+
+test '"${NGINX_UTIL}" del_ssl _lan' 0
+test_setpoint "/etc/crontabs/root" "prefix
+3 3 12 12 * ${NGINX_UTIL} 'check_ssl'
+postfix"
+
+test '"${NGINX_UTIL}" check_ssl' 0
+test_setpoint "/etc/crontabs/root" "prefix
+postfix"
+
+test '"${NGINX_UTIL}" add_ssl _lan' 0
+test_setpoint "/etc/crontabs/root" "prefix
+postfix
+3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+
+rm -f "/etc/crontabs/root"
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n'
+
+echo -n "prefix" >"/etc/crontabs/root"
+cp "minimal.sans" "minimal.conf"
+
+test '"${NGINX_UTIL}" add_ssl minimal' 0
+echo "postfix" >>"/etc/crontabs/root"
+test_setpoint "/etc/crontabs/root" "prefix
+3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'minimal'
+postfix"
+
+test '"${NGINX_UTIL}" del_ssl minimal' 0
+test_setpoint "/etc/crontabs/root" "prefix
+postfix"
+
+rm -f "/etc/crontabs/root"
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s init_lan ...\n" "${NGINX_UTIL}"
+
+
+rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf"
+rm -f "$(readlink "${UCI_CONF}")"
+
+test '"${NGINX_UTIL}" init_lan' 0
+test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)"
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\twith temporary UCI config ... \n'
+
+UCI_ADDED="$(uci add nginx server)" &&
+uci set nginx.@server[-1].server_name='temp' &&
+uci add_list nginx.@server[-1].listen='81 default_server' &&
+uci add_list nginx.@server[-1].listen='80' &&
+echo "UCI: nginx.${UCI_ADDED} added."
+
+rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf"
+rm -f "$(readlink "${UCI_CONF}")"
+
+test '"${NGINX_UTIL}" init_lan' 0
+test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)"
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n'
+
+cp "${LAN_NAME}.sans" "${LAN_NAME}.conf"
+touch "_redirect2ssl.conf" "${UCI_ADDED}.conf"
+rm -f "$(readlink "${UCI_CONF}")"
+test '"${NGINX_UTIL}" init_lan' 0
+
+skipped() {
+ printf "\t# skipped UCI server 'nginx.%s'" "$1"
+ printf " as it could conflict with: %s%s.conf\n\n" "${CONF_DIR}" "$1"
+}
+rhs="$(skipped "$LAN_NAME" && skipped _redirect2ssl && skipped "${UCI_ADDED}")"
+sed -E -e "s/^\t#UCI_HTTP_CONFIG$/$(__esc_sed_rhs "$rhs")\n/" \
+ -e 's/\\n/\n/g' -e "1i${fileauto}" "${UCI_CONF}.template" >"uci.setpoint"
+
+test_setpoint "${UCI_CONF}" "$(cat "uci.setpoint")"
+test_setpoint "/etc/crontabs/root" ""
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s add_ssl ...\n" "${NGINX_UTIL}"
+
+
+test '[ "${ADD_SSL_FCT}" = "add_ssl" ] ' 0
+
+rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf"
+rm -f "$(readlink "${UCI_CONF}")"
+test 'uci set nginx._lan.uci_manage_ssl="self-signed"' 0
+"${NGINX_UTIL}" del_ssl "${LAN_NAME}" 2>/dev/null
+test_setpoint "/etc/crontabs/root" ""
+test_existence "${LAN_NAME}.crt" 1
+test_existence "${LAN_NAME}.key" 1
+test '"${NGINX_UTIL}" add_ssl '"${UCI_ADDED}"' acme \
+ '"${CONF_DIR}${UCI_ADDED}.crt"' '"${CONF_DIR}${UCI_ADDED}.key"' ' 0
+test_setpoint "/etc/crontabs/root" ""
+test_existence "${UCI_ADDED}.crt" 1
+test_existence "${UCI_ADDED}.key" 1
+test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+test_existence "${LAN_NAME}.crt" 0
+test_existence "${LAN_NAME}.key" 0
+test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+test '"${NGINX_UTIL}" add_ssl inexistent' 1
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+test '"${NGINX_UTIL}" init_lan' 0
+test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)"
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'check_ssl'"
+test_existence "${UCI_ADDED}.crt" 1
+test_existence "${UCI_ADDED}.key" 1
+test_existence "${LAN_NAME}.crt" 0
+test_existence "${LAN_NAME}.key" 0
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n'
+
+cp different_name.sans different_name.with
+
+cp "/etc/crontabs/root" "cron.setpoint"
+for conf in ${CONFS}; do
+ name="${conf%:*}"
+ [ "${name}" = "acme" ] && continue
+ [ "${name}" = "different_name" ] ||
+ echo "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' '${name}'" >>"cron.setpoint"
+ cp "${name}.sans" "${name}.conf"
+ test '"${NGINX_UTIL}" add_ssl '"${name}" "${conf#*:}"
+ test_setpoint "${name}.conf" "$(cat "${name}.with")"
+ test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+ [ "${name}" = "different_name" ] || test_existence "${name}.crt" 0
+ [ "${name}" = "different_name" ] || test_existence "${name}.key" 0
+done
+
+cp acme.sans acme.conf
+test '"${NGINX_UTIL}" add_ssl acme acme /path/to/crt /path/to/key' 0
+test_setpoint "acme.conf" "$(cat "acme.with")"
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+test_existence "acme.crt" 1
+test_existence "acme.key" 1
+
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting %s del_ssl ...\n" "${NGINX_UTIL}"
+
+
+sed -E -e 's/443 ssl/80/' -e '/[^2]ssl/d' "/etc/config/nginx" >"config.setpoint"
+
+cp "/etc/crontabs/root" "cron.setpoint"
+rm -f "${LAN_NAME}.conf" "_redirect2ssl.conf" "${UCI_ADDED}.conf"
+test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+test_existence "${LAN_NAME}.crt" 1
+test_existence "${LAN_NAME}.key" 1
+test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 1
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+
+rm -f "$(readlink "${UCI_CONF}")"
+sed -E "/$(__esc_regex "'check_ssl'")/d" "/etc/crontabs/root" >"cron.setpoint"
+test '"${NGINX_UTIL}" init_lan' 0
+test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)"
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+
+touch "${UCI_ADDED}.crt" "${UCI_ADDED}.key"
+test '"${NGINX_UTIL}" del_ssl "'${UCI_ADDED}'" acme' 0
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+test_existence "${UCI_ADDED}.crt" 0
+test_existence "${UCI_ADDED}.key" 0
+
+test '"${NGINX_UTIL}" del_ssl inexistent' 1
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+
+test_setpoint "/etc/config/nginx" "$(cat "config.setpoint")"
+test '"${NGINX_UTIL}" add_ssl "'${UCI_ADDED}'" acme \
+ '"${CONF_DIR}${UCI_ADDED}.crt"' '"${CONF_DIR}${UCI_ADDED}.key"' ' 0
+test '"${NGINX_UTIL}" add_ssl "'$(uci get "nginx.${UCI_ADDED}.server_name")'"' 0
+test '"${NGINX_UTIL}" del_ssl "'$(uci get "nginx.${UCI_ADDED}.server_name")'"' 0
+rm -f "$(readlink "${UCI_CONF}")"
+sed -E "/$(__esc_regex "'check_ssl'")/d" "/etc/crontabs/root" >"cron.setpoint"
+test '"${NGINX_UTIL}" init_lan' 0
+test_setpoint "${UCI_CONF}" "$(setpoint_init_lan)"
+test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+test_existence "${UCI_ADDED}.crt" 1
+test_existence "${UCI_ADDED}.key" 1
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf '\n\t-"-\t(legacy) ... \n'
+
+for conf in ${CONFS}; do
+ name="${conf%:*}"
+ [ "${name}" = "acme" ] && continue
+ sed -E "/$(__esc_regex "'${name}'")/d" "/etc/crontabs/root" >"cron.setpoint"
+ touch "${name}.crt" "${name}.key"
+ cp "${name}.with" "${name}.conf"
+ test '"${NGINX_UTIL}" del_ssl '"${name}" "${conf#*:}"
+ test_setpoint "${name}.conf" "$(cat "${name}.sans")"
+ test_setpoint "/etc/crontabs/root" "$(cat "cron.setpoint")"
+ [ "${name}" = "different_name" ] && rm "${name}.crt" "${name}.key"
+ test_existence "${name}.crt" 1
+ test_existence "${name}.key" 1
+done
+test_setpoint "/etc/crontabs/root" ""
+
+test '"${NGINX_UTIL}" del_ssl acme acme' 0
+test_existence "acme.crt" 1
+test_existence "acme.key" 1
+
+cp acme.with acme.conf
+touch acme.crt acme.key
+echo "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'acme'" >>"/etc/crontabs/root"
+test '"${NGINX_UTIL}" del_ssl acme acme' 0
+test_setpoint "acme.conf" "$(cat "acme.sans")"
+test_setpoint "/etc/crontabs/root" "3 3 12 12 * ${NGINX_UTIL} 'add_ssl' 'acme'"
+test_existence "acme.crt" 0
+test_existence "acme.key" 0
+"${NGINX_UTIL}" del_ssl acme 2>/dev/null
+test_setpoint "/etc/crontabs/root" ""
+test_existence "acme.crt" 1
+test_existence "acme.key" 1
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting without UCI ... \n"
+
+rm -f "$(readlink "${UCI_CONF}")"
+
+test 'uci set nginx.global.uci_enable=0' 0
+
+test '"${NGINX_UTIL}" init_lan' 0
+
+test '[ -e "$(readlink '"${UCI_CONF}"')" ]' 1
+
+cp "${LAN_NAME}.sans" "${LAN_NAME}.conf"
+test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0
+test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 0
+test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0
+test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 0
+
+test 'rm "${LAN_NAME}.conf"' 0
+test '"${NGINX_UTIL}" add_ssl '"${LAN_NAME}" 1
+test '"${NGINX_UTIL}" del_ssl '"${LAN_NAME}" 1
+
+
+
+pst_exit 0
diff --git a/external/subpack/net/nginx-util/src/test-nginx-util.sh b/external/subpack/net/nginx-util/src/test-nginx-util.sh
new file mode 100755
index 0000000..6f8d072
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/test-nginx-util.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+printf "Initializing tests ...\n"
+
+fakechroot=""
+
+[ -x "/usr/bin/fakechroot" ] && fakechroot="/usr/bin/fakechroot" \
+|| [ "$(id -u)" -eq 0 ] || { \
+ printf "Error: Testing needs fakechroot or whoami=root for chroot."
+ return 1
+}
+
+TMPROOT="$(mktemp -d "/tmp/test-nginx-util-XXXXXX")"
+
+ln -s /bin "${TMPROOT}/bin"
+
+mkdir -p "${TMPROOT}/etc/crontabs/"
+
+mkdir -p "${TMPROOT}/etc/config/"
+cp "./config-nginx-ssl" "${TMPROOT}/etc/config/nginx"
+
+mkdir -p "${TMPROOT}/etc/nginx/"
+cp "./uci.conf.template" "${TMPROOT}/etc/nginx/uci.conf.template"
+ln -s "${TMPROOT}/var/lib/nginx/uci.conf" "${TMPROOT}/etc/nginx/uci.conf"
+
+mkdir -p "${TMPROOT}/usr/bin/"
+cp "/usr/local/bin/uci" "${TMPROOT}/usr/bin/"
+cp "./test-nginx-util-root.sh" "${TMPROOT}/usr/bin/"
+
+
+printf "\n\n******* Testing nginx-ssl-util-noubus *******\n"
+
+cp "./nginx-ssl-util-noubus" "${TMPROOT}/usr/bin/nginx-util"
+
+"${fakechroot}" /bin/chroot "${TMPROOT}" \
+ /bin/sh -c "/usr/bin/test-nginx-util-root.sh" ||
+{
+ echo "!!! Error: $?"
+ rm -r "${TMPROOT}"
+ exit 1
+}
+
+
+printf "\n\n******* Testing nginx-ssl-util-nopcre-noubus *******\n"
+
+cp "./nginx-ssl-util-nopcre-noubus" "${TMPROOT}/usr/bin/nginx-util"
+
+"${fakechroot}" /bin/chroot "${TMPROOT}" \
+ /bin/sh -c "/usr/bin/test-nginx-util-root.sh" ||
+{
+ echo "!!! Error: $?"
+ rm -r "${TMPROOT}"
+ exit 1
+}
+
+
+rm -r "${TMPROOT}"
diff --git a/external/subpack/net/nginx-util/src/test-px5g.sh b/external/subpack/net/nginx-util/src/test-px5g.sh
new file mode 100755
index 0000000..486b9ae
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/test-px5g.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+PRINT_PASSED=2
+
+printf "Initializing tests ...\n"
+
+OPENSSL_PEM="$(mktemp)"
+OPENSSL_DER="$(mktemp)"
+
+NONCE=$(dd if=/dev/urandom bs=1 count=4 2>/dev/null | hexdump -e '1/1 "%02x"')
+SUBJECT="/C=ZZ/ST=Somewhere/L=None/O=OpenWrt'$NONCE'/CN=OpenWrt"
+
+openssl req -x509 -nodes -days 1 -keyout /dev/null 2>/dev/null \
+ -out "$OPENSSL_PEM" -subj "$SUBJECT" \
+|| ( printf "error: generating PEM certificate with openssl"; return 1)
+openssl req -x509 -nodes -days 1 -keyout /dev/null 2>/dev/null \
+ -out "$OPENSSL_DER" -outform der -subj "$SUBJECT" \
+|| ( printf "error: generating DER certificate with openssl"; return 1)
+
+
+test() {
+ eval "$1 >/dev/null "
+ if [ $? -eq "$2" ]
+ then
+ [ "${PRINT_PASSED}" -gt 0 ] \
+ && printf "%-72s%-1s\n" "$1" ">/dev/null (-> $2?) passed."
+ else
+ printf "%-72s%-1s\n" "$1" ">/dev/null (-> $2?) failed!!!"
+ [ "${PRINT_PASSED}" -gt 1 ] && exit 1
+ fi
+}
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting openssl itself ...\n"
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * right PEM:\n"
+test 'cat "$OPENSSL_PEM" | openssl x509 -checkend 0 ' 0
+test 'cat "$OPENSSL_PEM" | openssl x509 -checkend 86300 ' 0
+test 'cat "$OPENSSL_PEM" | openssl x509 -checkend 86400 ' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * right DER:\n"
+test 'cat "$OPENSSL_DER" | openssl x509 -checkend 0 -inform der ' 0
+test 'cat "$OPENSSL_DER" | openssl x509 -checkend 86300 -inform der ' 0
+test 'cat "$OPENSSL_DER" | openssl x509 -checkend 86400 -inform der ' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * wrong:\n"
+test 'cat "$OPENSSL_PEM" | openssl x509 -checkend 0 -inform der 2>/dev/null' 1
+test 'cat "$OPENSSL_DER" | openssl x509 -checkend 0 2>/dev/null' 1
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting px5g checkend ...\n"
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * right PEM:\n"
+test 'cat "$OPENSSL_PEM" | ./px5g checkend 0 ' 0
+test 'cat "$OPENSSL_PEM" | ./px5g checkend 86300 ' 0
+test 'cat "$OPENSSL_PEM" | ./px5g checkend 86400 ' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * right DER:\n"
+test 'cat "$OPENSSL_DER" | ./px5g checkend -der 0 ' 0
+test 'cat "$OPENSSL_DER" | ./px5g checkend -der 86300 ' 0
+test 'cat "$OPENSSL_DER" | ./px5g checkend -der 86400 ' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * in option:\n"
+test 'cat "$OPENSSL_DER" | ./px5g checkend -in /proc/self/fd/0 -der 0 ' 0
+test 'cat "$OPENSSL_DER" | ./px5g checkend -der -in /proc/self/fd/0 99 ' 0
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * wrong:\n"
+test 'cat "$OPENSSL_PEM" | ./px5g checkend -der 0 2>/dev/null' 1
+test 'cat "$OPENSSL_DER" | ./px5g checkend 0 2>/dev/null' 1
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting px5g eckey ...\n"
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * standard curves:\n"
+test './px5g eckey P-256 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey P-384 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp384r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp256r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp256k1 | openssl ec -check 2>/dev/null' 0
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * more curves:\n"
+test './px5g eckey P-521 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp521r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp224r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp224k1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp192r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey secp192k1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey brainpoolP512r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey brainpoolP384r1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey brainpoolP256r1 | openssl ec -check 2>/dev/null' 0
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * other options:\n"
+test './px5g eckey -out /proc/self/fd/1 | openssl ec -check 2>/dev/null' 0
+test './px5g eckey -der | openssl ec -check -inform der 2>/dev/null' 0
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting px5g rsakey ...\n"
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * standard exponent:\n"
+test './px5g rsakey | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 512 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 1024 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 2048 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 4096 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 1111 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey 0 2>/dev/null' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * small exponent:\n"
+test './px5g rsakey -3 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 512 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 1024 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 2048 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 4096 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 1111 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -3 0 2>/dev/null' 1
+
+[ "$PRINT_PASSED" -gt 1 ] && printf " * other options:\n"
+test './px5g rsakey -out /proc/self/fd/1 | openssl rsa -check 2>/dev/null' 0
+test './px5g rsakey -der | openssl rsa -check -inform der 2>/dev/null' 0
+
+
+[ "$PRINT_PASSED" -gt 0 ] && printf "\nTesting px5g selfsigned ...\n"
+
+test './px5g selfsigned -der | openssl x509 -checkend 0 -inform der ' 0
+test './px5g selfsigned -days 1 | openssl x509 -checkend 0 ' 0
+test './px5g selfsigned -days 1 | openssl x509 -checkend 86300' 0
+test './px5g selfsigned -days 1 | openssl x509 -checkend 86400' 1
+test './px5g selfsigned -out /proc/self/fd/1 | openssl x509 -checkend 0 ' 0
+test './px5g selfsigned -newkey rsa:666 | openssl x509 -checkend 0 ' 0
+test './px5g selfsigned -newkey ec | openssl x509 -checkend 0 ' 0
+test './px5g selfsigned -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 \
+ | openssl x509 -checkend 0 ' 0
+test './px5g selfsigned -subj "$SUBJECT" | openssl x509 -noout \
+ -subject -nameopt compat | grep -q subject="$SUBJECT" 2>/dev/null' 0
+test './px5g selfsigned -out /dev/null -keyout /proc/self/fd/1 \
+ | openssl rsa -check 2>/dev/null ' 0
+
+
+rm "$OPENSSL_PEM" "$OPENSSL_DER"
diff --git a/external/subpack/net/nginx-util/src/ubus-cxx.cpp b/external/subpack/net/nginx-util/src/ubus-cxx.cpp
new file mode 100644
index 0000000..5fa29c7
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/ubus-cxx.cpp
@@ -0,0 +1,140 @@
+#include <iostream>
+
+#include "ubus-cxx.hpp"
+
+inline void example_for_checking_if_there_is_a_key()
+{
+ if (ubus::call("service", "list").filter("cron")) {
+ std::cout << "Cron is active (with or without instances) " << std::endl;
+ }
+}
+
+inline void example_for_getting_values()
+{
+ auto lan_status = ubus::call("network.interface.lan", "status");
+ for (const auto* t : lan_status.filter("ipv6-address", "", "address")) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto* x = const_cast<blob_attr*>(t);
+ std::cout << "[" << blobmsg_get_string(x) << "] ";
+ }
+ for (const auto* t : lan_status.filter("ipv4-address", "").filter("address")) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto* x = const_cast<blob_attr*>(t);
+ std::cout << blobmsg_get_string(x) << " ";
+ }
+ std::cout << std::endl;
+}
+
+inline void example_for_sending_message()
+{
+ auto set_arg = [](blob_buf* buf) -> int { return blobmsg_add_string(buf, "config", "nginx"); };
+ for (const auto* t : ubus::call("uci", "get", set_arg).filter("values")) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto* x = const_cast<blob_attr*>(t);
+ std::cout << blobmsg_get_string(x) << "\n";
+ }
+}
+
+inline void example_for_exploring()
+{
+ ubus::strings keys{"ipv4-address", "", ""};
+ for (const auto* t : ubus::call("network.interface.lan", "status").filter(keys)) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto* x = const_cast<blob_attr*>(t);
+ std::cout << blobmsg_name(x) << ": ";
+ switch (blob_id(x)) {
+ case BLOBMSG_TYPE_UNSPEC: std::cout << "[unspecified]"; break;
+ case BLOBMSG_TYPE_ARRAY: std::cout << "[array]"; break;
+ case BLOBMSG_TYPE_TABLE: std::cout << "[table]"; break;
+ case BLOBMSG_TYPE_STRING: std::cout << blobmsg_get_string(x); break;
+ case BLOBMSG_TYPE_INT64: std::cout << blobmsg_get_u64(x); break;
+ case BLOBMSG_TYPE_INT32: std::cout << blobmsg_get_u32(x); break;
+ case BLOBMSG_TYPE_INT16: std::cout << blobmsg_get_u16(x); break;
+ case BLOBMSG_TYPE_BOOL: std::cout << blobmsg_get_bool(x); break;
+ case BLOBMSG_TYPE_DOUBLE: std::cout << blobmsg_get_double(x); break;
+ default: std::cout << "[unknown]";
+ }
+ std::cout << std::endl;
+ }
+}
+
+inline void example_for_recursive_exploring()
+{ // output like from the original ubus call:
+ const auto explore = [](auto message) -> void {
+ auto end = message.end();
+ auto explore_internal = [&end](auto& explore_ref, auto it, size_t depth = 1) -> void {
+ std::cout << std::endl;
+ bool first = true;
+ for (; it != end; ++it) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+ auto* attr = const_cast<blob_attr*>(*it);
+ if (first) {
+ first = false;
+ }
+ else {
+ std::cout << ",\n";
+ }
+ std::cout << std::string(depth, '\t');
+ std::string name = blobmsg_name(attr);
+ if (!name.empty()) {
+ std::cout << "\"" << name << "\": ";
+ }
+ switch (blob_id(attr)) {
+ case BLOBMSG_TYPE_UNSPEC: std::cout << "(unspecified)"; break;
+ case BLOBMSG_TYPE_ARRAY:
+ std::cout << "[";
+ explore_ref(explore_ref, ubus::iterator{attr}, depth + 1);
+ std::cout << "\n" << std::string(depth, '\t') << "]";
+ break;
+ case BLOBMSG_TYPE_TABLE:
+ std::cout << "{";
+ explore_ref(explore_ref, ubus::iterator{attr}, depth + 1);
+ std::cout << "\n" << std::string(depth, '\t') << "}";
+ break;
+ case BLOBMSG_TYPE_STRING:
+ std::cout << "\"" << blobmsg_get_string(attr) << "\"";
+ break;
+ case BLOBMSG_TYPE_INT64: std::cout << blobmsg_get_u64(attr); break;
+ case BLOBMSG_TYPE_INT32: std::cout << blobmsg_get_u32(attr); break;
+ case BLOBMSG_TYPE_INT16: std::cout << blobmsg_get_u16(attr); break;
+ case BLOBMSG_TYPE_BOOL:
+ std::cout << (blobmsg_get_bool(attr) ? "true" : "false");
+ break;
+ case BLOBMSG_TYPE_DOUBLE: std::cout << blobmsg_get_double(attr); break;
+ default: std::cout << "(unknown)"; break;
+ }
+ }
+ };
+ std::cout << "{";
+ explore_internal(explore_internal, message.begin());
+ std::cout << "\n}" << std::endl;
+ };
+ explore(ubus::call("network.interface.lan", "status"));
+}
+
+auto main() -> int
+{
+ try {
+ example_for_checking_if_there_is_a_key();
+
+ example_for_getting_values();
+
+ example_for_sending_message();
+
+ example_for_exploring();
+
+ example_for_recursive_exploring();
+
+ return 0;
+ }
+
+ catch (const std::exception& e) {
+ std::cerr << e.what() << std::endl;
+ }
+
+ catch (...) {
+ perror("main error");
+ }
+
+ return 1;
+}
diff --git a/external/subpack/net/nginx-util/src/ubus-cxx.hpp b/external/subpack/net/nginx-util/src/ubus-cxx.hpp
new file mode 100644
index 0000000..6c193cf
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/ubus-cxx.hpp
@@ -0,0 +1,354 @@
+#ifndef _UBUS_CXX_HPP
+#define _UBUS_CXX_HPP
+
+#include <libubus.h>
+#include <cassert>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifndef NDEBUG
+#include <iostream>
+#endif
+
+namespace ubus {
+
+static constexpr int call_timeout = 500;
+
+using msg_ptr = std::shared_ptr<const blob_attr>;
+
+using strings = std::vector<std::string>;
+
+inline auto concat(strings dest)
+{
+ return dest;
+}
+
+template <class... Strings>
+inline auto concat(strings dest, strings src, Strings... more)
+{
+ dest.reserve(dest.size() + src.size());
+ dest.insert(std::end(dest), std::make_move_iterator(std::begin(src)),
+ std::make_move_iterator(std::end(src)));
+ return concat(std::move(dest), std::move(more)...);
+}
+
+template <class S, class... Strings>
+inline auto concat(strings dest, S src, Strings... more)
+{
+ dest.emplace_back(std::move(src));
+ return concat(std::move(dest), std::move(more)...);
+}
+
+class iterator {
+ private:
+ const strings& keys;
+
+ const size_t n = 0;
+
+ size_t i = 0;
+
+ const blob_attr* pos = nullptr;
+
+ std::unique_ptr<iterator> cur{};
+
+ iterator* parent = nullptr;
+
+ size_t rem = 0;
+
+ [[nodiscard]] inline auto matches() const -> bool
+ {
+ return (keys[i].empty() || blobmsg_name(cur->pos) == keys[i]);
+ }
+
+ explicit iterator(iterator* par)
+ : keys{par->keys}, n{par->n}, pos{par->pos}, cur{this}, parent{par}
+ {
+ if (pos != nullptr) {
+ rem = blobmsg_data_len(pos);
+ pos = static_cast<blob_attr*>(blobmsg_data(pos));
+ }
+ }
+
+ public:
+ explicit iterator(const blob_attr* msg, const strings& key_filter = {""})
+ : keys{key_filter}, n{keys.size() - 1}, pos{msg}, cur{this}
+ {
+ if (pos != nullptr) {
+ rem = blobmsg_data_len(pos);
+ pos = static_cast<blob_attr*>(blobmsg_data(pos));
+
+ if (rem == 0) {
+ pos = nullptr;
+ }
+ else if (i != n || !matches()) {
+ ++*this;
+ }
+ }
+ }
+
+ inline iterator(iterator&&) noexcept = default;
+
+ inline iterator(const iterator&) = delete;
+
+ inline auto operator=(const iterator&) -> iterator& = delete;
+
+ inline auto operator=(iterator &&) -> iterator& = delete;
+
+ inline auto operator*()
+ {
+ return cur->pos;
+ }
+
+ inline auto operator!=(const iterator& rhs)
+ {
+ return (cur->rem != rhs.cur->rem || cur->pos != rhs.cur->pos);
+ }
+
+ auto operator++() -> iterator&;
+
+ inline ~iterator()
+ {
+ if (cur.get() == this) {
+ static_cast<void>(cur.release());
+ }
+ }
+};
+
+class message {
+ private:
+ const msg_ptr msg{}; // initialized by callback.
+
+ const strings keys{};
+
+ public:
+ inline explicit message(msg_ptr message_ptr, strings key_filter = {""})
+ : msg{std::move(message_ptr)}, keys{std::move(key_filter)}
+ {}
+
+ inline message(message&&) = default;
+
+ inline message(const message&) = delete;
+
+ inline auto operator=(message &&) -> message& = delete;
+
+ inline auto operator=(const message&) -> message& = delete;
+
+ [[nodiscard]] inline auto begin() const -> iterator
+ {
+ return iterator{msg.get(), keys};
+ }
+
+ [[nodiscard]] inline auto end() const -> iterator
+ {
+ return iterator{nullptr, keys};
+ }
+
+ inline explicit operator bool() const
+ {
+ return begin() != end();
+ }
+
+ template <class... Strings>
+ auto filter(Strings... key_filter)
+ {
+ strings both{};
+ if (keys.size() != 1 || !keys[0].empty()) {
+ both = keys;
+ }
+ both = concat(std::move(both), std::move(key_filter)...);
+ return std::move(message{msg, std::move(both)});
+ }
+
+ inline ~message() = default;
+};
+
+class lock_shared_resources {
+ private:
+ static std::mutex inuse;
+
+ public:
+ inline lock_shared_resources()
+ {
+ inuse.lock();
+ }
+
+ inline lock_shared_resources(lock_shared_resources&&) noexcept = default;
+
+ inline lock_shared_resources(const lock_shared_resources&) = delete;
+
+ inline auto operator=(const lock_shared_resources&) -> auto& = delete;
+
+ inline auto operator=(lock_shared_resources &&) -> auto&& = delete;
+
+ // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+ inline auto get_context() -> ubus_context* // is member to enforce inuse.
+ {
+ static auto ubus_freeing = [](ubus_context* ctx) { ubus_free(ctx); };
+ static std::unique_ptr<ubus_context, decltype(ubus_freeing)> lazy_ctx{ubus_connect(nullptr),
+ ubus_freeing};
+
+ if (!lazy_ctx) { // it could be available on a later call:
+
+ lazy_ctx.reset(ubus_connect(nullptr));
+
+ if (!lazy_ctx) {
+ throw std::runtime_error("ubus error: cannot connect context");
+ }
+ }
+
+ return lazy_ctx.get();
+ }
+
+ // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+ inline auto get_blob_buf() -> blob_buf* // is member to enforce inuse.
+ {
+ static blob_buf buf;
+
+ static auto blob_buf_freeing = [](blob_buf* b) { blob_buf_free(b); };
+ static std::unique_ptr<blob_buf, decltype(blob_buf_freeing)>
+ created_to_free_on_the_end_of_life{&buf, blob_buf_freeing};
+
+ blob_buf_init(&buf, 0);
+
+ return &buf;
+ }
+
+ inline ~lock_shared_resources()
+ {
+ inuse.unlock();
+ }
+};
+
+template <class F>
+auto call(const char* path, const char* method, F set_arguments, int timeout = call_timeout)
+ -> message;
+
+inline auto call(const char* path, const char* method, int timeout = call_timeout) -> message
+{
+ return call(
+ path, method, [](blob_buf* /*buf*/) { return 0; }, timeout);
+}
+
+inline auto call(const char* path, int timeout = call_timeout) -> message
+{
+ return call(path, "", timeout);
+}
+
+// ------------------------- implementation: ----------------------------------
+
+std::mutex lock_shared_resources::inuse;
+
+inline auto iterator::operator++() -> iterator&
+{
+ for (;;) {
+#ifndef NDEBUG
+ std::cout << std::string(i, '>') << " look for " << keys[i] << " at ";
+ std::cout << blobmsg_name(cur->pos) << std::endl;
+#endif
+
+ auto id = blob_id(cur->pos);
+ if ((id == BLOBMSG_TYPE_TABLE || id == BLOBMSG_TYPE_ARRAY) && i < n && matches() &&
+ blobmsg_data_len(cur->pos) > 0)
+ { // immmerge:
+ ++i;
+
+ auto* tmp = cur.release();
+
+ struct new_iterator : public iterator // use private constructor:
+ {
+ explicit new_iterator(iterator* par) : iterator{par} {}
+ };
+ cur = std::make_unique<new_iterator>(tmp);
+ }
+ else {
+ while (true) {
+ cur->rem -= blob_pad_len(cur->pos);
+ cur->pos = blob_next(cur->pos);
+ auto len = blob_pad_len(cur->pos);
+
+ if (cur->rem > 0 && len <= cur->rem && len >= sizeof(blob_attr)) {
+ break;
+ }
+
+ // emerge:
+ auto* tmp = cur->parent;
+
+ if (tmp == nullptr) {
+ cur->pos = nullptr;
+ return *cur;
+ }
+
+ cur.reset(tmp);
+
+ --i;
+ }
+ }
+ if (i == n && matches()) {
+ return *cur;
+ }
+ }
+}
+
+template <class F>
+inline auto call(const char* path, const char* method, F set_arguments, int timeout) -> message
+{
+ auto shared = lock_shared_resources{};
+
+ auto* ctx = shared.get_context();
+
+ uint32_t id = 0;
+ int err = ubus_lookup_id(ctx, path, &id);
+
+ if (err == 0) { // call
+ ubus_request request{};
+
+ auto* buf = shared.get_blob_buf();
+ err = set_arguments(buf);
+ if (err == 0) {
+ err = ubus_invoke_async(ctx, id, method, buf->head, &request);
+ }
+
+ if (err == 0) {
+ msg_ptr message_ptr;
+
+ /* Cannot capture message_ptr, the lambda would be another type.
+ * Pass a location where to save the message as priv pointer when
+ * invoking and get it back here:
+ */
+ request.priv = &message_ptr;
+
+ request.data_cb = [](ubus_request* req, int /*type*/, blob_attr* msg) {
+ if (req == nullptr || msg == nullptr) {
+ return;
+ }
+
+ auto* saved = static_cast<msg_ptr*>(req->priv);
+ if (saved == nullptr || *saved) {
+ return;
+ }
+
+ saved->reset(blob_memdup(msg), free);
+ if (!*saved) {
+ throw std::bad_alloc();
+ }
+ };
+
+ err = ubus_complete_request(ctx, &request, timeout);
+
+ if (err == 0) {
+ return message{message_ptr};
+ }
+ }
+ }
+
+ std::string errmsg = "ubus::call error: cannot invoke";
+ errmsg += " (" + std::to_string(err) + ") " + path + " " + method;
+ throw std::runtime_error(errmsg);
+}
+
+} // namespace ubus
+
+#endif
diff --git a/external/subpack/net/nginx-util/src/uci-cxx.cpp b/external/subpack/net/nginx-util/src/uci-cxx.cpp
new file mode 100644
index 0000000..c880548
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/uci-cxx.cpp
@@ -0,0 +1,22 @@
+#include <iostream>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "uci-cxx.hpp"
+
+auto main() -> int
+{
+ uci::element p = uci::package{"nginx"};
+ std::cout << "package " << p.name() << "\n\n";
+ for (auto s : p) {
+ std::cout << "config " << s.type() << " '" << s.name() << "'\n";
+ for (auto o : s) {
+ for (auto i : o) {
+ std::cout << "\t" << o.type() << " " << o.name() << " '" << i.name() << "'\n";
+ }
+ }
+ std::cout << "\n";
+ }
+}
diff --git a/external/subpack/net/nginx-util/src/uci-cxx.hpp b/external/subpack/net/nginx-util/src/uci-cxx.hpp
new file mode 100644
index 0000000..4598890
--- /dev/null
+++ b/external/subpack/net/nginx-util/src/uci-cxx.hpp
@@ -0,0 +1,474 @@
+#ifndef _UCI_CXX_HPP
+#define _UCI_CXX_HPP
+
+#include <uci.h>
+#include <memory>
+#include <mutex>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+
+namespace uci {
+
+template <class T>
+class iterator { // like uci_foreach_element_safe.
+
+ private:
+ const uci_ptr& _ptr;
+
+ uci_element* _it = nullptr;
+
+ uci_element* _next = nullptr;
+
+ // wrapper for clang-tidy
+ inline auto _list_to_element(const uci_list* cur) -> uci_element*
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
+ return list_to_element(cur); // macro casting container=pointer-offset.
+ }
+
+ public:
+ inline explicit iterator(const uci_ptr& ptr, const uci_list* cur)
+ : _ptr{ptr}, _it{_list_to_element(cur)}
+ {
+ _next = _list_to_element(_it->list.next);
+ }
+
+ inline iterator(iterator&&) noexcept = default;
+
+ inline iterator(const iterator&) = delete;
+
+ inline auto operator=(const iterator&) -> iterator& = delete;
+
+ inline auto operator=(iterator &&) -> iterator& = delete;
+
+ auto operator*() -> T
+ {
+ return T{_ptr, _it};
+ }
+
+ inline auto operator!=(const iterator& rhs) -> bool
+ {
+ return (&_it->list != &rhs._it->list);
+ }
+
+ inline auto operator++() -> iterator&
+ {
+ _it = _next;
+ _next = _list_to_element(_next->list.next);
+ return *this;
+ }
+
+ inline ~iterator() = default;
+};
+
+class locked_context {
+ private:
+ static std::mutex inuse;
+
+ public:
+ inline locked_context()
+ {
+ inuse.lock();
+ }
+
+ inline locked_context(locked_context&&) noexcept = default;
+
+ inline locked_context(const locked_context&) = delete;
+
+ inline auto operator=(const locked_context&) -> locked_context& = delete;
+
+ inline auto operator=(locked_context &&) -> locked_context& = delete;
+
+ // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+ inline auto get() -> uci_context* // is member to enforce inuse
+ {
+ static auto free_ctx = [](uci_context* ctx) { uci_free_context(ctx); };
+ static std::unique_ptr<uci_context, decltype(free_ctx)> lazy_ctx{uci_alloc_context(),
+ free_ctx};
+
+ if (!lazy_ctx) { // it could be available on a later call:
+ lazy_ctx.reset(uci_alloc_context());
+ if (!lazy_ctx) {
+ throw std::runtime_error("uci error: cannot allocate context");
+ }
+ }
+
+ return lazy_ctx.get();
+ }
+
+ inline ~locked_context()
+ {
+ inuse.unlock();
+ }
+};
+
+template <class T>
+class element {
+ private:
+ uci_list* _begin = nullptr;
+
+ uci_list* _end = nullptr;
+
+ uci_ptr _ptr{};
+
+ protected:
+ [[nodiscard]] inline auto ptr() -> uci_ptr&
+ {
+ return _ptr;
+ }
+
+ [[nodiscard]] inline auto ptr() const -> const uci_ptr&
+ {
+ return _ptr;
+ }
+
+ void init_begin_end(uci_list* begin, uci_list* end)
+ {
+ _begin = begin;
+ _end = end;
+ }
+
+ inline explicit element(const uci_ptr& pre, uci_element* last) : _ptr{pre}
+ {
+ _ptr.last = last;
+ }
+
+ inline explicit element() = default;
+
+ public:
+ inline element(element&&) noexcept = default;
+
+ inline element(const element&) = delete;
+
+ inline auto operator=(const element&) -> element& = delete;
+
+ inline auto operator=(element &&) -> element& = delete;
+
+ auto operator[](std::string_view key) const -> T;
+
+ [[nodiscard]] inline auto name() const -> std::string
+ {
+ return _ptr.last->name;
+ }
+
+ void rename(const char* value) const;
+
+ void commit() const;
+
+ [[nodiscard]] inline auto begin() const -> iterator<T>
+ {
+ return iterator<T>{_ptr, _begin};
+ }
+
+ [[nodiscard]] inline auto end() const -> iterator<T>
+ {
+ return iterator<T>{_ptr, _end};
+ }
+
+ inline ~element() = default;
+};
+
+class section;
+
+class option;
+
+class item;
+
+class package : public element<section> {
+ public:
+ inline package(const uci_ptr& pre, uci_element* last) : element{pre, last}
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
+ ptr().p = uci_to_package(ptr().last); // macro casting pointer-offset.
+ ptr().package = ptr().last->name;
+
+ auto* end = &ptr().p->sections;
+ auto* begin = end->next;
+ init_begin_end(begin, end);
+ }
+
+ explicit package(const char* name);
+
+ auto set(const char* key, const char* type) const -> section;
+};
+
+class section : public element<option> {
+ public:
+ inline section(const uci_ptr& pre, uci_element* last) : element{pre, last}
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
+ ptr().s = uci_to_section(ptr().last); // macro casting pointer-offset.
+ ptr().section = ptr().last->name;
+
+ auto* end = &ptr().s->options;
+ auto* begin = end->next;
+ init_begin_end(begin, end);
+ }
+
+ auto set(const char* key, const char* value) const -> option;
+
+ void del();
+
+ [[nodiscard]] inline auto anonymous() const -> bool
+ {
+ return ptr().s->anonymous;
+ }
+
+ [[nodiscard]] inline auto type() const -> std::string
+ {
+ return ptr().s->type;
+ }
+};
+
+class option : public element<item> {
+ public:
+ inline option(const uci_ptr& pre, uci_element* last) : element{pre, last}
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast)
+ ptr().o = uci_to_option(ptr().last); // macro casting pointer-offset.
+ ptr().option = ptr().last->name;
+
+ if (ptr().o->type == UCI_TYPE_LIST) { // use union ptr().o->v as list:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
+ auto* end = &ptr().o->v.list;
+ auto* begin = end->next;
+ init_begin_end(begin, end);
+ }
+ else {
+ auto* begin = &ptr().last->list;
+ auto* end = begin->next;
+ init_begin_end(begin, end);
+ }
+ }
+
+ void del();
+
+ [[nodiscard]] inline auto type() const -> std::string
+ {
+ return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option");
+ }
+};
+
+class item : public element<item> {
+ public:
+ inline item(const uci_ptr& pre, uci_element* last) : element{pre, last}
+ {
+ ptr().value = ptr().last->name;
+ }
+
+ [[nodiscard]] inline auto type() const -> std::string
+ {
+ return (ptr().o->type == UCI_TYPE_LIST ? "list" : "option");
+ }
+
+ [[nodiscard]] inline auto name() const -> std::string
+ {
+ return (ptr().last->type == UCI_TYPE_ITEM
+ ? ptr().last->name
+ :
+ // else: use union ptr().o->v as string:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
+ ptr().o->v.string);
+ }
+
+ inline explicit operator bool() const
+ {
+ const auto x = std::string_view{name()};
+
+ if (x == "0" || x == "off" || x == "false" || x == "no" || x == "disabled") {
+ return false;
+ }
+
+ if (x == "1" || x == "on" || x == "true" || x == "yes" || x == "enabled") {
+ return true;
+ }
+
+ auto errmsg = std::string{"uci_error: item is not bool "} + name();
+ throw std::runtime_error(errmsg);
+ }
+
+ void rename(const char* value) const;
+};
+
+// ------------------------- implementation: ----------------------------------
+
+std::mutex locked_context::inuse{};
+
+inline auto uci_error(uci_context* ctx, const char* prefix = nullptr) -> std::runtime_error
+{
+ char* errmsg = nullptr;
+ uci_get_errorstr(ctx, &errmsg, prefix);
+
+ std::unique_ptr<char, decltype(&std::free)> auto_free{errmsg, std::free};
+ return std::runtime_error{errmsg};
+}
+
+template <class T>
+auto element<T>::operator[](std::string_view key) const -> T
+{
+ for (auto elmt : *this) {
+ if (elmt.name() == key) {
+ return elmt;
+ }
+ }
+
+ auto errmsg = std::string{"uci error: cannot find "}.append(key);
+ throw uci_error(locked_context{}.get(), errmsg.c_str());
+}
+
+template <class T>
+void element<T>::rename(const char* value) const
+{
+ if (value == name()) {
+ return;
+ }
+
+ auto ctx = locked_context{};
+ auto tmp_ptr = uci_ptr{_ptr};
+ tmp_ptr.value = value;
+ if (uci_rename(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot rename "}.append(name());
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+}
+
+template <class T>
+void element<T>::commit() const
+{
+ auto ctx = locked_context{};
+ // TODO(pst) use when possible:
+ // if (uci_commit(ctx.get(), &_ptr.p, true) != 0) {
+ // auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package;
+ // throw uci_error(ctx.get(), errmsg.c_str());
+ // }
+ auto err = uci_save(ctx.get(), _ptr.p);
+ if (err == 0) {
+ uci_package* tmp_pkg = nullptr;
+ uci_context* tmp_ctx = uci_alloc_context();
+ err = (tmp_ctx == nullptr ? 1 : 0);
+ if (err == 0) {
+ err = uci_load(tmp_ctx, _ptr.package, &tmp_pkg);
+ }
+ if (err == 0) {
+ err = uci_commit(tmp_ctx, &tmp_pkg, false);
+ }
+ if (err == 0) {
+ err = uci_unload(tmp_ctx, tmp_pkg);
+ }
+ if (tmp_ctx != nullptr) {
+ uci_free_context(tmp_ctx);
+ }
+ }
+
+ if (err != 0) {
+ auto errmsg = std::string{"uci error: cannot commit "} + _ptr.package;
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+}
+
+package::package(const char* name)
+{
+ auto ctx = locked_context{};
+
+ auto* pkg = uci_lookup_package(ctx.get(), name);
+ if (pkg == nullptr) {
+ if (uci_load(ctx.get(), name, &pkg) != 0) {
+ auto errmsg = std::string{"uci error: cannot load package "} + name;
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+ }
+
+ ptr().package = name;
+ ptr().p = pkg;
+ ptr().last = &pkg->e;
+
+ auto* end = &ptr().p->sections;
+ auto* begin = end->next;
+ init_begin_end(begin, end);
+}
+
+auto package::set(const char* key, const char* type) const -> section
+{
+ auto ctx = locked_context{};
+
+ auto tmp_ptr = uci_ptr{ptr()};
+ tmp_ptr.section = key;
+ tmp_ptr.value = type;
+ if (uci_set(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot set section "} + type + "'" + key +
+ "' in package " + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+
+ return section{ptr(), tmp_ptr.last};
+}
+
+auto section::set(const char* key, const char* value) const -> option
+{
+ auto ctx = locked_context{};
+
+ auto tmp_ptr = uci_ptr{ptr()};
+ tmp_ptr.option = key;
+ tmp_ptr.value = value;
+ if (uci_set(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot set option "} + key + "'" + value +
+ "' in package " + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+
+ return option{ptr(), tmp_ptr.last};
+}
+
+void section::del()
+{
+ auto ctx = locked_context{};
+ if (uci_delete(ctx.get(), &ptr()) != 0) {
+ auto errmsg = std::string{"uci error: cannot delete section "} + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+}
+
+void option::del()
+{
+ auto ctx = locked_context{};
+ if (uci_delete(ctx.get(), &ptr()) != 0) {
+ auto errmsg = std::string{"uci error: cannot delete option "} + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+}
+
+void item::rename(const char* value) const
+{
+ if (value == name()) {
+ return;
+ }
+
+ auto ctx = locked_context{};
+ auto tmp_ptr = uci_ptr{ptr()};
+
+ if (tmp_ptr.last->type != UCI_TYPE_ITEM) {
+ tmp_ptr.value = value;
+ if (uci_set(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot rename item "} + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+ return;
+ } // else:
+
+ tmp_ptr.value = tmp_ptr.last->name;
+ if (uci_del_list(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot rename (del) "} + name();
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+
+ tmp_ptr.value = value;
+ if (uci_add_list(ctx.get(), &tmp_ptr) != 0) {
+ auto errmsg = std::string{"uci error: cannot rename (add) "} + value;
+ throw uci_error(ctx.get(), errmsg.c_str());
+ }
+}
+
+} // namespace uci
+
+#endif