From 0781e8cf12f09fad71d0d63ce59c49c701b35d50 Mon Sep 17 00:00:00 2001
From: Marcel Waldvogel <marcel.waldvogel@trifence.ch>
Date: Mon, 12 Oct 2020 13:52:57 +0200
Subject: [PATCH 01/88] Use random hour for cron job

The hour for the cron job isn't really random (as is the minute),
but assuming acme.sh installation times are not correlated, neither
will be the resulting cron start times.
---
 acme.sh | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/acme.sh b/acme.sh
index 3be3849d..eb8aca49 100755
--- a/acme.sh
+++ b/acme.sh
@@ -5590,6 +5590,7 @@ installcronjob() {
   fi
   _t=$(_time)
   random_minute=$(_math $_t % 60)
+  random_hour=$(_math $_t / 60 % 24)
 
   if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
     _CRONTAB="fcrontab"
@@ -5616,12 +5617,12 @@ installcronjob() {
     if _exists uname && uname -a | grep SunOS >/dev/null; then
       $_CRONTAB -l | {
         cat
-        echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+        echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
       } | $_CRONTAB --
     else
       $_CRONTAB -l | {
         cat
-        echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+        echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
       } | $_CRONTAB -
     fi
   fi

From 92dbe6cdf8eb7d3b07b84876db79032c1d41f9c6 Mon Sep 17 00:00:00 2001
From: Marcel Waldvogel <marcel.waldvogel@trifence.ch>
Date: Mon, 12 Oct 2020 14:20:40 +0200
Subject: [PATCH 02/88] Simplify and clarify SunOS crontab differences

---
 acme.sh | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/acme.sh b/acme.sh
index eb8aca49..2ea5ccd7 100755
--- a/acme.sh
+++ b/acme.sh
@@ -5615,16 +5615,14 @@ installcronjob() {
   _info "Installing cron job"
   if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
     if _exists uname && uname -a | grep SunOS >/dev/null; then
-      $_CRONTAB -l | {
-        cat
-        echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
-      } | $_CRONTAB --
+      _CRONTAB_STDIN="$_CRONTAB --"
     else
-      $_CRONTAB -l | {
-        cat
-        echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
-      } | $_CRONTAB -
+      _CRONTAB_STDIN="$_CRONTAB -"
     fi
+    $_CRONTAB -l | {
+      cat
+      echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+    } | $_CRONTAB_STDIN
   fi
   if [ "$?" != "0" ]; then
     _err "Install cron job failed. You need to manually renew your certs."

From bcf63b5d270a4535347e64e7f25f24970e474e6f Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sun, 27 Feb 2022 14:17:34 +0100
Subject: [PATCH 03/88] Add ArtFiles.de DNS API plugin

---
 dnsapi/dns_artfiles.sh | 176 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 176 insertions(+)
 create mode 100644 dnsapi/dns_artfiles.sh

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
new file mode 100644
index 00000000..c857060a
--- /dev/null
+++ b/dnsapi/dns_artfiles.sh
@@ -0,0 +1,176 @@
+#!/usr/bin/env sh
+
+################################################################################
+# ACME.sh 3rd party DNS API plugin for ArtFiles.de
+################################################################################
+# Author:   Martin Arndt, https://troublezone.net/
+# Released: 2022-02-27
+# Issues:   https://github.com/acmesh-official/acme.sh/issues/XXXX
+################################################################################
+# Usage:
+# 1. export AF_API_Username="api12345678"
+# 2. export AF_API_Password="apiPassword"
+# 3. acme.sh --issue -d example.com --dns dns_artfiles
+################################################################################
+
+########## API configuration ###################################################
+AF_API_SUCCESS='status":"OK'
+AF_URL_DCP='https://dcp.c.artfiles.de/api/'
+AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
+AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html'
+
+########## Public functions ####################################################
+
+# Adds a new TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_add() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS addition API'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
+  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
+  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
+    _err 'Missing ArtFiles.de username and/or password.'
+    _err 'Please ensure both are set via export command & try again.'
+
+    return 1
+  fi
+
+  _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
+  _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
+
+  _set_headers
+  _get_zone "$domain"
+  _dns 'GET'
+  if ! _contains "$response" 'TXT'; then
+    _err 'Retrieving TXT records failed.'
+
+    return 1
+  fi
+
+  _clean_records
+  _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Adding ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+# Removes the existing TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_rm() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS removal API'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  _set_headers
+  _get_zone "$domain"
+  if ! _dns 'GET'; then
+    return 1
+  fi
+
+  if ! _contains "$response" "$txtValue"; then
+    _err 'Retrieved TXT records are missing given ACME challenge value.'
+
+    return 1
+  fi
+
+  _clean_records
+  response="$(printf -- '%s' "$response" | sed '$d')"
+  _dns 'SET' "$response"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Removing ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+########## Private functions ###################################################
+
+# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
+# Usage: _clean_records
+_clean_records()
+{
+  # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
+  # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
+  # from '\"' & turn '\n' into real LF characters.
+  # Yup, awful API to use - but that's all we got to get this working, so... ;)
+  _debug2 'Raw  ' "$response"
+  response="$(printf -- '%s' "$response"
+    \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
+  _debug2 'Clean' "$response"
+}
+
+# Executes an HTTP GET or POST request for getting or setting DNS records,
+# containing given payload upon POST.
+# Usage: _dns [GET | SET] [payload]
+_dns()
+{
+  action="$1"
+  payload="$(printf -- '%s' "$2" | _url_encode)"
+  url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain"
+    \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
+
+  if [ "$action" = 'SET' ]; then
+    _debug2 'Payload' "$payload"
+    response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
+  else
+    response="$(_get "$url" '' 10)"
+  fi
+
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err "DNS API error: $response"
+
+    return 1
+  fi
+
+  _debug 'Response' "$response"
+
+  return 0
+}
+
+# Gets the root domain zone for given domain.
+# Usage: _get_zone _acme-challenge.www.example.com
+_get_zone()
+{
+  _info 'Getting domain zone...'
+  _debug2 'Initial FQDN' "$1"
+  fqdn="$1"
+  fqdn="${fqdn#*.}" # Strip "_acme-challenge" right away
+  _debug2 'Reduced FQDN' "$fqdn"
+
+  domains="$(_get "$AF_URL_DOMAINS" '' 10)"
+  while true; do
+    if _contains "$domains" "$fqdn"; then
+      domain="$fqdn"
+      _info "Found root domain zone: $domain"
+      break
+    else
+      fqdn="${fqdn#*.}"
+      _debug2 'FQDN' "$fqdn"
+    fi
+  done
+
+  if [ "$domain" = "$fqdn" ]; then
+    return 0
+  fi
+
+  _err "Couldn't find root domain zone."
+
+  return 1
+}
+
+# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
+# Usage: _set_headers
+_set_headers()
+{
+  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+  export _H1="Authorization: Basic $encoded"
+  export _H2='Content-Type: application/json'
+}

From 72d02f442e2e8528afb04a0e55c88185bb99a7e9 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sun, 27 Feb 2022 14:35:21 +0100
Subject: [PATCH 04/88] Fix formatting according to Shellcheck

---
 dnsapi/dns_artfiles.sh | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index c857060a..4598854d 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -95,8 +95,7 @@ dns_artfiles_rm() {
 
 # Cleans awful TXT records response of ArtFiles's API & pretty prints it.
 # Usage: _clean_records
-_clean_records()
-{
+_clean_records() {
   # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
   # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
   # from '\"' & turn '\n' into real LF characters.
@@ -110,8 +109,7 @@ _clean_records()
 # Executes an HTTP GET or POST request for getting or setting DNS records,
 # containing given payload upon POST.
 # Usage: _dns [GET | SET] [payload]
-_dns()
-{
+_dns() {
   action="$1"
   payload="$(printf -- '%s' "$2" | _url_encode)"
   url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain"
@@ -137,8 +135,7 @@ _dns()
 
 # Gets the root domain zone for given domain.
 # Usage: _get_zone _acme-challenge.www.example.com
-_get_zone()
-{
+_get_zone() {
   _info 'Getting domain zone...'
   _debug2 'Initial FQDN' "$1"
   fqdn="$1"
@@ -168,8 +165,7 @@ _get_zone()
 
 # Adds the HTTP Authorization & Content-Type headers to a follow-up request.
 # Usage: _set_headers
-_set_headers()
-{
+_set_headers() {
   encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
   export _H1="Authorization: Basic $encoded"
   export _H2='Content-Type: application/json'

From 0bea2e2b94e49f59e05c7507bcec548c50a4abac Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sun, 27 Feb 2022 14:37:22 +0100
Subject: [PATCH 05/88] Fix formatting according to Shellcheck 2/2

---
 dnsapi/dns_artfiles.sh | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 4598854d..f83ce64a 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -101,8 +101,9 @@ _clean_records() {
   # from '\"' & turn '\n' into real LF characters.
   # Yup, awful API to use - but that's all we got to get this working, so... ;)
   _debug2 'Raw  ' "$response"
-  response="$(printf -- '%s' "$response"
-    \ | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
+  response="$(
+    printf -- '%s' "$response"
+    \  | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
   _debug2 'Clean' "$response"
 }
 
@@ -112,8 +113,9 @@ _clean_records() {
 _dns() {
   action="$1"
   payload="$(printf -- '%s' "$2" | _url_encode)"
-  url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain"
-    \ | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
+  url="$(
+    printf -- '%s%s' "$AF_URL_DNS" "$domain"
+    \  | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
 
   if [ "$action" = 'SET' ]; then
     _debug2 'Payload' "$payload"

From fb457968ecae380bc17914f22d134bcfba93d304 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sun, 27 Feb 2022 14:38:24 +0100
Subject: [PATCH 06/88] Fix formatting according to Shellcheck 3/3

---
 dnsapi/dns_artfiles.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index f83ce64a..9c1a4338 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -103,7 +103,8 @@ _clean_records() {
   _debug2 'Raw  ' "$response"
   response="$(
     printf -- '%s' "$response"
-    \  | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
+    \  | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g'
+  )"
   _debug2 'Clean' "$response"
 }
 
@@ -115,7 +116,8 @@ _dns() {
   payload="$(printf -- '%s' "$2" | _url_encode)"
   url="$(
     printf -- '%s%s' "$AF_URL_DNS" "$domain"
-    \  | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
+    \  | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/'
+  )"
 
   if [ "$action" = 'SET' ]; then
     _debug2 'Payload' "$payload"

From ed56d52af309a4d90747169a321dbd3698a4b71a Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sun, 27 Feb 2022 15:12:05 +0100
Subject: [PATCH 07/88] Changed GitHub issues URL

---
 dnsapi/dns_artfiles.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 9c1a4338..8e0a6151 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -5,7 +5,7 @@
 ################################################################################
 # Author:   Martin Arndt, https://troublezone.net/
 # Released: 2022-02-27
-# Issues:   https://github.com/acmesh-official/acme.sh/issues/XXXX
+# Issues:   https://github.com/Eagle3386/acme.sh/issues
 ################################################################################
 # Usage:
 # 1. export AF_API_Username="api12345678"

From 13c71829485aac03e1d0da98784ea50e983e73cd Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sat, 18 Jun 2022 17:32:56 +0200
Subject: [PATCH 08/88] Fix usage docs in file's header comment

---
 dnsapi/dns_artfiles.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 8e0a6151..2f8e158f 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -8,8 +8,8 @@
 # Issues:   https://github.com/Eagle3386/acme.sh/issues
 ################################################################################
 # Usage:
-# 1. export AF_API_Username="api12345678"
-# 2. export AF_API_Password="apiPassword"
+# 1. export AF_API_USERNAME="api12345678"
+# 2. export AF_API_PASSWORD="apiPassword"
 # 3. acme.sh --issue -d example.com --dns dns_artfiles
 ################################################################################
 

From cbb7082afd25419779152faf7d35b664a09030b3 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Fri, 31 Mar 2023 00:33:44 +0000
Subject: [PATCH 09/88] Fixed bug with wildcard certs and ecc keys

---
 deploy/panos.sh | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index ef622ded..3ee889b7 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -61,7 +61,7 @@ deployer() {
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
-      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
+      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cdomain.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
     fi
     #Close multipart
     content="$content${nl}--$delim--${nl}${nl}"
@@ -92,9 +92,18 @@ deployer() {
 
 # This is the main function that will call the other functions to deploy everything.
 panos_deploy() {
-  _cdomain="$1"
+  _cdomain=${1//[*]/WILDCARD_}  #Wildcard Safe filename
   _ckey="$2"
   _cfullchain="$5"
+  # VALID ECC KEY CHECK
+  if [[ "${_ckey: -8}" == "_ecc.key" ]] && [[ ! -f $_ckey ]]; then
+    _debug "The ECC key $_ckey doesn't exist. Attempting to strip _ecc from the filename"
+    _ckey="${_ckey:0:${#_ckey}-8}.key"
+    if [[ ! -f $_ckey ]]; then
+      _err "Still didn't work.  Try issuing the certificate using RSA (non-ECC) encryption."
+     return 1
+    fi
+  fi
   # PANOS ENV VAR check
   if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
     _debug "No ENV variables found lets check for saved variables"

From df753e2619f5e0069955ed07e32f6a340418126b Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Wed, 12 Apr 2023 22:00:53 +0000
Subject: [PATCH 10/88] Added functionality to save and reuse API key

---
 deploy/panos.sh | 34 ++++++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 3ee889b7..8edf115b 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -25,15 +25,27 @@ parse_response() {
   else
     status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
     message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
+    if  [ "$type" = 'testkey' ] && [ "$status" != "success" ]; then
+      _debug "**** Saved API key is invalid ****"
+      unset _panos_key
+    fi
   fi
   return 0
 }
 
 deployer() {
   content=""
-  type=$1 # Types are keygen, cert, key, commit
-  _debug "**** Deploying $type *****"
+  type=$1 # Types are testkey, keygen, cert, key, commit
+  _debug "**** Deploying $type ****"
   panos_url="https://$_panos_host/api/"
+  
+  #Test API Key by performing an empty commit.
+  if [ "$type" = 'testkey' ]; then
+    _H1="Content-Type: application/x-www-form-urlencoded"
+    content="type=commit&cmd=<commit></commit>&key=$_panos_key"
+  fi
+
+  # Generate API Key
   if [ "$type" = 'keygen' ]; then
     _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=keygen&user=$_panos_user&password=$_panos_pass"
@@ -134,8 +146,22 @@ panos_deploy() {
     _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
     return 1
   else
-    _debug "Getting PANOS KEY"
-    deployer keygen
+    #Check for saved API Key
+    _getdeployconf PANOS_KEY
+    _panos_key=$PANOS_KEY
+    if [ "$_panos_key" ]; then
+      _debug "**** Testing Saved API KEY ****"
+      deployer testkey
+    fi
+
+    # Generate a new API key if needed
+    if [ -z "$_panos_key" ]; then
+      _debug "**** Generating new PANOS API KEY ****"
+      deployer keygen
+      _savedeployconf PANOS_KEY "$_panos_key" 1
+    fi
+
+    # Recheck the key
     if [ -z "$_panos_key" ]; then
       _err "Missing apikey."
       return 1

From 7623025b9007386281d64275978d41a0c52a1bf3 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Mon, 24 Apr 2023 18:45:50 +0000
Subject: [PATCH 11/88] Fixes for POSIX sh shell

---
 deploy/panos.sh | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 8edf115b..7fb0c9db 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -10,6 +10,7 @@
 # export PANOS_USER=""  # required
 # export PANOS_PASS=""  # required
 # export PANOS_HOST=""  # required
+# export PANOS_KEY=""   # optional
 
 # This function is to parse the XML
 parse_response() {
@@ -25,7 +26,7 @@ parse_response() {
   else
     status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
     message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
-    if  [ "$type" = 'testkey' ] && [ "$status" != "success" ]; then
+    if [ "$type" = 'testkey' ] && [ "$status" != "success" ]; then
       _debug "**** Saved API key is invalid ****"
       unset _panos_key
     fi
@@ -38,7 +39,7 @@ deployer() {
   type=$1 # Types are testkey, keygen, cert, key, commit
   _debug "**** Deploying $type ****"
   panos_url="https://$_panos_host/api/"
-  
+
   #Test API Key by performing an empty commit.
   if [ "$type" = 'testkey' ]; then
     _H1="Content-Type: application/x-www-form-urlencoded"
@@ -104,16 +105,17 @@ deployer() {
 
 # This is the main function that will call the other functions to deploy everything.
 panos_deploy() {
-  _cdomain=${1//[*]/WILDCARD_}  #Wildcard Safe filename
+  _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _cfullchain="$5"
   # VALID ECC KEY CHECK
-  if [[ "${_ckey: -8}" == "_ecc.key" ]] && [[ ! -f $_ckey ]]; then
-    _debug "The ECC key $_ckey doesn't exist. Attempting to strip _ecc from the filename"
-    _ckey="${_ckey:0:${#_ckey}-8}.key"
-    if [[ ! -f $_ckey ]]; then
-      _err "Still didn't work.  Try issuing the certificate using RSA (non-ECC) encryption."
-     return 1
+  keysuffix=$(printf '%s' "$_ckey" | tail -c 8)
+  if [ "$keysuffix" = "_ecc.key" ] && [ ! -f "$_ckey" ]; then
+    _debug "The ECC key $_ckey doesn't exist. Attempting to strip '_ecc' from the key name"
+    _ckey=$(echo "$_ckey" | sed 's/\(.*\)_ecc.key$/\1.key/g')
+    if [ ! -f "$_ckey" ]; then
+      _err "Unable to find a valid key.  Try issuing the certificate using RSA (non-ECC) encryption."
+      return 1
     fi
   fi
   # PANOS ENV VAR check
@@ -122,9 +124,11 @@ panos_deploy() {
     _getdeployconf PANOS_USER
     _getdeployconf PANOS_PASS
     _getdeployconf PANOS_HOST
+    _getdeployconf PANOS_KEY
     _panos_user=$PANOS_USER
     _panos_pass=$PANOS_PASS
     _panos_host=$PANOS_HOST
+    _panos_key=$PANOS_KEY
     if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
       _err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
       return 1
@@ -140,28 +144,33 @@ panos_deploy() {
     _panos_user="$PANOS_USER"
     _panos_pass="$PANOS_PASS"
     _panos_host="$PANOS_HOST"
+    if [ "$PANOS_KEY" ]; then
+      _savedeployconf PANOS_KEY "$PANOS_KEY" 1
+      _panos_key="$PANOS_KEY"
+    else
+      _getdeployconf PANOS_KEY
+      _panos_key=$PANOS_KEY
+    fi
   fi
   _debug "Let's use username and pass to generate token."
   if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
     _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
     return 1
   else
-    #Check for saved API Key
-    _getdeployconf PANOS_KEY
-    _panos_key=$PANOS_KEY
+    #Test API Key
     if [ "$_panos_key" ]; then
       _debug "**** Testing Saved API KEY ****"
       deployer testkey
     fi
 
-    # Generate a new API key if needed
+    # Generate a new API key if no valid key exists
     if [ -z "$_panos_key" ]; then
       _debug "**** Generating new PANOS API KEY ****"
       deployer keygen
       _savedeployconf PANOS_KEY "$_panos_key" 1
     fi
 
-    # Recheck the key
+    # Confirm that a valid key was generated
     if [ -z "$_panos_key" ]; then
       _err "Missing apikey."
       return 1

From bb5f3cc32606507520f1705bb1a50334fc2796f5 Mon Sep 17 00:00:00 2001
From: Alexander Pushkarev <pa@mts.by>
Date: Mon, 1 May 2023 23:00:01 +0300
Subject: [PATCH 12/88] Add support for Mattermost notifications.

---
 notify/mattermost.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 notify/mattermost.sh

diff --git a/notify/mattermost.sh b/notify/mattermost.sh
new file mode 100644
index 00000000..9037ae0d
--- /dev/null
+++ b/notify/mattermost.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env sh
+
+# Support mattermost bots
+
+#MATTERMOST_API_URL=""
+#MATTERMOST_CHANNEL_ID=""
+#MATTERMOST_BOT_TOKEN=""
+
+mattermost_send() {
+  _subject="$1"
+  _content="$2"
+  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
+  _debug "_statusCode" "$_statusCode"
+
+  MATTERMOST_API_URL="${MATTERMOST_API_URL:-$(_readaccountconf_mutable MATTERMOST_API_URL)}"
+  if [ -z "$MATTERMOST_API_URL" ]; then
+    _err "You didn't specify a Mattermost API URL MATTERMOST_API_URL yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_API_URL "$MATTERMOST_API_URL"
+
+  MATTERMOST_CHANNEL_ID="${MATTERMOST_CHANNEL_ID:-$(_readaccountconf_mutable MATTERMOST_CHANNEL_ID)}"
+  if [ -z "$MATTERMOST_CHANNEL_ID" ]; then
+    _err "You didn't specify a Mattermost channel id MATTERMOST_CHANNEL_ID yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_CHANNEL_ID "$MATTERMOST_CHANNEL_ID"
+
+  MATTERMOST_BOT_TOKEN="${MATTERMOST_BOT_TOKEN:-$(_readaccountconf_mutable MATTERMOST_BOT_TOKEN)}"
+  if [ -z "$MATTERMOST_BOT_TOKEN" ]; then
+    _err "You didn't specify a Mattermost bot API token MATTERMOST_BOT_TOKEN yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_BOT_TOKEN "$MATTERMOST_BOT_TOKEN"
+
+  _content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
+  _data="{\"channel_id\": \"$MATTERMOST_CHANNEL_ID\", "
+  _data="$_data\"message\": \"$_content\"}"
+
+  export _H1="Authorization: Bearer $MATTERMOST_BOT_TOKEN"
+
+  if _post "$_data" "$MATTERMOST_API_URL" "" "POST" "application/json; charset=utf-8"; then
+    MATTERMOST_RESULT_OK=$(echo "$response" | _egrep_o 'create_at')
+    if [ "$?" = "0" ] && [ "$MATTERMOST_RESULT_OK" ]; then
+      _info "mattermost send success."
+      return 0
+    fi
+  fi
+  _err "mattermost send error."
+  _err "$response"
+  return 1
+}

From a8fba65cbd06d2bf1a25895a24c17f3c04f2dbca Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Mon, 15 May 2023 01:43:54 +0000
Subject: [PATCH 13/88] Cleaned up verbiage.  Added ability to store / update
 user variable.  Added ability to use user/pass OR key

---
 deploy/panos.sh | 140 ++++++++++++++++++++++++++++++------------------
 1 file changed, 88 insertions(+), 52 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 7fb0c9db..774060b0 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -7,10 +7,24 @@
 #
 # Firewall admin with superuser and IP address is required.
 #
-# export PANOS_USER=""  # required
-# export PANOS_PASS=""  # required
-# export PANOS_HOST=""  # required
-# export PANOS_KEY=""   # optional
+# You MUST include the following environment variable when first running
+# the sccript (can be deleted afterwards):
+#
+# REQURED:
+#     export PANOS_HOST=""  # required
+#
+# AND one of the two authenticiation methods:
+#
+# Method 1: Username & Password  (RECOMMENDED)
+#     export PANOS_USER=""
+#     export PANOS_PASS=""
+#
+# Method 2: API KEY
+#     export PANOS_KEY=""
+#
+#
+# The Username & Password method will automatically generate a new API key if
+# no key is found, or if a saved key has expired or is invalid.
 
 # This function is to parse the XML
 parse_response() {
@@ -26,8 +40,8 @@ parse_response() {
   else
     status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
     message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
-    if [ "$type" = 'testkey' ] && [ "$status" != "success" ]; then
-      _debug "**** Saved API key is invalid ****"
+    if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
+      _debug "****  API Key has EXPIRED or is INVALID ****"
       unset _panos_key
     fi
   fi
@@ -36,25 +50,27 @@ parse_response() {
 
 deployer() {
   content=""
-  type=$1 # Types are testkey, keygen, cert, key, commit
-  _debug "**** Deploying $type ****"
+  type=$1 # Types are keytest, keygen, cert, key, commit
   panos_url="https://$_panos_host/api/"
 
   #Test API Key by performing an empty commit.
-  if [ "$type" = 'testkey' ]; then
+  if [ "$type" = 'keytest' ]; then
+    _debug "**** Testing saved API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=commit&cmd=<commit></commit>&key=$_panos_key"
   fi
 
   # Generate API Key
   if [ "$type" = 'keygen' ]; then
+    _debug "**** Generating new API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=keygen&user=$_panos_user&password=$_panos_pass"
     # content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
   fi
 
   if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
-    #Generate DEIM
+    _debug "**** Deploying $type ****"
+    #Generate DELIM
     delim="-----MultipartDelimiter$(date "+%s%N")"
     nl="\015\012"
     #Set Header
@@ -83,8 +99,14 @@ deployer() {
   fi
 
   if [ "$type" = 'commit' ]; then
+    _debug "**** Committing changes ****"
     export _H1="Content-Type: application/x-www-form-urlencoded"
-    cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
+    if [ "$_panos_user" ]; then
+      _commit_desc=$_panos_user
+    else
+      _commit_desc="acmesh"
+    fi
+    cmd=$(printf "%s" "<commit><partial><$_commit_desc></$_commit_desc></partial></commit>" | _url_encode)
     content="type=commit&key=$_panos_key&cmd=$cmd"
   fi
   response=$(_post "$content" "$panos_url" "" "POST")
@@ -118,52 +140,66 @@ panos_deploy() {
       return 1
     fi
   fi
-  # PANOS ENV VAR check
-  if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
-    _debug "No ENV variables found lets check for saved variables"
-    _getdeployconf PANOS_USER
-    _getdeployconf PANOS_PASS
-    _getdeployconf PANOS_HOST
-    _getdeployconf PANOS_KEY
-    _panos_user=$PANOS_USER
-    _panos_pass=$PANOS_PASS
-    _panos_host=$PANOS_HOST
-    _panos_key=$PANOS_KEY
-    if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
-      _err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
-      return 1
-    else
-      _debug "Using saved env variables."
-    fi
+
+  # Environment Checks
+
+  # PANOS_HOST
+  if [ "$PANOS_HOST" ]; then
+    _debug "Detected ENV variable PANOS_HOST. Saving to file."
+    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
   else
-    _debug "Detected ENV variables to be saved to the deploy conf."
-    # Encrypt and save user
+    _debug "Attempting to load variable PANOS_HOST from file."
+    _getdeployconf PANOS_HOST
+  fi
+
+  # PANOS USER
+  if [ "$PANOS_USER" ]; then
+    _debug "Detected ENV variable PANOS_USER. Saving to file."
     _savedeployconf PANOS_USER "$PANOS_USER" 1
+  else
+    _debug "Attempting to load variable PANOS_USER from file."
+    _getdeployconf PANOS_USER
+  fi
+
+  # PANOS_KEY
+  if [ "$PANOS_PASS" ]; then
+    _debug "Detected ENV variable PANOS_PASS. Saving to file."
     _savedeployconf PANOS_PASS "$PANOS_PASS" 1
-    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
-    _panos_user="$PANOS_USER"
-    _panos_pass="$PANOS_PASS"
-    _panos_host="$PANOS_HOST"
-    if [ "$PANOS_KEY" ]; then
-      _savedeployconf PANOS_KEY "$PANOS_KEY" 1
-      _panos_key="$PANOS_KEY"
-    else
-      _getdeployconf PANOS_KEY
-      _panos_key=$PANOS_KEY
-    fi
+  else
+    _debug "Attempting to load variable PANOS_PASS from file."
+    _getdeployconf PANOS_PASS
   fi
-  _debug "Let's use username and pass to generate token."
-  if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
-    _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
-    return 1
+
+  # PANOS_KEY
+  if [ "$PANOS_KEY" ]; then
+    _debug "Detected ENV variable PANOS_KEY. Saving to file."
+    _savedeployconf PANOS_KEY "$PANOS_KEY" 1
   else
-    #Test API Key
-    if [ "$_panos_key" ]; then
-      _debug "**** Testing Saved API KEY ****"
-      deployer testkey
-    fi
+    _debug "Attempting to load variable PANOS_KEY from file."
+    _getdeployconf PANOS_KEY
+  fi
+
+  #Store variables
+  _panos_host=$PANOS_HOST
+  _panos_key=$PANOS_KEY
+  _panos_user=$PANOS_USER
+  _panos_pass=$PANOS_PASS
 
-    # Generate a new API key if no valid key exists
+  #Test API Key if found.  If the key is invalid, the variable panos_key will be unset.
+  if [ "$_panos_host" ] && [ "$_panos_key" ]; then
+    _debug "**** Testing API KEY ****"
+    deployer keytest
+  fi
+
+  # Check for valid variables
+  if [ -z "$_panos_host" ]; then
+    _err "No host found.  Please enter a valid host as environment variable PANOS_HOST."
+    return 1
+  elif [ -z "$_panos_key" ] && { [ -z "$_panos_user" ] || [ -z "$_panos_pass" ]; }; then
+    _err "No user and pass OR valid API key found.. If this is the first time deploying please set PANOS_USER and PANOS_PASS -- AND/OR -- PANOS_KEY in environment variables. Delete them after you have succesfully deployed certs."
+    return 1
+  else
+    # Generate a new API key if no valid API key is found
     if [ -z "$_panos_key" ]; then
       _debug "**** Generating new PANOS API KEY ****"
       deployer keygen
@@ -172,7 +208,7 @@ panos_deploy() {
 
     # Confirm that a valid key was generated
     if [ -z "$_panos_key" ]; then
-      _err "Missing apikey."
+      _err "Unable to generate an API key.  The user and pass may be invalid or not authorized to generate a new key.  Please check the credentials and try again"
       return 1
     else
       deployer cert

From 0ebc9f7a44f05f595084e55b83cff59b471b8d9f Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Mon, 15 May 2023 01:46:21 +0000
Subject: [PATCH 14/88] Fixed typo

---
 deploy/panos.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 774060b0..755ad5c9 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -161,7 +161,7 @@ panos_deploy() {
     _getdeployconf PANOS_USER
   fi
 
-  # PANOS_KEY
+  # PANOS_PASS
   if [ "$PANOS_PASS" ]; then
     _debug "Detected ENV variable PANOS_PASS. Saving to file."
     _savedeployconf PANOS_PASS "$PANOS_PASS" 1

From 2e2e7cd05408f8bf98016c5c0c9762608cd4e681 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Wed, 17 May 2023 20:06:06 +0000
Subject: [PATCH 15/88] Added ability to force commit to firewall.  Username is
 now also mandatory

---
 deploy/panos.sh | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 755ad5c9..2744ba6d 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -7,26 +7,24 @@
 #
 # Firewall admin with superuser and IP address is required.
 #
-# You MUST include the following environment variable when first running
-# the sccript (can be deleted afterwards):
-#
 # REQURED:
 #     export PANOS_HOST=""  # required
+#     export PANOS_USER=""  # required
 #
 # AND one of the two authenticiation methods:
 #
-# Method 1: Username & Password  (RECOMMENDED)
-#     export PANOS_USER=""
+# Method 1:  Password  (RECOMMENDED)
 #     export PANOS_PASS=""
 #
 # Method 2: API KEY
 #     export PANOS_KEY=""
 #
 #
-# The Username & Password method will automatically generate a new API key if
+# The Password method will automatically generate a new API key if
 # no key is found, or if a saved key has expired or is invalid.
+#
 
-# This function is to parse the XML
+# This function is to parse the XML response from the firewall
 parse_response() {
   type=$2
   if [ "$type" = 'keygen' ]; then
@@ -48,6 +46,7 @@ parse_response() {
   return 0
 }
 
+#This function is used to deploy to the firewall
 deployer() {
   content=""
   type=$1 # Types are keytest, keygen, cert, key, commit
@@ -68,6 +67,7 @@ deployer() {
     # content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
   fi
 
+  # Deploy Cert or Key
   if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
     _debug "**** Deploying $type ****"
     #Generate DELIM
@@ -98,17 +98,19 @@ deployer() {
     content=$(printf %b "$content")
   fi
 
+  # Commit changes
   if [ "$type" = 'commit' ]; then
     _debug "**** Committing changes ****"
     export _H1="Content-Type: application/x-www-form-urlencoded"
-    if [ "$_panos_user" ]; then
-      _commit_desc=$_panos_user
+    #Check for force commit
+    if [ "$FORCE" ]; then
+      cmd=$(printf "%s" "<commit><partial><force></force><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
     else
-      _commit_desc="acmesh"
+      cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
     fi
-    cmd=$(printf "%s" "<commit><partial><$_commit_desc></$_commit_desc></partial></commit>" | _url_encode)
     content="type=commit&key=$_panos_key&cmd=$cmd"
   fi
+
   response=$(_post "$content" "$panos_url" "" "POST")
   parse_response "$response" "$type"
   # Saving response to variables
@@ -141,8 +143,6 @@ panos_deploy() {
     fi
   fi
 
-  # Environment Checks
-
   # PANOS_HOST
   if [ "$PANOS_HOST" ]; then
     _debug "Detected ENV variable PANOS_HOST. Saving to file."
@@ -193,10 +193,13 @@ panos_deploy() {
 
   # Check for valid variables
   if [ -z "$_panos_host" ]; then
-    _err "No host found.  Please enter a valid host as environment variable PANOS_HOST."
+    _err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
+    return 1
+  elif [ -z "$_panos_user" ]; then
+    _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed certs."
     return 1
   elif [ -z "$_panos_key" ] && { [ -z "$_panos_user" ] || [ -z "$_panos_pass" ]; }; then
-    _err "No user and pass OR valid API key found.. If this is the first time deploying please set PANOS_USER and PANOS_PASS -- AND/OR -- PANOS_KEY in environment variables. Delete them after you have succesfully deployed certs."
+    _err "No pass OR valid API key found. If this is your first time deploying please set PANOS_PASS and/or PANOS_KEY in ENV variables. You can delete them after you have succesfully deployed certs."
     return 1
   else
     # Generate a new API key if no valid API key is found
@@ -208,7 +211,7 @@ panos_deploy() {
 
     # Confirm that a valid key was generated
     if [ -z "$_panos_key" ]; then
-      _err "Unable to generate an API key.  The user and pass may be invalid or not authorized to generate a new key.  Please check the credentials and try again"
+      _err "Unable to generate an API key.  The user and pass may be invalid or not authorized to generate a new key.  Please check the PANOS_USER and PANOS_PASS credentials and try again"
       return 1
     else
       deployer cert

From 126df9647b6ef11da3a7efe9897b453cf4e501d9 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Wed, 24 May 2023 18:51:57 +0000
Subject: [PATCH 16/88] Modified keytest to perform a partial empty commit

---
 deploy/panos.sh | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 2744ba6d..880e4cec 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -56,7 +56,7 @@ deployer() {
   if [ "$type" = 'keytest' ]; then
     _debug "**** Testing saved API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
-    content="type=commit&cmd=<commit></commit>&key=$_panos_key"
+    content="type=commit&key=$_panos_key&action=partial&cmd=<commit><partial><admin><member>acmekeytest</member></admin></partial></commit>"
   fi
 
   # Generate API Key
@@ -132,6 +132,7 @@ panos_deploy() {
   _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _cfullchain="$5"
+
   # VALID ECC KEY CHECK
   keysuffix=$(printf '%s' "$_ckey" | tail -c 8)
   if [ "$keysuffix" = "_ecc.key" ] && [ ! -f "$_ckey" ]; then
@@ -196,10 +197,10 @@ panos_deploy() {
     _err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
     return 1
   elif [ -z "$_panos_user" ]; then
-    _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed certs."
+    _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
     return 1
   elif [ -z "$_panos_key" ] && { [ -z "$_panos_user" ] || [ -z "$_panos_pass" ]; }; then
-    _err "No pass OR valid API key found. If this is your first time deploying please set PANOS_PASS and/or PANOS_KEY in ENV variables. You can delete them after you have succesfully deployed certs."
+    _err "No pass OR valid API key found. If this is your first time deploying please set PANOS_PASS and/or PANOS_KEY in ENV variables. You can delete them after you have succesfully deployed the certs."
     return 1
   else
     # Generate a new API key if no valid API key is found

From 63fca33b0459b1483c91c38a9ae3060bb6ef621a Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Mon, 29 May 2023 20:12:52 +0200
Subject: [PATCH 17/88] Fix retrieval of domain zone

---
 dnsapi/dns_artfiles.sh | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 2f8e158f..703c02ac 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -140,14 +140,13 @@ _dns() {
 # Gets the root domain zone for given domain.
 # Usage: _get_zone _acme-challenge.www.example.com
 _get_zone() {
-  _info 'Getting domain zone...'
-  _debug2 'Initial FQDN' "$1"
   fqdn="$1"
-  fqdn="${fqdn#*.}" # Strip "_acme-challenge" right away
-  _debug2 'Reduced FQDN' "$fqdn"
+  domains="$(_get "$AF_URL_DOMAINS" "" 10)"
+  _info 'Getting domain zone...'
+  _debug2 'FQDN' "$fqdn"
+  _debug2 'Domains' "$domains"
 
-  domains="$(_get "$AF_URL_DOMAINS" '' 10)"
-  while true; do
+  while _contains "$fqdn" "."; do
     if _contains "$domains" "$fqdn"; then
       domain="$fqdn"
       _info "Found root domain zone: $domain"
@@ -162,7 +161,7 @@ _get_zone() {
     return 0
   fi
 
-  _err "Couldn't find root domain zone."
+  _err 'Couldn\'t find root domain zone.'
 
   return 1
 }

From fb33ea2a0b6fc8ff260da123709b0872c6b91e79 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Mon, 29 May 2023 20:21:16 +0200
Subject: [PATCH 18/88] Fix single quote escaping

---
 dnsapi/dns_artfiles.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 703c02ac..ad547e3f 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -161,7 +161,7 @@ _get_zone() {
     return 0
   fi
 
-  _err 'Couldn\'t find root domain zone.'
+  _err 'Couldn'\''t find root domain zone.'
 
   return 1
 }

From d108072bfbc057f895d29620a8ecd6752ebe47f6 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Tue, 30 May 2023 09:24:17 +0200
Subject: [PATCH 19/88] Add ArtFiles.de DNS API plugin

---
 dnsapi/dns_artfiles.sh | 175 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 175 insertions(+)
 create mode 100644 dnsapi/dns_artfiles.sh

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
new file mode 100644
index 00000000..ad547e3f
--- /dev/null
+++ b/dnsapi/dns_artfiles.sh
@@ -0,0 +1,175 @@
+#!/usr/bin/env sh
+
+################################################################################
+# ACME.sh 3rd party DNS API plugin for ArtFiles.de
+################################################################################
+# Author:   Martin Arndt, https://troublezone.net/
+# Released: 2022-02-27
+# Issues:   https://github.com/Eagle3386/acme.sh/issues
+################################################################################
+# Usage:
+# 1. export AF_API_USERNAME="api12345678"
+# 2. export AF_API_PASSWORD="apiPassword"
+# 3. acme.sh --issue -d example.com --dns dns_artfiles
+################################################################################
+
+########## API configuration ###################################################
+AF_API_SUCCESS='status":"OK'
+AF_URL_DCP='https://dcp.c.artfiles.de/api/'
+AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
+AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html'
+
+########## Public functions ####################################################
+
+# Adds a new TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_add() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS addition API'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
+  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
+  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
+    _err 'Missing ArtFiles.de username and/or password.'
+    _err 'Please ensure both are set via export command & try again.'
+
+    return 1
+  fi
+
+  _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
+  _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
+
+  _set_headers
+  _get_zone "$domain"
+  _dns 'GET'
+  if ! _contains "$response" 'TXT'; then
+    _err 'Retrieving TXT records failed.'
+
+    return 1
+  fi
+
+  _clean_records
+  _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Adding ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+# Removes the existing TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_rm() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS removal API'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  _set_headers
+  _get_zone "$domain"
+  if ! _dns 'GET'; then
+    return 1
+  fi
+
+  if ! _contains "$response" "$txtValue"; then
+    _err 'Retrieved TXT records are missing given ACME challenge value.'
+
+    return 1
+  fi
+
+  _clean_records
+  response="$(printf -- '%s' "$response" | sed '$d')"
+  _dns 'SET' "$response"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Removing ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+########## Private functions ###################################################
+
+# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
+# Usage: _clean_records
+_clean_records() {
+  # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
+  # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
+  # from '\"' & turn '\n' into real LF characters.
+  # Yup, awful API to use - but that's all we got to get this working, so... ;)
+  _debug2 'Raw  ' "$response"
+  response="$(
+    printf -- '%s' "$response"
+    \  | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g'
+  )"
+  _debug2 'Clean' "$response"
+}
+
+# Executes an HTTP GET or POST request for getting or setting DNS records,
+# containing given payload upon POST.
+# Usage: _dns [GET | SET] [payload]
+_dns() {
+  action="$1"
+  payload="$(printf -- '%s' "$2" | _url_encode)"
+  url="$(
+    printf -- '%s%s' "$AF_URL_DNS" "$domain"
+    \  | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/'
+  )"
+
+  if [ "$action" = 'SET' ]; then
+    _debug2 'Payload' "$payload"
+    response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
+  else
+    response="$(_get "$url" '' 10)"
+  fi
+
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err "DNS API error: $response"
+
+    return 1
+  fi
+
+  _debug 'Response' "$response"
+
+  return 0
+}
+
+# Gets the root domain zone for given domain.
+# Usage: _get_zone _acme-challenge.www.example.com
+_get_zone() {
+  fqdn="$1"
+  domains="$(_get "$AF_URL_DOMAINS" "" 10)"
+  _info 'Getting domain zone...'
+  _debug2 'FQDN' "$fqdn"
+  _debug2 'Domains' "$domains"
+
+  while _contains "$fqdn" "."; do
+    if _contains "$domains" "$fqdn"; then
+      domain="$fqdn"
+      _info "Found root domain zone: $domain"
+      break
+    else
+      fqdn="${fqdn#*.}"
+      _debug2 'FQDN' "$fqdn"
+    fi
+  done
+
+  if [ "$domain" = "$fqdn" ]; then
+    return 0
+  fi
+
+  _err 'Couldn'\''t find root domain zone.'
+
+  return 1
+}
+
+# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
+# Usage: _set_headers
+_set_headers() {
+  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+  export _H1="Authorization: Basic $encoded"
+  export _H2='Content-Type: application/json'
+}

From db8a2d0c653ba19df024c8289942166d7e5bb025 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Wed, 5 Jul 2023 11:05:06 +0200
Subject: [PATCH 20/88] Fix & improve DNS API for ArtFiles.de

---
 dnsapi/dns_artfiles.sh | 80 ++++++++++++++++++++++++------------------
 1 file changed, 46 insertions(+), 34 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index ad547e3f..6224c416 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -5,40 +5,34 @@
 ################################################################################
 # Author:   Martin Arndt, https://troublezone.net/
 # Released: 2022-02-27
-# Issues:   https://github.com/Eagle3386/acme.sh/issues
+# Issues:    https://github.com/acmesh-official/acme.sh/issues/XXXX
 ################################################################################
 # Usage:
-# 1. export AF_API_USERNAME="api12345678"
-# 2. export AF_API_PASSWORD="apiPassword"
+# 1. export AF_API_USERNAME='api12345678'
+# 2. export AF_API_PASSWORD='apiPassword'
 # 3. acme.sh --issue -d example.com --dns dns_artfiles
 ################################################################################
 
 ########## API configuration ###################################################
+
 AF_API_SUCCESS='status":"OK'
 AF_URL_DCP='https://dcp.c.artfiles.de/api/'
 AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
-AF_URL_DOMAINS=${AF_URL_BASE}'domain/get_domains.html'
+AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
 
 ########## Public functions ####################################################
 
 # Adds a new TXT record for given ACME challenge value & domain.
 # Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
-dns_artfiles_add() {
+dns_artfiles_add()
+{
   domain="$1"
   txtValue="$2"
-  _info 'Using ArtFiles.de DNS addition API'
+  _info 'Using ArtFiles.de DNS addition API…'
   _debug 'Domain' "$domain"
   _debug 'txtValue' "$txtValue"
 
-  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
-  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
-  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
-    _err 'Missing ArtFiles.de username and/or password.'
-    _err 'Please ensure both are set via export command & try again.'
-
-    return 1
-  fi
-
+  _set_credentials
   _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
   _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
 
@@ -62,13 +56,15 @@ dns_artfiles_add() {
 
 # Removes the existing TXT record for given ACME challenge value & domain.
 # Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
-dns_artfiles_rm() {
+dns_artfiles_rm()
+{
   domain="$1"
   txtValue="$2"
-  _info 'Using ArtFiles.de DNS removal API'
+  _info 'Using ArtFiles.de DNS removal API…'
   _debug 'Domain' "$domain"
   _debug 'txtValue' "$txtValue"
 
+  _set_credentials
   _set_headers
   _get_zone "$domain"
   if ! _dns 'GET'; then
@@ -95,29 +91,28 @@ dns_artfiles_rm() {
 
 # Cleans awful TXT records response of ArtFiles's API & pretty prints it.
 # Usage: _clean_records
-_clean_records() {
+_clean_records()
+{
+  _info 'Cleaning TXT records…'
   # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
   # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
   # from '\"' & turn '\n' into real LF characters.
-  # Yup, awful API to use - but that's all we got to get this working, so... ;)
+  # Yup, awful API to use - but that's all we got to get this working, so… ;)
   _debug2 'Raw  ' "$response"
-  response="$(
-    printf -- '%s' "$response"
-    \  | sed 's/^\(.*TXT":"\)\([^,}]*\)\(.*\)$/\2/;s/.$//;s/\\"/"/g;s/\\n/\n/g'
-  )"
+                                                                                                             
+  response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
   _debug2 'Clean' "$response"
 }
 
 # Executes an HTTP GET or POST request for getting or setting DNS records,
 # containing given payload upon POST.
 # Usage: _dns [GET | SET] [payload]
-_dns() {
+_dns()
+{
+  _info 'Executing HTTP request…'
   action="$1"
   payload="$(printf -- '%s' "$2" | _url_encode)"
-  url="$(
-    printf -- '%s%s' "$AF_URL_DNS" "$domain"
-    \  | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/'
-  )"
+  url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
 
   if [ "$action" = 'SET' ]; then
     _debug2 'Payload' "$payload"
@@ -139,10 +134,11 @@ _dns() {
 
 # Gets the root domain zone for given domain.
 # Usage: _get_zone _acme-challenge.www.example.com
-_get_zone() {
+_get_zone()
+{
   fqdn="$1"
-  domains="$(_get "$AF_URL_DOMAINS" "" 10)"
-  _info 'Getting domain zone...'
+  domains="$(_get "$AF_URL_DOMAINS" '' 10)"
+  _info 'Getting domain zone…'
   _debug2 'FQDN' "$fqdn"
   _debug2 'Domains' "$domains"
 
@@ -166,10 +162,26 @@ _get_zone() {
   return 1
 }
 
+# Sets the credentials for accessing ArtFiles's API
+# Usage: _set_credentials
+_set_credentials()
+{
+  _info 'Setting credentials…'
+  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
+  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
+  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
+    _err 'Missing ArtFiles.de username and/or password.'
+    _err 'Please ensure both are set via export command & try again.'
+
+    return 1
+  fi
+}
+
 # Adds the HTTP Authorization & Content-Type headers to a follow-up request.
 # Usage: _set_headers
-_set_headers() {
-  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
-  export _H1="Authorization: Basic $encoded"
+_set_headers()
+{
+  _info 'Setting headers…'
+  export _H1="$(printf -- 'Authorization: Basic %s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
   export _H2='Content-Type: application/json'
 }

From 2961a90e7ffb37ad498a8d841d00dbb2a08d251c Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Wed, 5 Jul 2023 11:14:49 +0200
Subject: [PATCH 21/88] Make ShellCheck & ShellFormat happy

---
 dnsapi/dns_artfiles.sh | 25 +++++++++----------------
 1 file changed, 9 insertions(+), 16 deletions(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 6224c416..04cdf026 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -24,8 +24,7 @@ AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
 
 # Adds a new TXT record for given ACME challenge value & domain.
 # Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
-dns_artfiles_add()
-{
+dns_artfiles_add() {
   domain="$1"
   txtValue="$2"
   _info 'Using ArtFiles.de DNS addition API…'
@@ -56,8 +55,7 @@ dns_artfiles_add()
 
 # Removes the existing TXT record for given ACME challenge value & domain.
 # Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
-dns_artfiles_rm()
-{
+dns_artfiles_rm() {
   domain="$1"
   txtValue="$2"
   _info 'Using ArtFiles.de DNS removal API…'
@@ -91,15 +89,13 @@ dns_artfiles_rm()
 
 # Cleans awful TXT records response of ArtFiles's API & pretty prints it.
 # Usage: _clean_records
-_clean_records()
-{
+_clean_records() {
   _info 'Cleaning TXT records…'
   # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
   # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
   # from '\"' & turn '\n' into real LF characters.
   # Yup, awful API to use - but that's all we got to get this working, so… ;)
   _debug2 'Raw  ' "$response"
-                                                                                                             
   response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
   _debug2 'Clean' "$response"
 }
@@ -107,8 +103,7 @@ _clean_records()
 # Executes an HTTP GET or POST request for getting or setting DNS records,
 # containing given payload upon POST.
 # Usage: _dns [GET | SET] [payload]
-_dns()
-{
+_dns() {
   _info 'Executing HTTP request…'
   action="$1"
   payload="$(printf -- '%s' "$2" | _url_encode)"
@@ -134,8 +129,7 @@ _dns()
 
 # Gets the root domain zone for given domain.
 # Usage: _get_zone _acme-challenge.www.example.com
-_get_zone()
-{
+_get_zone() {
   fqdn="$1"
   domains="$(_get "$AF_URL_DOMAINS" '' 10)"
   _info 'Getting domain zone…'
@@ -164,8 +158,7 @@ _get_zone()
 
 # Sets the credentials for accessing ArtFiles's API
 # Usage: _set_credentials
-_set_credentials()
-{
+_set_credentials() {
   _info 'Setting credentials…'
   AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
   AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
@@ -179,9 +172,9 @@ _set_credentials()
 
 # Adds the HTTP Authorization & Content-Type headers to a follow-up request.
 # Usage: _set_headers
-_set_headers()
-{
+_set_headers() {
   _info 'Setting headers…'
-  export _H1="$(printf -- 'Authorization: Basic %s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+  export _H1="Authorization: Basic $encoded"
   export _H2='Content-Type: application/json'
 }

From 8b3acb719eb6eee82ef40edff94ed2372ecdc1df Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Wed, 5 Jul 2023 13:04:08 +0200
Subject: [PATCH 22/88] Fix TXT record removal

---
 dnsapi/dns_artfiles.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index 04cdf026..faf8c2ae 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -76,7 +76,7 @@ dns_artfiles_rm() {
   fi
 
   _clean_records
-  response="$(printf -- '%s' "$response" | sed '$d')"
+  response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
   _dns 'SET' "$response"
   if ! _contains "$response" "$AF_API_SUCCESS"; then
     _err 'Removing ACME challenge value failed.'

From d86414febbaeeefe1ef563ada1882051ccd6c758 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 11 Jul 2023 23:41:24 +0000
Subject: [PATCH 23/88] Excluded scopes for api key test

---
 deploy/panos.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 880e4cec..2bb9f08f 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -56,7 +56,9 @@ deployer() {
   if [ "$type" = 'keytest' ]; then
     _debug "**** Testing saved API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
-    content="type=commit&key=$_panos_key&action=partial&cmd=<commit><partial><admin><member>acmekeytest</member></admin></partial></commit>"
+    #Exclude all scopes for the empty commit
+    _exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
+    content="type=commit&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
   fi
 
   # Generate API Key

From e69a19db5c555c8a23fb31e1be393d0f220341fa Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 11 Jul 2023 23:56:41 +0000
Subject: [PATCH 24/88] Incorporated partial commit to address issue #4198

---
 deploy/panos.sh | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 2bb9f08f..f18482fc 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -36,8 +36,9 @@ parse_response() {
       message="PAN-OS Key could not be set."
     fi
   else
-    status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
-    message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
+    status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
+    message=$(echo "$1" | tr -d '\n' | sed 's/.*\(<result>\|<msg>\|<line>\)\([^<]*\).*/\2/g')
+    _debug "Firewall message:  $message"
     if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
       _debug "****  API Key has EXPIRED or is INVALID ****"
       unset _panos_key
@@ -58,7 +59,7 @@ deployer() {
     _H1="Content-Type: application/x-www-form-urlencoded"
     #Exclude all scopes for the empty commit
     _exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
-    content="type=commit&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
+    content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
   fi
 
   # Generate API Key
@@ -104,20 +105,21 @@ deployer() {
   if [ "$type" = 'commit' ]; then
     _debug "**** Committing changes ****"
     export _H1="Content-Type: application/x-www-form-urlencoded"
-    #Check for force commit
+    #Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
     if [ "$FORCE" ]; then
-      cmd=$(printf "%s" "<commit><partial><force></force><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
+      _debug "Force switch detected.  Committing ALL changes to the firewall."
+      cmd=$(printf "%s" "<commit><partial><force><admin><member>$_panos_user</member></admin></force></partial></commit>" | _url_encode)
     else
-      cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
+      _exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network>"
+      cmd=$(printf "%s" "<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
     fi
-    content="type=commit&key=$_panos_key&cmd=$cmd"
+    content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
   fi
 
   response=$(_post "$content" "$panos_url" "" "POST")
   parse_response "$response" "$type"
   # Saving response to variables
   response_status=$status
-  #DEBUG
   _debug response_status "$response_status"
   if [ "$response_status" = "success" ]; then
     _debug "Successfully deployed $type"

From b556908cab48cf71ad577f912693316eea1a7078 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Wed, 12 Jul 2023 00:03:21 +0000
Subject: [PATCH 25/88] Modified ECC file test

---
 deploy/panos.sh | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index f18482fc..e11a6eed 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -137,15 +137,10 @@ panos_deploy() {
   _ckey="$2"
   _cfullchain="$5"
 
-  # VALID ECC KEY CHECK
-  keysuffix=$(printf '%s' "$_ckey" | tail -c 8)
-  if [ "$keysuffix" = "_ecc.key" ] && [ ! -f "$_ckey" ]; then
-    _debug "The ECC key $_ckey doesn't exist. Attempting to strip '_ecc' from the key name"
-    _ckey=$(echo "$_ckey" | sed 's/\(.*\)_ecc.key$/\1.key/g')
-    if [ ! -f "$_ckey" ]; then
-      _err "Unable to find a valid key.  Try issuing the certificate using RSA (non-ECC) encryption."
-      return 1
-    fi
+  # VALID FILE CHECK
+  if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
+    _err "Unable to find a valid key and/or cert.  If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
+    return 1
   fi
 
   # PANOS_HOST

From edd1b60c3d39269dc1c5ff15ea842f1a0d3624f2 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 18 Jul 2023 19:43:47 +0000
Subject: [PATCH 26/88] Removed ability to specify API key to facilitate future
 multiple host functionality.

---
 deploy/panos.sh | 47 ++++++++++++++++++-----------------------------
 1 file changed, 18 insertions(+), 29 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index e11a6eed..27919a25 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -8,21 +8,13 @@
 # Firewall admin with superuser and IP address is required.
 #
 # REQURED:
-#     export PANOS_HOST=""  # required
-#     export PANOS_USER=""  # required
-#
-# AND one of the two authenticiation methods:
-#
-# Method 1:  Password  (RECOMMENDED)
+#     export PANOS_HOST=""
+#     export PANOS_USER=""    #User *MUST* have Commit and Import Permissions in XML API for Admin Role
 #     export PANOS_PASS=""
 #
-# Method 2: API KEY
-#     export PANOS_KEY=""
-#
-#
-# The Password method will automatically generate a new API key if
+# The script will automatically generate a new API key if
 # no key is found, or if a saved key has expired or is invalid.
-#
+
 
 # This function is to parse the XML response from the firewall
 parse_response() {
@@ -53,13 +45,15 @@ deployer() {
   type=$1 # Types are keytest, keygen, cert, key, commit
   panos_url="https://$_panos_host/api/"
 
-  #Test API Key by performing an empty commit.
+  #Test API Key by performing a lookup
   if [ "$type" = 'keytest' ]; then
     _debug "**** Testing saved API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
-    #Exclude all scopes for the empty commit
-    _exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
-    content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
+    # Get Version Info to test key
+    content="type=version&key=$_panos_key"
+    ## Exclude all scopes for the empty commit
+    #_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
+    #content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
   fi
 
   # Generate API Key
@@ -170,22 +164,17 @@ panos_deploy() {
     _getdeployconf PANOS_PASS
   fi
 
-  # PANOS_KEY
-  if [ "$PANOS_KEY" ]; then
-    _debug "Detected ENV variable PANOS_KEY. Saving to file."
-    _savedeployconf PANOS_KEY "$PANOS_KEY" 1
-  else
-    _debug "Attempting to load variable PANOS_KEY from file."
-    _getdeployconf PANOS_KEY
-  fi
-
   #Store variables
   _panos_host=$PANOS_HOST
-  _panos_key=$PANOS_KEY
   _panos_user=$PANOS_USER
   _panos_pass=$PANOS_PASS
 
-  #Test API Key if found.  If the key is invalid, the variable panos_key will be unset.
+  #Load saved keys
+  _getdeployconf PANOS_KEY
+  _panos_key=$PANOS_KEY
+
+
+  #Test API Key if found.  If the key is invalid, the variable _panos_key will be unset.
   if [ "$_panos_host" ] && [ "$_panos_key" ]; then
     _debug "**** Testing API KEY ****"
     deployer keytest
@@ -198,8 +187,8 @@ panos_deploy() {
   elif [ -z "$_panos_user" ]; then
     _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
     return 1
-  elif [ -z "$_panos_key" ] && { [ -z "$_panos_user" ] || [ -z "$_panos_pass" ]; }; then
-    _err "No pass OR valid API key found. If this is your first time deploying please set PANOS_PASS and/or PANOS_KEY in ENV variables. You can delete them after you have succesfully deployed the certs."
+  elif [ -z "$_panos_pass" ]; then
+    _err "No password found. If this is your first time deploying, please set PANOS_PASS in ENV variables. You can delete it after you have successfully deployed the certs."
     return 1
   else
     # Generate a new API key if no valid API key is found

From ae035deb92b1557832fa06e4ebecd0519df6c8b4 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 18 Jul 2023 20:10:31 +0000
Subject: [PATCH 27/88] Fixed shell check errors

---
 deploy/panos.sh | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 27919a25..efc1a656 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -15,7 +15,6 @@
 # The script will automatically generate a new API key if
 # no key is found, or if a saved key has expired or is invalid.
 
-
 # This function is to parse the XML response from the firewall
 parse_response() {
   type=$2
@@ -130,6 +129,8 @@ panos_deploy() {
   _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _cfullchain="$5"
+ _regen_keys=false    #Flag to regenerate keys if PANOS_USER or PANOS_PASS changes.
+
 
   # VALID FILE CHECK
   if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
@@ -164,15 +165,22 @@ panos_deploy() {
     _getdeployconf PANOS_PASS
   fi
 
+  # PANOS_KEY
+  _getdeployconf PANOS_KEY
+  if [ "$PANOS_KEY" ]; then
+    _debug "Detected saved key."
+    _panos_key=$PANOS_KEY
+  else
+    _debug "No key detected"
+    unset _panos_key
+  fi
+
+
   #Store variables
   _panos_host=$PANOS_HOST
   _panos_user=$PANOS_USER
   _panos_pass=$PANOS_PASS
 
-  #Load saved keys
-  _getdeployconf PANOS_KEY
-  _panos_key=$PANOS_KEY
-
 
   #Test API Key if found.  If the key is invalid, the variable _panos_key will be unset.
   if [ "$_panos_host" ] && [ "$_panos_key" ]; then

From 02de281e40ecebd2e68a12d92066f44303b57348 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 18 Jul 2023 20:15:46 +0000
Subject: [PATCH 28/88] Removed unused variable

---
 deploy/panos.sh | 1 -
 1 file changed, 1 deletion(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index efc1a656..9e48e4b3 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -129,7 +129,6 @@ panos_deploy() {
   _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _cfullchain="$5"
- _regen_keys=false    #Flag to regenerate keys if PANOS_USER or PANOS_PASS changes.
 
 
   # VALID FILE CHECK

From 1984f44ffe1e192c4d0b66fd026df4aa29801684 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Tue, 18 Jul 2023 20:18:12 +0000
Subject: [PATCH 29/88] Shell formatting

---
 deploy/panos.sh | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/deploy/panos.sh b/deploy/panos.sh
index 9e48e4b3..89458e5f 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -130,7 +130,6 @@ panos_deploy() {
   _ckey="$2"
   _cfullchain="$5"
 
-
   # VALID FILE CHECK
   if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
     _err "Unable to find a valid key and/or cert.  If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
@@ -174,13 +173,11 @@ panos_deploy() {
     unset _panos_key
   fi
 
-
   #Store variables
   _panos_host=$PANOS_HOST
   _panos_user=$PANOS_USER
   _panos_pass=$PANOS_PASS
 
-
   #Test API Key if found.  If the key is invalid, the variable _panos_key will be unset.
   if [ "$_panos_host" ] && [ "$_panos_key" ]; then
     _debug "**** Testing API KEY ****"

From a9f631f404eaaeaa9a39ba14bb8236a4925db9e8 Mon Sep 17 00:00:00 2001
From: sg1888 <ssg1888@nyu.edu>
Date: Fri, 21 Jul 2023 16:49:20 +0000
Subject: [PATCH 30/88] Added help verbiage for --ecc flag

---
 acme.sh | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/acme.sh b/acme.sh
index 7a9468fd..d356084c 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1553,7 +1553,7 @@ createDomainKey() {
 createCSR() {
   _info "Creating csr"
   if [ -z "$1" ]; then
-    _usage "Usage: $PROJECT_ENTRY --create-csr --domain <domain.tld> [--domain <domain2.tld> ...]"
+    _usage "Usage: $PROJECT_ENTRY --create-csr --domain <domain.tld> [--domain <domain2.tld> ...] [--ecc]"
     return
   fi
 
@@ -6936,7 +6936,8 @@ Parameters:
   --no-profile                      Only valid for '--install' command, which means: do not install aliases to user profile.
   --no-color                        Do not output color text.
   --force-color                     Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
-  --ecc                             Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--to-pkcs12' and '--create-csr'
+  --ecc                             Specifies use of the ECC cert. Only valid for '--install-cert', '--renew', '--remove ', '--revoke',
+                                      '--deploy', '--to-pkcs8', '--to-pkcs12' and '--create-csr'.
   --csr <file>                      Specifies the input csr.
   --pre-hook <command>              Command to be run before obtaining any certificates.
   --post-hook <command>             Command to be run after attempting to obtain/renew certificates. Runs regardless of whether obtain/renew succeeded or failed.

From 2014ca9feb3758999338b31827acbe12c3ca2813 Mon Sep 17 00:00:00 2001
From: Malte Rabenseifner <mail@malte-rabenseifner.de>
Date: Wed, 26 Jul 2023 15:36:11 +0200
Subject: [PATCH 31/88] Fix the API calls that get the list of domains that
 PLESK can manage

---
 dnsapi/dns_pleskxml.sh | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh
index 799c374c..9cb0b1be 100644
--- a/dnsapi/dns_pleskxml.sh
+++ b/dnsapi/dns_pleskxml.sh
@@ -46,6 +46,10 @@ pleskxml_tplt_get_domains="<packet><webspace><get><filter/><dataset><gen_info/><
 # Also used to test credentials and URI.
 # No params.
 
+pleskxml_tplt_get_additional_domains="<packet><site><get><filter/><dataset><gen_info/></dataset></get></site></packet>"
+# Get a list of additional domains that PLESK can manage, so we can check root domain + host for acme.sh
+# No params.
+
 pleskxml_tplt_get_dns_records="<packet><dns><get_rec><filter><site-id>%s</site-id></filter></get_rec></dns></packet>"
 # Get all DNS records for a Plesk domain ID.
 # PARAM = Plesk domain id to query
@@ -375,16 +379,44 @@ _pleskxml_get_root_domain() {
     return 1
   fi
 
-  # Generate a crude list of domains known to this Plesk account.
+  # Generate a crude list of domains known to this Plesk account based on subscriptions.
   # We convert <ascii-name> tags to <name> so it'll flag on a hit with either <name> or <ascii-name> fields,
   # for non-Western character sets.
   # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
   # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 
   output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
+  debug_output="$(printf "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+  
+  _debug 'Domains managed by Plesk server are:'
+  _debug "$debug_output" 
+
+  _debug "Querying Plesk server for list of additional managed domains..."
+  
+  _call_api "$pleskxml_tplt_get_additional_domains"
+  if [ "$pleskxml_retcode" -ne 0 ]; then
+    return 1
+  fi
+
+  # Generate a crude list of additional domains known to this Plesk account based on sites.
+  # We convert <ascii-name> tags to <name> so it'll flag on a hit with either <name> or <ascii-name> fields,
+  # for non-Western character sets.
+  # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
+  # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 
-  _debug 'Domains managed by Plesk server are (ignore the hacked output):'
-  _debug "$output"
+  output_additional="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
+  debug_additional="$(printf "$output_additional" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+
+  _debug 'Additional domains managed by Plesk server are:'
+  _debug "$debug_additional"
+  
+  # Concate the two outputs together.
+  
+  output="$(printf "$output $NEWLINE $output_additional")"
+  debug_output="$(printf "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+  
+  _debug 'Domains (including additional) managed by Plesk server are:'
+  _debug "$debug_output" 
 
   # loop and test if domain, or any parent domain, is managed by Plesk
   # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain

From 4d4b6edbc2e59c147b487306604de75e25657f1e Mon Sep 17 00:00:00 2001
From: samuel <53528911+samuel-jimenez@users.noreply.github.com>
Date: Wed, 26 Jul 2023 10:29:38 -0500
Subject: [PATCH 32/88] Add DNSExit.com API support

---
 dnsapi/dns_dnsexit.sh | 185 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 185 insertions(+)
 create mode 100644 dnsapi/dns_dnsexit.sh

diff --git a/dnsapi/dns_dnsexit.sh b/dnsapi/dns_dnsexit.sh
new file mode 100644
index 00000000..62d7d757
--- /dev/null
+++ b/dnsapi/dns_dnsexit.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#use dns-01 at DNSExit.com
+
+#Author: Samuel Jimenez
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+
+#DNSEXIT_API_KEY=ABCDEFGHIJ0123456789abcdefghij
+#DNSEXIT_AUTH_USER=login@email.address
+#DNSEXIT_AUTH_PASS=aStrongPassword
+DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
+DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
+
+########  Public functions #####################
+#Usage: dns_dnsexit_add   _acme-challenge.*.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dnsexit_add() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using DNSExit.com"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  _debug 'Load account auth'
+  if ! get_account_info; then
+    return 1
+  fi
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
+    _err "$response"
+    return 1
+  fi
+
+  _debug2 _response "$response"
+  return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_dnsexit_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using DNSExit.com"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  _debug 'Load account auth'
+  if ! get_account_info; then
+    return 1
+  fi
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    _err "$response"
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
+    _err "$response"
+    return 1
+  fi
+
+  _debug2 _response "$response"
+  return 0
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+  domain=$1
+  i=1
+  while true; do
+    _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$_domain"
+    if [ -z "$_domain" ]; then
+      return 1
+    fi
+
+    _debug login "$DNSEXIT_AUTH_USER"
+    _debug password "$DNSEXIT_AUTH_PASS"
+    _debug domain "$_domain"
+
+    _dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
+
+    if _contains "$response" "0=$_domain"; then
+      _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+      return 0
+    else
+      _debug "Go to next level of $_domain"
+    fi
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}
+
+_dnsexit_rest() {
+  m=POST
+  ep=""
+  data="$1"
+  _debug _dnsexit_rest "$ep"
+  _debug data "$data"
+
+  api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
+
+  export _H1="apikey: $api_key_trimmed"
+  export _H2='Content-Type: application/json'
+
+  if [ "$m" != "GET" ]; then
+    _debug data "$data"
+    response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
+  else
+    response="$(_get "$DNSEXIT_API_URL/$ep")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "Error $ep"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+_dnsexit_http() {
+  m=GET
+  param="$1"
+  _debug param "$param"
+  _debug get "$DNSEXIT_HOSTS_URL?$param"
+
+  response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
+
+  _debug response "$response"
+
+  if [ "$?" != "0" ]; then
+    _err "Error $param"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+get_account_info() {
+
+  DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
+  if test -z "$DNSEXIT_API_KEY"; then
+    DNSEXIT_API_KEY=''
+    _err 'DNSEXIT_API_KEY was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
+
+  DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
+  if test -z "$DNSEXIT_AUTH_USER"; then
+    DNSEXIT_AUTH_USER=""
+    _err 'DNSEXIT_AUTH_USER was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
+
+  DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
+  if test -z "$DNSEXIT_AUTH_PASS"; then
+    DNSEXIT_AUTH_PASS=""
+    _err 'DNSEXIT_AUTH_PASS was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
+
+  return 0
+}

From 80006f47300bd0559729a2800ae014475f7499ce Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Sat, 29 Jul 2023 10:26:59 +0200
Subject: [PATCH 33/88] Added bug report url

---
 dnsapi/dns_artfiles.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_artfiles.sh b/dnsapi/dns_artfiles.sh
index faf8c2ae..a762837e 100644
--- a/dnsapi/dns_artfiles.sh
+++ b/dnsapi/dns_artfiles.sh
@@ -5,7 +5,7 @@
 ################################################################################
 # Author:   Martin Arndt, https://troublezone.net/
 # Released: 2022-02-27
-# Issues:    https://github.com/acmesh-official/acme.sh/issues/XXXX
+# Issues:   https://github.com/acmesh-official/acme.sh/issues/4718
 ################################################################################
 # Usage:
 # 1. export AF_API_USERNAME='api12345678'

From 3b7be478aa7f5f257397e9cf8cccea3e1a2aa502 Mon Sep 17 00:00:00 2001
From: Malte Rabenseifner <mail@malte-rabenseifner.de>
Date: Sat, 29 Jul 2023 11:27:33 +0200
Subject: [PATCH 34/88] Fix stuff from tests

ShellCheck tests have brought up a couple of issues, that I was not aware of needed to be taken care. This should fix the tests.
---
 dnsapi/dns_pleskxml.sh | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh
index 9cb0b1be..81973e07 100644
--- a/dnsapi/dns_pleskxml.sh
+++ b/dnsapi/dns_pleskxml.sh
@@ -386,13 +386,13 @@ _pleskxml_get_root_domain() {
   # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 
   output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
-  debug_output="$(printf "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
-  
+  debug_output="$(printf "%s" "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+
   _debug 'Domains managed by Plesk server are:'
-  _debug "$debug_output" 
+  _debug "$debug_output"
 
   _debug "Querying Plesk server for list of additional managed domains..."
-  
+
   _call_api "$pleskxml_tplt_get_additional_domains"
   if [ "$pleskxml_retcode" -ne 0 ]; then
     return 1
@@ -405,18 +405,18 @@ _pleskxml_get_root_domain() {
   # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 
   output_additional="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
-  debug_additional="$(printf "$output_additional" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+  debug_additional="$(printf "%s" "$output_additional" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
 
   _debug 'Additional domains managed by Plesk server are:'
   _debug "$debug_additional"
-  
+
   # Concate the two outputs together.
-  
-  output="$(printf "$output $NEWLINE $output_additional")"
-  debug_output="$(printf "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
-  
+
+  output="$(printf "%s" "$output $NEWLINE $output_additional")"
+  debug_output="$(printf "%s" "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+
   _debug 'Domains (including additional) managed by Plesk server are:'
-  _debug "$debug_output" 
+  _debug "$debug_output"
 
   # loop and test if domain, or any parent domain, is managed by Plesk
   # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain

From d52b38777aaf19bc49ef6edd5d23546ddd3a4f03 Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Wed, 9 Aug 2023 19:52:37 +0200
Subject: [PATCH 35/88] Fix Auth API access for DSM 6

---
 deploy/synology_dsm.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index 7398b350..ba35d784 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -91,8 +91,10 @@ synology_dsm_deploy() {
 
   _debug "Getting API version"
   response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
+  api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([0-9]*\)".*/\1/p')
   api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
   _debug3 response "$response"
+  _debug3 api_path "$api_path"
   _debug3 api_version "$api_version"
 
   # Login, get the session ID & SynoToken from JSON
@@ -133,12 +135,12 @@ synology_dsm_deploy() {
       [ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal"
     fi
 
-    response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
+    response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
     _debug3 response "$response"
     SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p')
     _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
   else
-    response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")
+    response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")
     _debug3 response "$response"
   fi
 

From b793dbf977dbf5d6b6829d185e0b2a0fa130941a Mon Sep 17 00:00:00 2001
From: Martin Arndt <5111490+Eagle3386@users.noreply.github.com>
Date: Fri, 11 Aug 2023 17:55:45 +0200
Subject: [PATCH 36/88] Fix device ID property name for DSM 6

---
 deploy/synology_dsm.sh | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index ba35d784..21c7ac9b 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -136,8 +136,11 @@ synology_dsm_deploy() {
     fi
 
     response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
-    _debug3 response "$response"
-    SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p')
+    _secure_debug3 response "$response"
+
+    id_property='device_id'
+    [ "${api_version}" -gt '6' ] || id_property='did'
+    SYNO_Device_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
     _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
   else
     response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")

From 8d00f489cd6072d562dce05e7a43a5bdb0877113 Mon Sep 17 00:00:00 2001
From: Vito <35035879+vitoyucepi@users.noreply.github.com>
Date: Sun, 20 Aug 2023 20:05:45 +0300
Subject: [PATCH 37/88] Remove excessive full stop from help

---
 acme.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acme.sh b/acme.sh
index 07aef029..70f1daac 100755
--- a/acme.sh
+++ b/acme.sh
@@ -6925,7 +6925,7 @@ Parameters:
 
   These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
 
-  --cert-file <file>                Path to copy the cert file to after issue/renew..
+  --cert-file <file>                Path to copy the cert file to after issue/renew.
   --key-file <file>                 Path to copy the key file to after issue/renew.
   --ca-file <file>                  Path to copy the intermediate cert file to after issue/renew.
   --fullchain-file <file>           Path to copy the fullchain cert file to after issue/renew.

From 13d31ecb7f195f623a9a4deff9c2eb3fc6aecaeb Mon Sep 17 00:00:00 2001
From: Nirjas Jakilim <nirjas01@student.sust.edu>
Date: Mon, 21 Aug 2023 12:28:50 +0600
Subject: [PATCH 38/88] fixed regex for nginx conf path

Fixed the regex for nginx path configuration to fix grep: unrecognized option error
---
 acme.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acme.sh b/acme.sh
index 75efde4c..8c662486 100755
--- a/acme.sh
+++ b/acme.sh
@@ -3130,7 +3130,7 @@ _setNginx() {
         _err "nginx command is not found."
         return 1
       fi
-      NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
+      NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "\-\-conf-path=[^ ]* " | tr -d " ")"
       _debug NGINX_CONF "$NGINX_CONF"
       NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
       _debug NGINX_CONF "$NGINX_CONF"

From 9143cd1485a4a0220897124fec78eb2ec45f81ec Mon Sep 17 00:00:00 2001
From: glocknerc <65351984+glocknerc@users.noreply.github.com>
Date: Wed, 23 Aug 2023 14:07:07 +0200
Subject: [PATCH 39/88] Change grep to be case-insensitive when looking for
 Set-Cookie header since INWX change casing to lowercase

---
 dnsapi/dns_inwx.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index ba789da9..e483c0e8 100755
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -194,7 +194,7 @@ _inwx_login() {
 
   response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
 
-  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
   _H1=$INWX_Cookie
   export _H1
   export INWX_Cookie

From 9b0b5bce9f79672dff5eb80dbb74a0ec4c88986f Mon Sep 17 00:00:00 2001
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Mon, 28 Aug 2023 15:30:26 +0200
Subject: [PATCH 40/88] inwx: Be case insensitive while searching for the
 cookie.

At least since 2023-08-25 the cookie is set via `set-cookie' instead the
expecting `Set-Cookie' string. A month earlier it was working.

Ignore the case while matching the cookie.

Fixes: #4763
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 dnsapi/dns_inwx.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_inwx.sh b/dnsapi/dns_inwx.sh
index ba789da9..e483c0e8 100755
--- a/dnsapi/dns_inwx.sh
+++ b/dnsapi/dns_inwx.sh
@@ -194,7 +194,7 @@ _inwx_login() {
 
   response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
 
-  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
   _H1=$INWX_Cookie
   export _H1
   export INWX_Cookie

From 089d35708b99fe326660c8d95ed30b5040532d40 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 14:31:17 +0800
Subject: [PATCH 41/88] fix for curl bugs
 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation

see
https://bugs.launchpad.net/ubuntu/+source/curl/+bug/2018342
https://github.com/acmesh-official/acme.sh/commit/b6f62ac446ab9ff78f4a90c4330dc314678545c9
https://github.com/acmesh-official/acme.sh/issues/4775
---
 .github/workflows/DNS.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 615e5d8b..a13cb51c 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -332,7 +332,7 @@ jobs:
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
-          pkg_add curl socat
+          pkg_add curl socat libnghttp2
         usesh: true
         copyback: false
         run: |
@@ -384,7 +384,7 @@ jobs:
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
-          pkg install -y curl socat
+          pkg install -y curl socat libnghttp2
         usesh: true
         copyback: false
         run: |

From 29a2920a2c585488ba1f81f9d459d86aab951f9e Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 14:49:43 +0800
Subject: [PATCH 42/88] Add dns_tencent.sh

Adapt to Tencent Cloud (DNSPod) API 3.0
---
 dnsapi/dns_tencent.sh | 201 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 201 insertions(+)
 create mode 100644 dnsapi/dns_tencent.sh

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
new file mode 100644
index 00000000..30e33f03
--- /dev/null
+++ b/dnsapi/dns_tencent.sh
@@ -0,0 +1,201 @@
+#!/usr/bin/env sh
+set -x
+Tencent_API="https://dnspod.tencentcloudapi.com"
+
+#Tencent_SecretId="AKIDz8krbsJ5yKBZQpn74WFkmLPx3gnPhESA"
+#Tencent_SecretKey="Gu5t9xGARNpq86cd98joQYCN3Cozk1qA"
+
+#Usage: dns_tencent_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_tencent_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+  if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
+    Tencent_SecretId=""
+    Tencent_SecretKey=""
+    _err "You don't specify tencent api SecretId and SecretKey yet."
+    return 1
+  fi
+
+  #save the api SecretId and SecretKey to the account conf file.
+  _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
+  _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Add record"
+  _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
+}
+
+dns_tencent_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Get record list"
+  sleep 30
+  _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
+
+  record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+  _debug2 record_id "$record_id"
+
+  if [ -z "$record_id" ]; then
+    _debug "record not found, skip"
+  else
+    _debug "Delete record"
+    _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
+  fi
+}
+
+####################  Private functions below ##################################
+
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+    if [ -z "$h" ]; then
+      #not valid
+      return 1
+    fi
+
+    _describe_records_query "$h" "@"
+    if ! _tencent_rest "DescribeRecordList" "ignore"; then
+      return 1
+    fi
+
+    if _contains "$response" "\"TotalCount\":"; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+      _debug _sub_domain "$_sub_domain"
+      _domain="$h"
+      _debug _domain "$_domain"
+      return 0
+    fi
+    p="$i"
+    i=$(_math "$i" + 1)
+  done
+  return 1
+}
+
+_tencent_rest() {
+  action=$1
+  service="dnspod"
+  payload="${query}"
+  timestamp=$(date -u +%s)
+
+  token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
+  version="2021-03-23"
+
+  if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
+    _err "Error <$1>"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  if [ -z "$2" ]; then
+    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+    if [ "$message" ]; then
+      _err "$message"
+      return 1
+    fi
+  fi
+}
+
+_add_record_query() {
+  query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
+}
+
+_describe_records_query() {
+  query="{\"Domain\":\"$1\",\"Limit\":3000}"
+}
+
+_delete_record_query() {
+  query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
+}
+
+_check_exist_query() {
+  _domain="$1"
+  _subdomain="$2"
+  _value="$3"
+  query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
+}
+
+# shell client for tencent cloud api v3 | @author: rehiy
+
+tencent_sha256() {
+  printf %b "$@" | openssl dgst -sha256 -hex | sed 's/^.* //'
+}
+
+tencent_hmac_sha256() {
+  k=$1
+  shift
+  printf %b "$@" | openssl dgst -sha256 -hmac "$k" | sed 's/^.* //'
+}
+
+tencent_hmac_sha256_hexkey() {
+  k=$1
+  shift
+  printf %b "$@" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //'
+}
+
+tencent_signature_v3() {
+  service=$1
+  action=$(echo "$2" | tr '[:upper:]' '[:lower:]')
+  payload=${3:-'{}'}
+  timestamp=${4:-$(date +%s)}
+
+  domain="$service.tencentcloudapi.com"
+  secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
+  secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
+
+  algorithm='TC3-HMAC-SHA256'
+  date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
+  [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
+
+  canonicalUri='/'
+  canonicalQuery=''
+  canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
+
+  signedHeaders='content-type;host;x-tc-action'
+  canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
+
+  credentialScope="$date/$service/tc3_request"
+  stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+
+  secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
+  secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
+  secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
+  signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
+
+  echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
+}
+
+tencent_api_request() {
+  service=$1
+  version=$2
+  action=$3
+  payload=${4:-'{}'}
+  timestamp=${5:-$(date +%s)}
+
+  token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
+
+  _H1="Content-Type: application/json"
+  _H2="Authorization: $token"
+  _H3="X-TC-Version: $version"
+  _H4="X-TC-Timestamp: $timestamp"
+  _H5="X-TC-Action: $action"
+
+  _post "$payload" "$Tencent_API" "" "POST" "application/json"
+}

From e5b76ed4c4326c6ce039121c1ed78908aa450288 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 15:13:37 +0800
Subject: [PATCH 43/88] Delete dnsapi/dns_tencent.sh

---
 dnsapi/dns_tencent.sh | 201 ------------------------------------------
 1 file changed, 201 deletions(-)
 delete mode 100644 dnsapi/dns_tencent.sh

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
deleted file mode 100644
index 30e33f03..00000000
--- a/dnsapi/dns_tencent.sh
+++ /dev/null
@@ -1,201 +0,0 @@
-#!/usr/bin/env sh
-set -x
-Tencent_API="https://dnspod.tencentcloudapi.com"
-
-#Tencent_SecretId="AKIDz8krbsJ5yKBZQpn74WFkmLPx3gnPhESA"
-#Tencent_SecretKey="Gu5t9xGARNpq86cd98joQYCN3Cozk1qA"
-
-#Usage: dns_tencent_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_tencent_add() {
-  fulldomain=$1
-  txtvalue=$2
-
-  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
-  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
-  if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
-    Tencent_SecretId=""
-    Tencent_SecretKey=""
-    _err "You don't specify tencent api SecretId and SecretKey yet."
-    return 1
-  fi
-
-  #save the api SecretId and SecretKey to the account conf file.
-  _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
-  _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
-
-  _debug "First detect the root zone"
-  if ! _get_root "$fulldomain"; then
-    return 1
-  fi
-
-  _debug "Add record"
-  _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
-}
-
-dns_tencent_rm() {
-  fulldomain=$1
-  txtvalue=$2
-  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
-  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
-
-  _debug "First detect the root zone"
-  if ! _get_root "$fulldomain"; then
-    return 1
-  fi
-
-  _debug "Get record list"
-  sleep 30
-  _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
-
-  record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
-  _debug2 record_id "$record_id"
-
-  if [ -z "$record_id" ]; then
-    _debug "record not found, skip"
-  else
-    _debug "Delete record"
-    _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
-  fi
-}
-
-####################  Private functions below ##################################
-
-_get_root() {
-  domain=$1
-  i=2
-  p=1
-  while true; do
-    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
-    if [ -z "$h" ]; then
-      #not valid
-      return 1
-    fi
-
-    _describe_records_query "$h" "@"
-    if ! _tencent_rest "DescribeRecordList" "ignore"; then
-      return 1
-    fi
-
-    if _contains "$response" "\"TotalCount\":"; then
-      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
-      _debug _sub_domain "$_sub_domain"
-      _domain="$h"
-      _debug _domain "$_domain"
-      return 0
-    fi
-    p="$i"
-    i=$(_math "$i" + 1)
-  done
-  return 1
-}
-
-_tencent_rest() {
-  action=$1
-  service="dnspod"
-  payload="${query}"
-  timestamp=$(date -u +%s)
-
-  token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
-  version="2021-03-23"
-
-  if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
-    _err "Error <$1>"
-    return 1
-  fi
-
-  _debug2 response "$response"
-  if [ -z "$2" ]; then
-    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
-    if [ "$message" ]; then
-      _err "$message"
-      return 1
-    fi
-  fi
-}
-
-_add_record_query() {
-  query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
-}
-
-_describe_records_query() {
-  query="{\"Domain\":\"$1\",\"Limit\":3000}"
-}
-
-_delete_record_query() {
-  query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
-}
-
-_check_exist_query() {
-  _domain="$1"
-  _subdomain="$2"
-  _value="$3"
-  query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
-}
-
-# shell client for tencent cloud api v3 | @author: rehiy
-
-tencent_sha256() {
-  printf %b "$@" | openssl dgst -sha256 -hex | sed 's/^.* //'
-}
-
-tencent_hmac_sha256() {
-  k=$1
-  shift
-  printf %b "$@" | openssl dgst -sha256 -hmac "$k" | sed 's/^.* //'
-}
-
-tencent_hmac_sha256_hexkey() {
-  k=$1
-  shift
-  printf %b "$@" | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //'
-}
-
-tencent_signature_v3() {
-  service=$1
-  action=$(echo "$2" | tr '[:upper:]' '[:lower:]')
-  payload=${3:-'{}'}
-  timestamp=${4:-$(date +%s)}
-
-  domain="$service.tencentcloudapi.com"
-  secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
-  secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
-
-  algorithm='TC3-HMAC-SHA256'
-  date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
-  [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
-
-  canonicalUri='/'
-  canonicalQuery=''
-  canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
-
-  signedHeaders='content-type;host;x-tc-action'
-  canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
-
-  credentialScope="$date/$service/tc3_request"
-  stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
-
-  secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
-  secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
-  secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
-  signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
-
-  echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
-}
-
-tencent_api_request() {
-  service=$1
-  version=$2
-  action=$3
-  payload=${4:-'{}'}
-  timestamp=${5:-$(date +%s)}
-
-  token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
-
-  _H1="Content-Type: application/json"
-  _H2="Authorization: $token"
-  _H3="X-TC-Version: $version"
-  _H4="X-TC-Timestamp: $timestamp"
-  _H5="X-TC-Action: $action"
-
-  _post "$payload" "$Tencent_API" "" "POST" "application/json"
-}

From b32d22731b00b723600e0de9f6b9fceb60952f37 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Sep 2023 15:32:12 +0800
Subject: [PATCH 44/88] remove

---
 README.md | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/README.md b/README.md
index 73ff3321..717ecf5f 100644
--- a/README.md
+++ b/README.md
@@ -506,10 +506,6 @@ Support this project with your organization. Your logo will show up here with a
 <a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
 
 
-#### Sponsors
-
-[![quantumca-acmesh-logo](https://user-images.githubusercontent.com/8305679/183255712-634ee1db-bb61-4c03-bca0-bacce99e078c.svg)](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
-
 
 # 19. License & Others
 

From 8bdcd224862af85d99e682844e71122772c3eba7 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Sep 2023 15:44:45 +0800
Subject: [PATCH 45/88] fix
 https://github.com/acmesh-official/acme.sh/issues/4746

---
 acme.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acme.sh b/acme.sh
index 70f1daac..a8256609 100755
--- a/acme.sh
+++ b/acme.sh
@@ -931,7 +931,7 @@ fi
 
 _egrep_o() {
   if [ "$__USE_EGREP" ]; then
-    egrep -o "$1"
+    egrep -o -- "$1"
   else
     sed -n 's/.*\('"$1"'\).*/\1/p'
   fi

From 04946e992eb60a3cc85b4c9b0e0fe9a577ac1e5e Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Sep 2023 17:15:17 +0800
Subject: [PATCH 46/88] change the default debug level to 2.

---
 acme.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/acme.sh b/acme.sh
index 92faeb5e..7b61b109 100755
--- a/acme.sh
+++ b/acme.sh
@@ -107,7 +107,7 @@ DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
 DEBUG_LEVEL_1=1
 DEBUG_LEVEL_2=2
 DEBUG_LEVEL_3=3
-DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
+DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_2
 DEBUG_LEVEL_NONE=0
 
 DOH_CLOUDFLARE=1
@@ -6899,7 +6899,7 @@ Parameters:
 
   -f, --force                       Force install, force cert renewal or override sudo restrictions.
   --staging, --test                 Use staging server, for testing.
-  --debug [0|1|2|3]                 Output debug info. Defaults to 1 if argument is omitted.
+  --debug [0|1|2|3]                 Output debug info. Defaults to $DEBUG_LEVEL_DEFAULT if argument is omitted.
   --output-insecure                 Output all the sensitive messages.
                                       By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
   -w, --webroot <directory>         Specifies the web root folder for web root mode.

From c18364c75549e840a9db0025fc86947f4fdab114 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Sep 2023 17:18:12 +0800
Subject: [PATCH 47/88] change default log level to 2

---
 acme.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/acme.sh b/acme.sh
index 7b61b109..4fab21d2 100755
--- a/acme.sh
+++ b/acme.sh
@@ -102,7 +102,7 @@ ECC_SUFFIX="${ECC_SEP}ecc"
 LOG_LEVEL_1=1
 LOG_LEVEL_2=2
 LOG_LEVEL_3=3
-DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
+DEFAULT_LOG_LEVEL="$LOG_LEVEL_2"
 
 DEBUG_LEVEL_1=1
 DEBUG_LEVEL_2=2
@@ -6917,7 +6917,7 @@ Parameters:
   -k, --keylength <bits>            Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
   -ak, --accountkeylength <bits>    Specifies the account key length: 2048, 3072, 4096
   --log [file]                      Specifies the log file. Defaults to \"$DEFAULT_LOG_FILE\" if argument is omitted.
-  --log-level <1|2>                 Specifies the log level, default is 1.
+  --log-level <1|2>                 Specifies the log level, default is $DEFAULT_LOG_LEVEL.
   --syslog <0|3|6|7>                Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
   --eab-kid <eab_key_id>            Key Identifier for External Account Binding.
   --eab-hmac-key <eab_hmac_key>     HMAC key for External Account Binding.

From eed8a7f0788190d44e6a603bd49eb96c508fdb3c Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Sep 2023 17:27:21 +0800
Subject: [PATCH 48/88] add more debug code
 https://github.com/acmesh-official/acme.sh/issues/4768

---
 acme.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/acme.sh b/acme.sh
index 4fab21d2..54dfa9a5 100755
--- a/acme.sh
+++ b/acme.sh
@@ -5015,9 +5015,9 @@ $_authorizations_map"
         errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
         _debug2 errordetail "$errordetail"
         if [ "$errordetail" ]; then
-          _err "$d:Verify error:$errordetail"
+          _err "Invalid status, $d:Verify error detail:$errordetail"
         else
-          _err "$d:Verify error:$error"
+          _err "Invalid status, $d:Verify error:$error"
         fi
         if [ "$DEBUG" ]; then
           if [ "$vtype" = "$VTYPE_HTTP" ]; then
@@ -5044,7 +5044,7 @@ $_authorizations_map"
       elif _contains "$status" "processing"; then
         _info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
       else
-        _err "$d:Verify error:$response"
+        _err "Unknown status: $status, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _on_issue_err "$_post_hook" "$vlist"
@@ -5057,7 +5057,7 @@ $_authorizations_map"
       _send_signed_request "$_authz_url"
 
       if [ "$?" != "0" ]; then
-        _err "$d:Verify error:$response"
+        _err "Invalid code, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _on_issue_err "$_post_hook" "$vlist"

From 87dc4fe388244663055cea068b23c97632561544 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 18:23:14 +0800
Subject: [PATCH 49/88] =?UTF-8?q?fix=20for=20curl=20bugs=20nghttp2=5Foptio?=
 =?UTF-8?q?n=5Fset=5Fno=5Frfc9113=5Fleading=5Fand=5Ftrailing=5F=E2=80=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In #4776, I mistakenly added libnghttp2 to NetBsd, and now it has been corrected and added to OpenBsd
---
 .github/workflows/DNS.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index a13cb51c..8bae3520 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -280,7 +280,7 @@ jobs:
     - uses: vmactions/openbsd-vm@v0
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
-        prepare: pkg_add socat curl
+        prepare: pkg_add socat curl libnghttp2
         usesh: true
         copyback: false
         run: |
@@ -332,7 +332,7 @@ jobs:
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
-          pkg_add curl socat libnghttp2
+          pkg_add curl socat
         usesh: true
         copyback: false
         run: |

From 09b41aa66711777b90c8d6200b825330ffee2e09 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 18:38:51 +0800
Subject: [PATCH 50/88] fix for
 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation

In #4776, I mistakenly added libnghttp2 to NetBSD, now for correction.
---
 .github/workflows/DNS.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 8bae3520..507755c9 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -280,7 +280,7 @@ jobs:
     - uses: vmactions/openbsd-vm@v0
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
-        prepare: pkg_add socat curl libnghttp2
+        prepare: pkg_add socat curl
         usesh: true
         copyback: false
         run: |

From 3abcfd8fa926fb1ee03b45826f8a12644e63ff05 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sat, 2 Sep 2023 18:47:59 +0800
Subject: [PATCH 51/88] Add dns_tencent.sh

Adapt to Tencent Cloud (DNSPod) API 3.0
---
 dnsapi/dns_tencent.sh | 210 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 210 insertions(+)
 create mode 100644 dnsapi/dns_tencent.sh

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
new file mode 100644
index 00000000..b4b4cb24
--- /dev/null
+++ b/dnsapi/dns_tencent.sh
@@ -0,0 +1,210 @@
+#!/usr/bin/env sh
+Tencent_API="https://dnspod.tencentcloudapi.com"
+
+#Tencent_SecretId="AKIDz81d2cd22cdcdc2dcd1cc1d1A"
+#Tencent_SecretKey="Gu5t9abcabcaabcbabcbbbcbcbbccbbcb"
+
+#Usage: dns_tencent_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_tencent_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+  if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
+    Tencent_SecretId=""
+    Tencent_SecretKey=""
+    _err "You don't specify tencent api SecretId and SecretKey yet."
+    return 1
+  fi
+
+  #save the api SecretId and SecretKey to the account conf file.
+  _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
+  _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Add record"
+  _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
+}
+
+dns_tencent_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Get record list"
+  attempt=1
+  max_attempts=5
+  while [ -z "$record_id" ] && [ $attempt -le $max_attempts ]; do
+    _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
+    record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+    _debug2 record_id "$record_id"
+    if [ -z "$record_id" ]; then
+      _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
+      sleep 10
+      attempt=$((attempt + 1))
+    fi
+  done
+
+  record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+  _debug2 record_id "$record_id"
+
+  if [ -z "$record_id" ]; then
+    _debug "record not found after $max_attempts attempts, skip"
+  else
+    _debug "Delete record"
+    _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
+  fi
+}
+
+####################  Private functions below ##################################
+
+_get_root() {
+  domain=$1
+  i=2
+  p=1
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+    if [ -z "$h" ]; then
+      #not valid
+      return 1
+    fi
+
+    _describe_records_query "$h" "@"
+    if ! _tencent_rest "DescribeRecordList" "ignore"; then
+      return 1
+    fi
+
+    if _contains "$response" "\"TotalCount\":"; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+      _debug _sub_domain "$_sub_domain"
+      _domain="$h"
+      _debug _domain "$_domain"
+      return 0
+    fi
+    p="$i"
+    i=$(_math "$i" + 1)
+  done
+  return 1
+}
+
+_tencent_rest() {
+  action=$1
+  service="dnspod"
+  payload="${query}"
+  timestamp=$(date -u +%s)
+
+  token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
+  version="2021-03-23"
+
+  if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
+    _err "Error <$1>"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  if [ -z "$2" ]; then
+    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+    if [ "$message" ]; then
+      _err "$message"
+      return 1
+    fi
+  fi
+}
+
+_add_record_query() {
+  query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
+}
+
+_describe_records_query() {
+  query="{\"Domain\":\"$1\",\"Limit\":3000}"
+}
+
+_delete_record_query() {
+  query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
+}
+
+_check_exist_query() {
+  _domain="$1"
+  _subdomain="$2"
+  _value="$3"
+  query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
+}
+
+# shell client for tencent cloud api v3 | @author: rehiy
+
+tencent_sha256() {
+  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hex | sed 's/^.* //'
+}
+
+tencent_hmac_sha256() {
+  k=$1
+  shift
+  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hmac "$k" | sed 's/^.* //'
+}
+
+tencent_hmac_sha256_hexkey() {
+  k=$1
+  shift
+  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //'
+}
+
+tencent_signature_v3() {
+  service=$1
+  action=$(echo "$2" | tr '[:upper:]' '[:lower:]')
+  payload=${3:-'{}'}
+  timestamp=${4:-$(date +%s)}
+
+  domain="$service.tencentcloudapi.com"
+  secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
+  secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
+
+  algorithm='TC3-HMAC-SHA256'
+  date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
+  [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
+
+  canonicalUri='/'
+  canonicalQuery=''
+  canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
+
+  signedHeaders='content-type;host;x-tc-action'
+  canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
+
+  credentialScope="$date/$service/tc3_request"
+  stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+
+  secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
+  secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
+  secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
+  signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
+
+  echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
+}
+
+tencent_api_request() {
+  service=$1
+  version=$2
+  action=$3
+  payload=${4:-'{}'}
+  timestamp=${5:-$(date +%s)}
+
+  token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
+
+  _H1="Content-Type: application/json"
+  _H2="Authorization: $token"
+  _H3="X-TC-Version: $version"
+  _H4="X-TC-Timestamp: $timestamp"
+  _H5="X-TC-Action: $action"
+
+  _post "$payload" "$Tencent_API" "" "POST" "application/json"
+}

From 27b1dd04c4d240d368d79216e84debbaeed813ae Mon Sep 17 00:00:00 2001
From: LJea <sino@lioat.cn>
Date: Sun, 3 Sep 2023 01:02:16 +0900
Subject: [PATCH 52/88] improve the compatibility

Fixed an issue where some embedded devices could not obtain nanoseconds resulting in abnormal parameter coding
---
 dnsapi/dns_ali.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh
index c2105672..c69839dc 100755
--- a/dnsapi/dns_ali.sh
+++ b/dnsapi/dns_ali.sh
@@ -117,7 +117,7 @@ _ali_urlencode() {
 _ali_nonce() {
   #_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
   #Not so good...
-  date +"%s%N"
+  date +"%s%N" | sed 's/%N//g'
 }
 
 _check_exist_query() {

From b3f8612e6128d186359d0b0b91dcb8ee2683dc8f Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sun, 3 Sep 2023 01:31:57 +0800
Subject: [PATCH 53/88] Following Neilpang's suggestions and project standards,
 replace some functions.

---
 dnsapi/dns_tencent.sh | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
index b4b4cb24..746f00f0 100644
--- a/dnsapi/dns_tencent.sh
+++ b/dnsapi/dns_tencent.sh
@@ -51,7 +51,7 @@ dns_tencent_rm() {
     _debug2 record_id "$record_id"
     if [ -z "$record_id" ]; then
       _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
-      sleep 10
+      _sleep 10
       attempt=$((attempt + 1))
     fi
   done
@@ -71,7 +71,7 @@ dns_tencent_rm() {
 
 _get_root() {
   domain=$1
-  i=2
+  i=1
   p=1
   while true; do
     h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
@@ -144,19 +144,20 @@ _check_exist_query() {
 # shell client for tencent cloud api v3 | @author: rehiy
 
 tencent_sha256() {
-  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hex | sed 's/^.* //'
+  printf %b "$@" | _digest sha256 hex
 }
 
 tencent_hmac_sha256() {
   k=$1
   shift
-  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -hmac "$k" | sed 's/^.* //'
+  hex_key=$(_ascii_hex "$k" | tr -d ' ')
+  printf %b "$@" | _hmac sha256 "$hex_key" hex
 }
 
 tencent_hmac_sha256_hexkey() {
   k=$1
   shift
-  printf %b "$@" | ${ACME_OPENSSL_BIN:-openssl} dgst -sha256 -mac HMAC -macopt "hexkey:$k" | sed 's/^.* //'
+  printf %b "$@" | _hmac sha256 "$k" hex
 }
 
 tencent_signature_v3() {

From e3c4c9265dfe2d2816e68dfe310d5f6fcda4f25b Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Sun, 3 Sep 2023 21:21:05 +0800
Subject: [PATCH 54/88] Replace some functions.

---
 dnsapi/dns_tencent.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
index 746f00f0..5cce4aeb 100644
--- a/dnsapi/dns_tencent.sh
+++ b/dnsapi/dns_tencent.sh
@@ -45,14 +45,14 @@ dns_tencent_rm() {
   _debug "Get record list"
   attempt=1
   max_attempts=5
-  while [ -z "$record_id" ] && [ $attempt -le $max_attempts ]; do
+  while [ -z "$record_id" ] && [ "$attempt" -le $max_attempts ]; do
     _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
     record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
     _debug2 record_id "$record_id"
     if [ -z "$record_id" ]; then
       _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
       _sleep 10
-      attempt=$((attempt + 1))
+      attempt=$(_math "$attempt + 1")
     fi
   done
 
@@ -162,7 +162,7 @@ tencent_hmac_sha256_hexkey() {
 
 tencent_signature_v3() {
   service=$1
-  action=$(echo "$2" | tr '[:upper:]' '[:lower:]')
+  action=$(echo "$2" | _lower_case)
   payload=${3:-'{}'}
   timestamp=${4:-$(date +%s)}
 

From 772bbdc862777cf06c649271c9319f45b9075313 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Wed, 6 Sep 2023 12:57:19 +0800
Subject: [PATCH 55/88] Replace some functions

---
 dnsapi/dns_tencent.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
index 5cce4aeb..b926225b 100644
--- a/dnsapi/dns_tencent.sh
+++ b/dnsapi/dns_tencent.sh
@@ -150,7 +150,7 @@ tencent_sha256() {
 tencent_hmac_sha256() {
   k=$1
   shift
-  hex_key=$(_ascii_hex "$k" | tr -d ' ')
+  hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
   printf %b "$@" | _hmac sha256 "$hex_key" hex
 }
 
@@ -183,6 +183,7 @@ tencent_signature_v3() {
 
   credentialScope="$date/$service/tc3_request"
   stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+  _debug "stringToSign: $stringToSign"
 
   secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
   secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")

From af534a73fc75cddf5a60f0ec1b13f79056b51940 Mon Sep 17 00:00:00 2001
From: KincaidYang <91786638+KincaidYang@users.noreply.github.com>
Date: Wed, 6 Sep 2023 13:09:52 +0800
Subject: [PATCH 56/88] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E9=83=A8=E5=88=86?=
 =?UTF-8?q?=E6=95=8F=E6=84=9Fdebug=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 dnsapi/dns_tencent.sh | 1 -
 1 file changed, 1 deletion(-)

diff --git a/dnsapi/dns_tencent.sh b/dnsapi/dns_tencent.sh
index b926225b..2f8d3b67 100644
--- a/dnsapi/dns_tencent.sh
+++ b/dnsapi/dns_tencent.sh
@@ -183,7 +183,6 @@ tencent_signature_v3() {
 
   credentialScope="$date/$service/tc3_request"
   stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
-  _debug "stringToSign: $stringToSign"
 
   secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
   secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")

From ae4c186f55b10b26d9bb0528bc90893413bd967e Mon Sep 17 00:00:00 2001
From: Tobias Grave <grave@variomedia.de>
Date: Thu, 7 Sep 2023 08:40:46 +0200
Subject: [PATCH 57/88] Fix Variomedia API

---
 dnsapi/dns_variomedia.sh | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh
index a35b8f0f..200b3f50 100644
--- a/dnsapi/dns_variomedia.sh
+++ b/dnsapi/dns_variomedia.sh
@@ -69,7 +69,7 @@ dns_variomedia_rm() {
     return 1
   fi
 
-  _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
+  _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
   _debug _record_id "$_record_id"
   if [ "$_record_id" ]; then
     _info "Successfully retrieved the record id for ACME challenge."
@@ -93,11 +93,11 @@ dns_variomedia_rm() {
 # _sub_domain=_acme-challenge.www
 # _domain=domain.com
 _get_root() {
-  fulldomain=$1
-  i=1
+  domain=$1
+  i=2
+  p=1
   while true; do
-    h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
-    _debug h "$h"
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
     if [ -z "$h" ]; then
       return 1
     fi
@@ -106,17 +106,14 @@ _get_root() {
       return 1
     fi
 
-    if _startswith "$response" "\{\"data\":"; then
-      if _contains "$response" "\"id\":\"$h\""; then
-        _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
-        _domain=$h
-        return 0
-      fi
+    if _contains "$response" "\"id\":\"$h\""; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
+      _domain="$h"
+      return 0
     fi
+    p=$i
     i=$(_math "$i" + 1)
   done
-
-  _debug "root domain not found"
   return 1
 }
 

From 1a08be0a3fca85413421cedcf598ac4d3f5ded04 Mon Sep 17 00:00:00 2001
From: Julien Furgerot <julien.furgerot@gmail.com>
Date: Mon, 11 Sep 2023 15:05:12 +0200
Subject: [PATCH 58/88] dns_gandi: implements personal access token in addition
 to the (deprecated) API key

---
 dnsapi/dns_gandi_livedns.sh | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
index 931da883..f299779e 100644
--- a/dnsapi/dns_gandi_livedns.sh
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -1,7 +1,8 @@
 #!/usr/bin/env sh
 
 # Gandi LiveDNS v5 API
-# https://doc.livedns.gandi.net/
+# https://api.gandi.net/docs/livedns/
+# https://api.gandi.net/docs/authentication/ for token + apikey (deprecated) authentication
 # currently under beta
 #
 # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
@@ -19,13 +20,23 @@ dns_gandi_livedns_add() {
   fulldomain=$1
   txtvalue=$2
 
-  if [ -z "$GANDI_LIVEDNS_KEY" ]; then
-    _err "No API key specified for Gandi LiveDNS."
-    _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+  if [ -z "$GANDI_LIVEDNS_KEY" -a -z "$GANDI_LIVEDNS_TOKEN" ]; then
+    _err "No Token or API key (deprecated) specified for Gandi LiveDNS."
+    _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
     return 1
   fi
 
-  _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+  # Keep only one secret in configuration
+  if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+    _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
+    _clearaccountconf GANDI_LIVEDNS_KEY
+  elif [ -n "$GANDI_LIVEDNS_KEY" ]; then
+    _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+    _clearaccountconf GANDI_LIVEDNS_TOKEN
+  fi
+
+
+
 
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
@@ -157,7 +168,12 @@ _gandi_livedns_rest() {
   _debug "$ep"
 
   export _H1="Content-Type: application/json"
-  export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+  if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+    export _H2="Authorization: Bearer $GANDI_LIVEDNS_TOKEN"
+  else
+    export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+  fi
 
   if [ "$m" = "GET" ]; then
     response="$(_get "$GANDI_LIVEDNS_API/$ep")"

From 558e706bdeb395ce0164cb9b7a31078269413d33 Mon Sep 17 00:00:00 2001
From: Julien Furgerot <julien.furgerot@gmail.com>
Date: Tue, 12 Sep 2023 15:54:44 +0200
Subject: [PATCH 59/88] fix ci errors (shellcheck & shfmt)

---
 dnsapi/dns_gandi_livedns.sh | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
index f299779e..14939d7c 100644
--- a/dnsapi/dns_gandi_livedns.sh
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -20,7 +20,7 @@ dns_gandi_livedns_add() {
   fulldomain=$1
   txtvalue=$2
 
-  if [ -z "$GANDI_LIVEDNS_KEY" -a -z "$GANDI_LIVEDNS_TOKEN" ]; then
+  if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
     _err "No Token or API key (deprecated) specified for Gandi LiveDNS."
     _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
     return 1
@@ -35,9 +35,6 @@ dns_gandi_livedns_add() {
     _clearaccountconf GANDI_LIVEDNS_TOKEN
   fi
 
-
-
-
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
     _err "invalid domain"

From dfd49e46ad268d6f1796cf850b0b353b667cc11d Mon Sep 17 00:00:00 2001
From: Tobias Grave <grave@variomedia.de>
Date: Thu, 14 Sep 2023 09:15:08 +0200
Subject: [PATCH 60/88] Fix root zone determination for Variomedia API

---
 dnsapi/dns_variomedia.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh
index 200b3f50..aa743807 100644
--- a/dnsapi/dns_variomedia.sh
+++ b/dnsapi/dns_variomedia.sh
@@ -94,7 +94,7 @@ dns_variomedia_rm() {
 # _domain=domain.com
 _get_root() {
   domain=$1
-  i=2
+  i=1
   p=1
   while true; do
     h=$(printf "%s" "$domain" | cut -d . -f $i-100)

From 59f976dc48873787d089df34c3ebc092a14bc110 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Wed, 20 Sep 2023 18:07:16 +0800
Subject: [PATCH 61/88] fix
 https://github.com/acmesh-official/acme.sh/issues/4798

---
 acme.sh | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/acme.sh b/acme.sh
index 067780d7..bce7a41d 100755
--- a/acme.sh
+++ b/acme.sh
@@ -2118,12 +2118,7 @@ _tail_n() {
 }
 
 _tail_c() {
-  if _is_solaris; then
-    #fix for solaris
-    tail -"$1"c
-  else
-    tail -c "$1"
-  fi
+  tail -c "$1" 2>/dev/null || tail -"$1"c
 }
 
 # url  payload needbase64  keyfile

From 87a7bde61813391e6c8379ae93c921bd68889c53 Mon Sep 17 00:00:00 2001
From: Romeo Dumitrescu <romeo@drc.ro>
Date: Mon, 25 Sep 2023 18:43:01 +0300
Subject: [PATCH 62/88] fix: Synology DSM API path regex

Fix the regex for looking up the API path value from the Synology API query.
---
 deploy/synology_dsm.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index 21c7ac9b..10da861a 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -91,7 +91,7 @@ synology_dsm_deploy() {
 
   _debug "Getting API version"
   response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
-  api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([0-9]*\)".*/\1/p')
+  api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
   api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
   _debug3 response "$response"
   _debug3 api_path "$api_path"

From f2e1b589b5c5ff115fe9e8d87f4a926cd575b5ff Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Fri, 6 Oct 2023 20:01:28 +0800
Subject: [PATCH 63/88] start 3.0.8

---
 acme.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acme.sh b/acme.sh
index bce7a41d..34291708 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
 #!/usr/bin/env sh
 
-VER=3.0.7
+VER=3.0.8
 
 PROJECT_NAME="acme.sh"
 

From e15513bfdd6fb843f8b57be407bffe28bb12de6b Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Fri, 6 Oct 2023 20:05:39 +0800
Subject: [PATCH 64/88] fix format

---
 notify/mattermost.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/notify/mattermost.sh b/notify/mattermost.sh
index 9037ae0d..f13f6ebc 100644
--- a/notify/mattermost.sh
+++ b/notify/mattermost.sh
@@ -38,7 +38,7 @@ mattermost_send() {
   _data="$_data\"message\": \"$_content\"}"
 
   export _H1="Authorization: Bearer $MATTERMOST_BOT_TOKEN"
-
+  response=""
   if _post "$_data" "$MATTERMOST_API_URL" "" "POST" "application/json; charset=utf-8"; then
     MATTERMOST_RESULT_OK=$(echo "$response" | _egrep_o 'create_at')
     if [ "$?" = "0" ] && [ "$MATTERMOST_RESULT_OK" ]; then

From fe890c62f4229c8696d85edfd2336130214135db Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sun, 22 Oct 2023 23:07:00 +0800
Subject: [PATCH 65/88] fix
 https://github.com/acmesh-official/acme.sh/issues/4835

---
 acme.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/acme.sh b/acme.sh
index 34291708..3fdaa804 100755
--- a/acme.sh
+++ b/acme.sh
@@ -931,7 +931,7 @@ fi
 
 _egrep_o() {
   if [ "$__USE_EGREP" ]; then
-    egrep -o -- "$1"
+    egrep -o -- "$1" 2>/dev/null
   else
     sed -n 's/.*\('"$1"'\).*/\1/p'
   fi

From 8ca5ca6594227b473edc8882753371c6176e6f0e Mon Sep 17 00:00:00 2001
From: podguzovvasily <podguzovvasily@gmail.com>
Date: Tue, 24 Oct 2023 16:58:47 +0300
Subject: [PATCH 66/88] Update haproxy.sh

resolved issue with HAProxy https://github.com/acmesh-official/acme.sh/issues/4788
according https://serversforhackers.com/c/letsencrypt-with-haproxy
---
 deploy/haproxy.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/deploy/haproxy.sh b/deploy/haproxy.sh
index c255059d..d638abb8 100644
--- a/deploy/haproxy.sh
+++ b/deploy/haproxy.sh
@@ -147,7 +147,7 @@ haproxy_deploy() {
   # Create a temporary PEM file
   _temppem="$(_mktemp)"
   _debug _temppem "${_temppem}"
-  cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
+  cat "${_ccert}" "${_cca}" "${_ckey}" >"${_temppem}"
   _ret="$?"
 
   # Check that we could create the temporary file

From 00dbc3881fa377646115a237bb12193f13504973 Mon Sep 17 00:00:00 2001
From: Adnan RIHAN <adnan@rihan.fr>
Date: Wed, 1 Nov 2023 20:02:16 +0100
Subject: [PATCH 67/88] Fixed variables

---
 deploy/proxmoxve.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/deploy/proxmoxve.sh b/deploy/proxmoxve.sh
index 216a8fc7..f9de590c 100644
--- a/deploy/proxmoxve.sh
+++ b/deploy/proxmoxve.sh
@@ -99,11 +99,11 @@ proxmoxve_deploy() {
     _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
     _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
   fi
-  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
+  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
 
   # PVE API Token header value. Used in "Authorization: PVEAPIToken".
   _proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
-  _debug2 "Auth Header" _proxmoxve_header_api_token
+  _debug2 "Auth Header" "$_proxmoxve_header_api_token"
 
   # Ugly. I hate putting heredocs inside functions because heredocs don't
   # account for whitespace correctly but it _does_ work and is several times
@@ -124,8 +124,8 @@ HEREDOC
   )
   _debug2 Payload "$_json_payload"
 
-  # Push certificates to server.
-  export _HTTPS_INSECURE=1
+  _info "Push certificates to server"
+  export HTTPS_INSECURE=1
   export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
   _post "$_json_payload" "$_target_url" "" POST "application/json"
 

From 199977be6a6785f7507f8ee21fe7d350979909e6 Mon Sep 17 00:00:00 2001
From: Gavin Leo <trulyliu@gmail.com>
Date: Mon, 30 Oct 2023 11:16:37 +0800
Subject: [PATCH 68/88] Fix
 https://github.com/acmesh-official/acme.sh/issues/4460

Update gcore API url.
---
 dnsapi/dns_gcore.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dnsapi/dns_gcore.sh b/dnsapi/dns_gcore.sh
index d549a650..5f7f037e 100755
--- a/dnsapi/dns_gcore.sh
+++ b/dnsapi/dns_gcore.sh
@@ -4,8 +4,8 @@
 #GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
 #
 
-GCORE_Api="https://api.gcorelabs.com/dns/v2"
-GCORE_Doc="https://apidocs.gcore.com/dns"
+GCORE_Api="https://api.gcore.com/dns/v2"
+GCORE_Doc="https://api.gcore.com/docs/dns"
 
 ########  Public functions #####################
 

From 8454ffa331905efc0e85f70a12a6c1a9dbf5c128 Mon Sep 17 00:00:00 2001
From: Matthew Robinson <mrobinson@study.com>
Date: Thu, 2 Nov 2023 08:30:44 -0700
Subject: [PATCH 69/88] Fix issue with similar domain names causing an error in
 selecting the proper root domain to add challenge records in

---
 dnsapi/dns_aws.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
index 50c93260..bb0682a7 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -157,7 +157,7 @@ _get_root() {
 
   # iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
   while true; do
-    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100 | sed 's/\./\\./g')
     _debug "Checking domain: $h"
     if [ -z "$h" ]; then
       _error "invalid domain"

From 5342c7c82b4ef07e0921070d1b1392b094a3644e Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Fri, 3 Nov 2023 18:14:26 +0800
Subject: [PATCH 70/88] support West.cn Domain

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/dns_west.sh | 108 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 dnsapi/dns_west.sh

diff --git a/dnsapi/dns_west.sh b/dnsapi/dns_west.sh
new file mode 100644
index 00000000..6a9ebe5d
--- /dev/null
+++ b/dnsapi/dns_west.sh
@@ -0,0 +1,108 @@
+#!/usr/bin/env sh
+
+# West.cn Domain api
+#
+#WEST_Username="uername"
+#
+#WEST_Key="sADDsdasdgdsf"
+
+REST_API="https://api.west.cn/API/v2"
+
+########  Public functions #####################
+
+#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_west_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+  WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+  if [ -z "$WEST_Username" ] || [ -z "$WEST_Key" ]; then
+    WEST_Username=""
+    WEST_Key=""
+    _err "You don't specify dnspod api key and key id yet."
+    _err "Please create you key and try again."
+    return 1
+  fi
+
+  #save the api key and email to the account conf file.
+  _saveaccountconf_mutable WEST_Username "$WEST_Username"
+  _saveaccountconf_mutable WEST_Key "$WEST_Key"
+
+  add_record "$fulldomain" "$txtvalue"
+}
+
+#Usage: rm _acme-challenge.www.domain.com
+dns_west_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+  WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.list&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT"; then
+    _err "Record.Lis error."
+    return 1
+  fi
+
+  if _contains "$response" 'no records'; then
+    _info "Don't need to remove."
+    return 0
+  fi
+
+  record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"record_id"' | cut -d : -f 2 | cut -d ',' -f 1)
+  _debug record_id "$record_id"
+  if [ -z "$record_id" ]; then
+    _err "Can not get record id."
+    return 1
+  fi
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.remove&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_id=$record_id"; then
+    _err "Record.Remove error."
+    return 1
+  fi
+
+  _contains "$response" "success"
+
+
+}
+
+#add the txt record.
+#usage: add fulldomain txtvalue
+add_record() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _info "Adding record"
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&value=$txtvalue"; then
+    return 1
+  fi
+
+  _contains "$response" "success"
+}
+
+#Usage: method  URI  data
+_rest() {
+  m="$1"
+  ep="$2"
+  data="$3"
+  _debug "$ep"
+  url="$REST_API/$ep"
+
+  _debug url "$url"
+
+  if [ "$m" = "GET" ]; then
+    response="$(_get "$url" | tr -d '\r')"
+  else
+    _debug2 data "$data"
+    response="$(_post "$data" "$url" | tr -d '\r')"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}

From 6ea09444ec792f2f4e690cc2fb479e669cae4e7e Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Sat, 4 Nov 2023 00:04:05 +0800
Subject: [PATCH 71/88] Update dns_west.sh

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/dns_west.sh | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/dnsapi/dns_west.sh b/dnsapi/dns_west.sh
index 6a9ebe5d..1ef302ca 100644
--- a/dnsapi/dns_west.sh
+++ b/dnsapi/dns_west.sh
@@ -2,8 +2,8 @@
 
 # West.cn Domain api
 #
-#WEST_Username="uername"
-#
+#WEST_Username="username"
+# set key at https://www.west.cn/manager/API/APIconfig.asp
 #WEST_Key="sADDsdasdgdsf"
 
 REST_API="https://api.west.cn/API/v2"
@@ -63,8 +63,6 @@ dns_west_rm() {
   fi
 
   _contains "$response" "success"
-
-
 }
 
 #add the txt record.

From feffbba6de593693dff8d5c87a7c021b21eadcf1 Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Sat, 4 Nov 2023 14:16:11 +0800
Subject: [PATCH 72/88] Update dns_west.sh

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/dns_west.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/dnsapi/dns_west.sh b/dnsapi/dns_west.sh
index 1ef302ca..afb50fad 100644
--- a/dnsapi/dns_west.sh
+++ b/dnsapi/dns_west.sh
@@ -20,8 +20,8 @@ dns_west_add() {
   if [ -z "$WEST_Username" ] || [ -z "$WEST_Key" ]; then
     WEST_Username=""
     WEST_Key=""
-    _err "You don't specify dnspod api key and key id yet."
-    _err "Please create you key and try again."
+    _err "You don't specify west api key and username yet."
+    _err "Please set you key and try again."
     return 1
   fi
 
@@ -41,7 +41,7 @@ dns_west_rm() {
   WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
 
   if ! _rest POST "domain/dns/" "act=dnsrec.list&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT"; then
-    _err "Record.Lis error."
+    _err "dnsrec.list error."
     return 1
   fi
 
@@ -58,7 +58,7 @@ dns_west_rm() {
   fi
 
   if ! _rest POST "domain/dns/" "act=dnsrec.remove&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_id=$record_id"; then
-    _err "Record.Remove error."
+    _err "dnsrec.remove error."
     return 1
   fi
 

From 1cc3a13c497d8981dc7c6b700e0b38f8d0469262 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 4 Nov 2023 10:04:26 +0100
Subject: [PATCH 73/88] fix comments

---
 .github/workflows/pr_dns.yml    | 3 ++-
 .github/workflows/pr_notify.yml | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/pr_dns.yml b/.github/workflows/pr_dns.yml
index 5faa9105..b627d3ab 100644
--- a/.github/workflows/pr_dns.yml
+++ b/.github/workflows/pr_dns.yml
@@ -22,9 +22,10 @@ jobs:
               owner: context.repo.owner,
               repo: context.repo.repo,
               body: `**Welcome**
-                Please make sure you're read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
+                Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
+                注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
                 `
             })
 
diff --git a/.github/workflows/pr_notify.yml b/.github/workflows/pr_notify.yml
index 4844e297..3b0e3e4b 100644
--- a/.github/workflows/pr_notify.yml
+++ b/.github/workflows/pr_notify.yml
@@ -22,7 +22,7 @@ jobs:
               owner: context.repo.owner,
               repo: context.repo.repo,
               body: `**Welcome**
-                Please make sure you're read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
+                Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
                 `

From a60d0c41087e3b79e60c0c0587c29a0de25c5dee Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Mon, 6 Nov 2023 11:25:09 +0800
Subject: [PATCH 74/88] Update dns_west_cn.sh

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/{dns_west.sh => dns_west_cn.sh} | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
 rename dnsapi/{dns_west.sh => dns_west_cn.sh} (96%)

diff --git a/dnsapi/dns_west.sh b/dnsapi/dns_west_cn.sh
similarity index 96%
rename from dnsapi/dns_west.sh
rename to dnsapi/dns_west_cn.sh
index afb50fad..fa723b64 100644
--- a/dnsapi/dns_west.sh
+++ b/dnsapi/dns_west_cn.sh
@@ -3,15 +3,15 @@
 # West.cn Domain api
 #
 #WEST_Username="username"
-# set key at https://www.west.cn/manager/API/APIconfig.asp
 #WEST_Key="sADDsdasdgdsf"
+#Set key at https://www.west.cn/manager/API/APIconfig.asp
 
 REST_API="https://api.west.cn/API/v2"
 
 ########  Public functions #####################
 
 #Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
-dns_west_add() {
+dns_west_cn_add() {
   fulldomain=$1
   txtvalue=$2
 
@@ -33,7 +33,7 @@ dns_west_add() {
 }
 
 #Usage: rm _acme-challenge.www.domain.com
-dns_west_rm() {
+dns_west_cn_rm() {
   fulldomain=$1
   txtvalue=$2
 

From eb99803b53de63bad45cf144621fe5a59892f2d8 Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Mon, 6 Nov 2023 13:18:36 +0800
Subject: [PATCH 75/88] Update west.cn domain api

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/dns_west_cn.sh | 1 -
 1 file changed, 1 deletion(-)

diff --git a/dnsapi/dns_west_cn.sh b/dnsapi/dns_west_cn.sh
index fa723b64..bff4598a 100644
--- a/dnsapi/dns_west_cn.sh
+++ b/dnsapi/dns_west_cn.sh
@@ -1,7 +1,6 @@
 #!/usr/bin/env sh
 
 # West.cn Domain api
-#
 #WEST_Username="username"
 #WEST_Key="sADDsdasdgdsf"
 #Set key at https://www.west.cn/manager/API/APIconfig.asp

From bea71f34115428299b8c73fd6cc56dedc39697f7 Mon Sep 17 00:00:00 2001
From: mrbaiwei <mrbaiwei@gmail.com>
Date: Tue, 7 Nov 2023 07:20:25 +0800
Subject: [PATCH 76/88] Update dns_west_cn.sh

Signed-off-by: mrbaiwei <mrbaiwei@gmail.com>
---
 dnsapi/dns_west_cn.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_west_cn.sh b/dnsapi/dns_west_cn.sh
index bff4598a..d0bb7d49 100644
--- a/dnsapi/dns_west_cn.sh
+++ b/dnsapi/dns_west_cn.sh
@@ -72,7 +72,7 @@ add_record() {
 
   _info "Adding record"
 
-  if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&value=$txtvalue"; then
+  if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&record_value=$txtvalue"; then
     return 1
   fi
 

From 15d10eeebcb03884ceddfe2df84d142f7fe43e8d Mon Sep 17 00:00:00 2001
From: Adrian Fedoreanu <phedoreanu@users.noreply.github.com>
Date: Fri, 10 Nov 2023 08:22:28 +0100
Subject: [PATCH 77/88] dns_1984.hosting.sh: update login and account status
 URLs

---
 dnsapi/dns_1984hosting.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dnsapi/dns_1984hosting.sh b/dnsapi/dns_1984hosting.sh
index 2c6b2e4f..e4ef2e4b 100755
--- a/dnsapi/dns_1984hosting.sh
+++ b/dnsapi/dns_1984hosting.sh
@@ -128,7 +128,7 @@ _1984hosting_login() {
   _debug "Login to 1984Hosting as user $One984HOSTING_Username."
   username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
   password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
-  url="https://1984.hosting/accounts/checkuserauth/"
+  url="https://1984.hosting/api/auth/"
 
   _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
   csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
@@ -185,7 +185,7 @@ _check_cookies() {
     return 1
   fi
 
-  _authget "https://1984.hosting/accounts/loginstatus/"
+  _authget "https://1984.hosting/api/auth/"
   if _contains "$_response" '"ok": true'; then
     _debug "Cached cookies still valid."
     return 0

From 074cf00a7c0cd154a84522881f1386657aa7cc17 Mon Sep 17 00:00:00 2001
From: Sander Cox <sander@paralleldimension.nl>
Date: Tue, 14 Nov 2023 11:28:24 +0100
Subject: [PATCH 78/88] Update dns_gcloud.sh rm logs record added

The logs show record was added twice but the second time was actual the rm command thus the removal of the record!
---
 dnsapi/dns_gcloud.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh
index 2788ad59..dc82c09d 100755
--- a/dnsapi/dns_gcloud.sh
+++ b/dnsapi/dns_gcloud.sh
@@ -42,7 +42,7 @@ dns_gcloud_rm() {
   echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
   _dns_gcloud_execute_tr || return $?
 
-  _info "$fulldomain record added"
+  _info "$fulldomain record removed"
 }
 
 ####################  Private functions below ##################################

From f899d0d8ed620271756e590ba8c7b38ac19e6177 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Mon, 20 Nov 2023 23:39:25 +0100
Subject: [PATCH 79/88] update

---
 .github/workflows/FreeBSD.yml      | 6 +++---
 .github/workflows/Linux.yml        | 2 +-
 .github/workflows/MacOS.yml        | 2 +-
 .github/workflows/NetBSD.yml       | 6 +++---
 .github/workflows/OpenBSD.yml      | 6 +++---
 .github/workflows/PebbleStrict.yml | 4 ++--
 .github/workflows/Ubuntu.yml       | 2 +-
 .github/workflows/Windows.yml      | 2 +-
 .github/workflows/dockerhub.yml    | 2 +-
 .github/workflows/shellcheck.yml   | 4 ++--
 10 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/.github/workflows/FreeBSD.yml b/.github/workflows/FreeBSD.yml
index 0fa55fd4..b90c9ccd 100644
--- a/.github/workflows/FreeBSD.yml
+++ b/.github/workflows/FreeBSD.yml
@@ -41,7 +41,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "githubtest@acme.sh"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       with:
@@ -61,7 +61,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0
+    - uses: vmactions/freebsd-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |
diff --git a/.github/workflows/Linux.yml b/.github/workflows/Linux.yml
index 156fa5df..33e43483 100644
--- a/.github/workflows/Linux.yml
+++ b/.github/workflows/Linux.yml
@@ -33,7 +33,7 @@ jobs:
       TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
       TEST_ACME_Server: "LetsEncrypt.org_test"
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: |
           cd .. \
diff --git a/.github/workflows/MacOS.yml b/.github/workflows/MacOS.yml
index c1f29769..c3f046ab 100644
--- a/.github/workflows/MacOS.yml
+++ b/.github/workflows/MacOS.yml
@@ -44,7 +44,7 @@ jobs:
       CA_EMAIL: ${{ matrix.CA_EMAIL }}
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run:  brew install socat
     - name: Clone acmetest
diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml
index 25872c42..e9301860 100644
--- a/.github/workflows/NetBSD.yml
+++ b/.github/workflows/NetBSD.yml
@@ -36,7 +36,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "githubtest@acme.sh"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -45,7 +45,7 @@ jobs:
       CA_EMAIL: ${{ matrix.CA_EMAIL }}
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       with:
@@ -55,7 +55,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/netbsd-vm@v0
+    - uses: vmactions/netbsd-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
         nat: |
diff --git a/.github/workflows/OpenBSD.yml b/.github/workflows/OpenBSD.yml
index 745a9408..e141c47b 100644
--- a/.github/workflows/OpenBSD.yml
+++ b/.github/workflows/OpenBSD.yml
@@ -41,7 +41,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "githubtest@acme.sh"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       with:
@@ -61,7 +61,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/openbsd-vm@v0
+    - uses: vmactions/openbsd-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |
diff --git a/.github/workflows/PebbleStrict.yml b/.github/workflows/PebbleStrict.yml
index 9f3a98ce..3f8fdb62 100644
--- a/.github/workflows/PebbleStrict.yml
+++ b/.github/workflows/PebbleStrict.yml
@@ -33,7 +33,7 @@ jobs:
       TEST_CA: "Pebble Intermediate CA"
 
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat
     - name: Run Pebble
@@ -58,7 +58,7 @@ jobs:
       TEST_IPCERT: 1
 
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat
     - name: Run Pebble
diff --git a/.github/workflows/Ubuntu.yml b/.github/workflows/Ubuntu.yml
index 4bf2ba29..53cc1060 100644
--- a/.github/workflows/Ubuntu.yml
+++ b/.github/workflows/Ubuntu.yml
@@ -70,7 +70,7 @@ jobs:
       TestingDomain: ${{ matrix.TestingDomain }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat wget
     - name: Start StepCA
diff --git a/.github/workflows/Windows.yml b/.github/workflows/Windows.yml
index c02e2f77..61ef5ad8 100644
--- a/.github/workflows/Windows.yml
+++ b/.github/workflows/Windows.yml
@@ -49,7 +49,7 @@ jobs:
     - name: Set git to use LF
       run: |
           git config --global core.autocrlf false
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install cygwin base packages with chocolatey
       run: |
           choco config get cacheLocation
diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml
index 48c44429..ea446d84 100644
--- a/.github/workflows/dockerhub.yml
+++ b/.github/workflows/dockerhub.yml
@@ -41,7 +41,7 @@ jobs:
     if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
     steps:
       - name: checkout code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Set up QEMU
         uses: docker/setup-qemu-action@v2
       - name: Set up Docker Buildx
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index a5a08bbf..746727d4 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -22,7 +22,7 @@ jobs:
   ShellCheck:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install Shellcheck
       run: sudo apt-get install -y shellcheck
     - name: DoShellcheck
@@ -31,7 +31,7 @@ jobs:
   shfmt:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install shfmt
       run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
     - name: shfmt

From f364d4fbefa8fca1b8e78745247d916a96241c5e Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Tue, 21 Nov 2023 08:45:54 +0100
Subject: [PATCH 80/88] fix

---
 .github/workflows/NetBSD.yml | 142 +++++++++++++++++------------------
 1 file changed, 71 insertions(+), 71 deletions(-)

diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml
index e9301860..e5206913 100644
--- a/.github/workflows/NetBSD.yml
+++ b/.github/workflows/NetBSD.yml
@@ -1,71 +1,71 @@
-name: NetBSD
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/NetBSD.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/NetBSD.yml'
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-
-
-
-jobs:
-  NetBSD:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "githubtest@acme.sh"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: ubuntu-latest
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-    steps:
-    - uses: actions/checkout@v4
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/netbsd-vm@v1
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
-        nat: |
-          "8080": "80"
-        prepare: |
-          pkg_add curl socat
-        usesh: true
-        copyback: false
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
-
+name: NetBSD
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/NetBSD.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/NetBSD.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+
+jobs:
+  NetBSD:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "githubtest@acme.sh"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/netbsd-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
+        nat: |
+          "8080": "80"
+        prepare: |
+          pkg_add curl socat
+        usesh: true
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

From a4bd89c93857eeed29653978aee66da753119f95 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Tue, 21 Nov 2023 09:00:22 +0100
Subject: [PATCH 81/88] fix

---
 .github/workflows/NetBSD.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/NetBSD.yml b/.github/workflows/NetBSD.yml
index e5206913..a5565d09 100644
--- a/.github/workflows/NetBSD.yml
+++ b/.github/workflows/NetBSD.yml
@@ -61,7 +61,7 @@ jobs:
         nat: |
           "8080": "80"
         prepare: |
-          pkg_add curl socat
+          /usr/sbin/pkg_add curl socat
         usesh: true
         copyback: false
         run: |

From 3b7bc5a56ad5046b63c747923c510c1e6a51bb95 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sat, 2 Dec 2023 22:50:59 +0100
Subject: [PATCH 82/88] update dragonflybsd-vm@v1

---
 .github/workflows/DNS.yml          |  32 +++----
 .github/workflows/DragonFlyBSD.yml | 142 ++++++++++++++---------------
 2 files changed, 87 insertions(+), 87 deletions(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 507755c9..6e68df9c 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -65,7 +65,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
     - name: Set env file
@@ -113,7 +113,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run:  brew install socat
     - name: Clone acmetest
@@ -164,7 +164,7 @@ jobs:
     - name: Set git to use LF
       run: |
           git config --global core.autocrlf false
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install cygwin base packages with chocolatey
       run: |
           choco config get cacheLocation
@@ -204,7 +204,7 @@ jobs:
 
 
   FreeBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: Windows
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -223,10 +223,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0
+    - uses: vmactions/freebsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: pkg install -y socat curl
@@ -255,7 +255,7 @@ jobs:
 
 
   OpenBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: FreeBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -274,10 +274,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/openbsd-vm@v0
+    - uses: vmactions/openbsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: pkg_add socat curl
@@ -306,7 +306,7 @@ jobs:
 
 
   NetBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: OpenBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -325,10 +325,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/netbsd-vm@v0
+    - uses: vmactions/netbsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
@@ -358,7 +358,7 @@ jobs:
 
 
   DragonFlyBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: NetBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -377,10 +377,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/dragonflybsd-vm@v0
+    - uses: vmactions/dragonflybsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
@@ -433,7 +433,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
     - uses: vmactions/solaris-vm@v0
diff --git a/.github/workflows/DragonFlyBSD.yml b/.github/workflows/DragonFlyBSD.yml
index 5a0d81ba..3d4f650d 100644
--- a/.github/workflows/DragonFlyBSD.yml
+++ b/.github/workflows/DragonFlyBSD.yml
@@ -1,71 +1,71 @@
-name: DragonFlyBSD
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/DragonFlyBSD.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/DragonFlyBSD.yml'
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-
-
-
-jobs:
-  DragonFlyBSD:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "githubtest@acme.sh"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-    steps:
-    - uses: actions/checkout@v3
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/dragonflybsd-vm@v0
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
-        copyback: "false"
-        nat: |
-          "8080": "80"
-        prepare: |
-          pkg install -y curl socat libnghttp2
-        usesh: true
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
-
+name: DragonFlyBSD
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/DragonFlyBSD.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/DragonFlyBSD.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+jobs:
+  DragonFlyBSD:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "githubtest@acme.sh"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/dragonflybsd-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
+        copyback: "false"
+        nat: |
+          "8080": "80"
+        prepare: |
+          pkg install -y curl socat libnghttp2
+        usesh: true
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

From a12a3640a7a887bf3695acc08b1e29d4b2342242 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sun, 3 Dec 2023 14:40:32 +0100
Subject: [PATCH 83/88] update

---
 .github/workflows/DragonFlyBSD.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/DragonFlyBSD.yml b/.github/workflows/DragonFlyBSD.yml
index 3d4f650d..f360f85c 100644
--- a/.github/workflows/DragonFlyBSD.yml
+++ b/.github/workflows/DragonFlyBSD.yml
@@ -57,13 +57,13 @@ jobs:
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
     - uses: vmactions/dragonflybsd-vm@v1
       with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
-        copyback: "false"
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |
           "8080": "80"
         prepare: |
           pkg install -y curl socat libnghttp2
         usesh: true
+        copyback: false
         run: |
           cd ../acmetest \
           && ./letest.sh

From f71d8d7348934e8a4f6835431d74fdd5a1bce0f2 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Sun, 3 Dec 2023 14:44:23 +0100
Subject: [PATCH 84/88] minor

---
 .github/workflows/Solaris.yml | 148 +++++++++++++++++-----------------
 1 file changed, 74 insertions(+), 74 deletions(-)

diff --git a/.github/workflows/Solaris.yml b/.github/workflows/Solaris.yml
index 34d31a59..4ae46d79 100644
--- a/.github/workflows/Solaris.yml
+++ b/.github/workflows/Solaris.yml
@@ -1,74 +1,74 @@
-name: Solaris
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/Solaris.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/Solaris.yml'
-
-
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-jobs:
-  Solaris:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-           ACME_USE_WGET: 1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "githubtest@acme.sh"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
-    steps:
-    - uses: actions/checkout@v3
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/solaris-vm@v0
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
-        copyback: "false"
-        nat: |
-          "8080": "80"
-        prepare: pkgutil -y -i socat curl wget
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
+name: Solaris
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/Solaris.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/Solaris.yml'
+
+
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  Solaris:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+           ACME_USE_WGET: 1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "githubtest@acme.sh"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: macos-12
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/solaris-vm@v0
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+        copyback: "false"
+        nat: |
+          "8080": "80"
+        prepare: pkgutil -y -i socat curl wget
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+

From 50f6a459cf66912a01738eaa8db69178f9be03fb Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Mon, 4 Dec 2023 09:41:39 +0100
Subject: [PATCH 85/88] update solaris

---
 .github/workflows/DNS.yml     |  4 ++--
 .github/workflows/Solaris.yml | 11 ++++++-----
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 6e68df9c..95fad2d1 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -413,7 +413,7 @@ jobs:
 
 
   Solaris:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: DragonFlyBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -436,7 +436,7 @@ jobs:
     - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/solaris-vm@v0
+    - uses: vmactions/solaris-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         copyback: false
diff --git a/.github/workflows/Solaris.yml b/.github/workflows/Solaris.yml
index 4ae46d79..bdd3f040 100644
--- a/.github/workflows/Solaris.yml
+++ b/.github/workflows/Solaris.yml
@@ -14,12 +14,12 @@ on:
       - '*.sh'
       - '.github/workflows/Solaris.yml'
 
-
-
 concurrency: 
   group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
+
+
 jobs:
   Solaris:
     strategy:
@@ -41,7 +41,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "githubtest@acme.sh"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -61,14 +61,15 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/solaris-vm@v0
+    - uses: vmactions/solaris-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
-        copyback: "false"
         nat: |
           "8080": "80"
         prepare: pkgutil -y -i socat curl wget
+        copyback: false
         run: |
           cd ../acmetest \
           && ./letest.sh
 
+

From f0ac566c9369cbc64daf72c9c9449d20c181fb04 Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Mon, 4 Dec 2023 23:51:06 +0100
Subject: [PATCH 86/88] add Omnios

---
 .github/workflows/DNS.yml    | 51 ++++++++++++++++++++++++
 .github/workflows/Omnios.yml | 75 ++++++++++++++++++++++++++++++++++++
 README.md                    |  2 +-
 3 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 .github/workflows/Omnios.yml

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 95fad2d1..9373fdaf 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -463,3 +463,54 @@ jobs:
           ./letest.sh
 
 
+  Omnios:
+    runs-on: ubuntu-latest
+    needs: DragonFlyBSD
+    env:
+      TEST_DNS : ${{ secrets.TEST_DNS }}
+      TestingDomain: ${{ secrets.TestingDomain }}
+      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
+      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
+      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
+      CASE: le_test_dnsapi
+      TEST_LOCAL: 1
+      DEBUG: ${{ secrets.DEBUG }}
+      http_proxy: ${{ secrets.http_proxy }}
+      https_proxy: ${{ secrets.https_proxy }}
+      HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
+      TokenName1: ${{ secrets.TokenName1}}
+      TokenName2: ${{ secrets.TokenName2}}
+      TokenName3: ${{ secrets.TokenName3}}
+      TokenName4: ${{ secrets.TokenName4}}
+      TokenName5: ${{ secrets.TokenName5}}
+    steps:
+    - uses: actions/checkout@v4
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/omnios-vm@v1
+      with:
+        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
+        copyback: false
+        prepare: pkgutil -y -i socat
+        run: |
+          pkg set-mediator -v -I default@1.1 openssl
+          export PATH=/usr/gnu/bin:$PATH
+          if [ "${{ secrets.TokenName1}}" ] ; then
+            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
+          fi
+          if [ "${{ secrets.TokenName2}}" ] ; then
+            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
+          fi
+          if [ "${{ secrets.TokenName3}}" ] ; then
+            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
+          fi
+          if [ "${{ secrets.TokenName4}}" ] ; then
+            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
+          fi
+          if [ "${{ secrets.TokenName5}}" ] ; then
+            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
+          fi
+          cd ../acmetest
+          ./letest.sh
+
+
diff --git a/.github/workflows/Omnios.yml b/.github/workflows/Omnios.yml
new file mode 100644
index 00000000..e3da0be8
--- /dev/null
+++ b/.github/workflows/Omnios.yml
@@ -0,0 +1,75 @@
+name: Omnios
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/Omnios.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/Omnios.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+jobs:
+  Omnios:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+           ACME_USE_WGET: 1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "githubtest@acme.sh"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/omnios-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+        nat: |
+          "8080": "80"
+        prepare: pkg install socat wget
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+
diff --git a/README.md b/README.md
index 717ecf5f..a64ba4d7 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
 [![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
 [![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
 [![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
-
+[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)
 
 ![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
 ![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg)

From f4315e2c6f6ba22185fb92102ee592f824ef8a8e Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Tue, 5 Dec 2023 19:33:10 +0100
Subject: [PATCH 87/88] fix _date2time

---
 .github/workflows/DNS.yml | 4 ++--
 acme.sh                   | 4 ++++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/DNS.yml b/.github/workflows/DNS.yml
index 9373fdaf..bf56c1d6 100644
--- a/.github/workflows/DNS.yml
+++ b/.github/workflows/DNS.yml
@@ -465,7 +465,7 @@ jobs:
 
   Omnios:
     runs-on: ubuntu-latest
-    needs: DragonFlyBSD
+    needs: Solaris
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TestingDomain: ${{ secrets.TestingDomain }}
@@ -491,7 +491,7 @@ jobs:
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         copyback: false
-        prepare: pkgutil -y -i socat
+        prepare: pkg install socat
         run: |
           pkg set-mediator -v -I default@1.1 openssl
           export PATH=/usr/gnu/bin:$PATH
diff --git a/acme.sh b/acme.sh
index 3fdaa804..75030acd 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1795,6 +1795,10 @@ _date2time() {
   if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
     return
   fi
+  #Omnios
+  if da="$(echo "$1" | tr -d "Z" | tr "T" ' ')" perl -MTime::Piece -e 'print Time::Piece->strptime($ENV{da}, "%Y-%m-%d %H:%M:%S")->epoch, "\n";' 2>/dev/null; then
+    return
+  fi
   _err "Can not parse _date2time $1"
   return 1
 }

From 8cb1b6b5d548c5561060087cd81cfd9c8f8ab2fa Mon Sep 17 00:00:00 2001
From: neil <neilgit@neilpang.com>
Date: Tue, 5 Dec 2023 20:19:40 +0100
Subject: [PATCH 88/88] update

---
 README.md | 29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index a64ba4d7..9a5c106b 100644
--- a/README.md
+++ b/README.md
@@ -73,20 +73,21 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 |7|[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
 |8|[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
 |9|[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
-|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
-|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
-|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
-|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
-|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
-|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
-|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
-|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
-|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
-|11|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
-|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
-|23|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
+|10|[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
+|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
+|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
+|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
+|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
+|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
+|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
+|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
+|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
+|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
+|22|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
+|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
+|24|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
 
 
 Check our [testing project](https://github.com/acmesh-official/acmetest):