From 7ca8a9e449a16969b30f5c69420f55b14edec6b0 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 24 Oct 2025 11:34:25 -0400 Subject: [PATCH 01/38] QUIC.cloud support for acme.sh --- dnsapi/dns_qc.sh | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100755 dnsapi/dns_qc.sh diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh new file mode 100755 index 00000000..64956bd5 --- /dev/null +++ b/dnsapi/dns_qc.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_qc_info='QUIC.cloud +Site: quic.cloud +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_qc +Options: + QC_API_KEY QC API Key + QC_API_EMAIL Your account email +' + +QC_Api="https://api.quic.cloud/v2" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_qc_add() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + if [ "$QC_API_KEY" ]; then + _savedomainconf QC_API_KEY "$QC_API_KEY" + else + _err "You didn't specify a QUIC.cloud are api key and email yet." + _err "You can get yours from here https://my.quic.cloud/up/api." + return 1 + fi + + if ! _contains "$QC_API_EMAIL" "@"; then + _err "It seems that the QC_API_EMAIL=$QC_API_EMAIL is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error failed response from QC GET: $response" + return 1 + fi + + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then + _info "Adding record" + if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then + if _contains "$response" "$txtvalue"; then + _info "Added, OK" + return 0 + elif _contains "$response" "Same record already exists"; then + _info "Already exists, OK" + return 0 + else + _err "Add txt record error: $response" + return 1 + fi + fi + _err "Add txt record error: POST failed: $response" + return 1 + +} + +#fulldomain txtvalue +dns_qc_rm() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_rm fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error rm GET response: $response" + return 1 + fi + + response=$(echo "$response"|jq ".result[] | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + if [ "${response}" = "" ]; then + _info "Don't need to remove." + else + record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') + _debug "record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _info "TXT Record ID: $record_id successfully deleted" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=1 + p=1 + + h=$(printf "%s" "$domain" | cut -d . -f2-) + _debug h "$h" + if [ -z "$h" ]; then + _err "$h ($domain) is an invalid domain" + return 1 + fi + + if ! _qc_rest GET "zones"; then + _debug "qc_rest failed" + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" || _contains "$response" "\"name\":\"$h.\""; then + _domain_id=$h + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + _domain=$h + return 0 + fi + _err "Empty domain_id $h" + return 1 + fi + _err "Missing domain_id $h" + return 1 +} + +_qc_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + email_trimmed=$(echo "$QC_API_EMAIL" | tr -d '"') + token_trimmed=$(echo "$QC_API_KEY" | tr -d '"') + + export _H1="Content-Type: application/json" + export _H2="X-Auth-Email: $email_trimmed" + export _H3="X-Auth-Key: $token_trimmed" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$QC_Api/$ep" "" "$m")" + else + response="$(_get "$QC_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From e25e30dcdd5e20ab3ff27f9fb1abe1027090e103 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 27 Oct 2025 12:00:01 -0400 Subject: [PATCH 02/38] Added wiki doc --- wiki/dnsapi/dns_qc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 wiki/dnsapi/dns_qc diff --git a/wiki/dnsapi/dns_qc b/wiki/dnsapi/dns_qc new file mode 100644 index 00000000..50b1879e --- /dev/null +++ b/wiki/dnsapi/dns_qc @@ -0,0 +1,27 @@ +# Use QUIC.cloud DNS API + +This uses the QUIC.cloud DNS API. + +## Obtain an API key from the QUIC.cloude system. If you do not already have one, once logged into QUIC.cloud: + +- Select the Human icon at the top right of the screen and select **Edit Profile** +- On the left side of the screen, press the **API Access** item. +- Press the **Generate Key** button. It will present you with a token you will need below. + +## Use the API + +You will need to provide 2 environment variables to acme.sh: + +- **QC_API_KEY**: This is the API Token value obtained in the QUIC.cloud screens. +- **QC_API_EMAIL**: This is the email you used in your QUIC.cloud configuration + +## Using in OpenLiteSpeed + +This feature is fully supported and documented in version 1.9 and later of OpenLiteSpeed. See the OpenLiteSpeed (documentation)[https://docs.openlitespeed.org/config/advanced/acme/). + +## License + +Copyright: acme.sh wiki contributors + +License: GNU General Public License version 3 or any later version + From 68eb6defd356a62dd0b6c8255846f2851e667da3 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 27 Oct 2025 12:04:08 -0400 Subject: [PATCH 03/38] Removed false wiki page --- wiki/dnsapi/dns_qc | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 wiki/dnsapi/dns_qc diff --git a/wiki/dnsapi/dns_qc b/wiki/dnsapi/dns_qc deleted file mode 100644 index 50b1879e..00000000 --- a/wiki/dnsapi/dns_qc +++ /dev/null @@ -1,27 +0,0 @@ -# Use QUIC.cloud DNS API - -This uses the QUIC.cloud DNS API. - -## Obtain an API key from the QUIC.cloude system. If you do not already have one, once logged into QUIC.cloud: - -- Select the Human icon at the top right of the screen and select **Edit Profile** -- On the left side of the screen, press the **API Access** item. -- Press the **Generate Key** button. It will present you with a token you will need below. - -## Use the API - -You will need to provide 2 environment variables to acme.sh: - -- **QC_API_KEY**: This is the API Token value obtained in the QUIC.cloud screens. -- **QC_API_EMAIL**: This is the email you used in your QUIC.cloud configuration - -## Using in OpenLiteSpeed - -This feature is fully supported and documented in version 1.9 and later of OpenLiteSpeed. See the OpenLiteSpeed (documentation)[https://docs.openlitespeed.org/config/advanced/acme/). - -## License - -Copyright: acme.sh wiki contributors - -License: GNU General Public License version 3 or any later version - From 9a74c86327bd4731f1bfc3494df2b8a911548555 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Wed, 19 Nov 2025 09:06:55 -0500 Subject: [PATCH 04/38] Commit to force initial test --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 64956bd5..aa4ff891 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -141,7 +141,7 @@ _get_root() { fi if ! _qc_rest GET "zones"; then - _debug "qc_rest failed" + _err "qc_rest failed" return 1 fi From 9381835a7c48e432b440d0dab24ed5aba683ee35 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 24 Oct 2025 11:34:25 -0400 Subject: [PATCH 05/38] QUIC.cloud support for acme.sh --- dnsapi/dns_qc.sh | 188 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100755 dnsapi/dns_qc.sh diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh new file mode 100755 index 00000000..64956bd5 --- /dev/null +++ b/dnsapi/dns_qc.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_qc_info='QUIC.cloud +Site: quic.cloud +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_qc +Options: + QC_API_KEY QC API Key + QC_API_EMAIL Your account email +' + +QC_Api="https://api.quic.cloud/v2" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_qc_add() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + if [ "$QC_API_KEY" ]; then + _savedomainconf QC_API_KEY "$QC_API_KEY" + else + _err "You didn't specify a QUIC.cloud are api key and email yet." + _err "You can get yours from here https://my.quic.cloud/up/api." + return 1 + fi + + if ! _contains "$QC_API_EMAIL" "@"; then + _err "It seems that the QC_API_EMAIL=$QC_API_EMAIL is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error failed response from QC GET: $response" + return 1 + fi + + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then + _info "Adding record" + if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then + if _contains "$response" "$txtvalue"; then + _info "Added, OK" + return 0 + elif _contains "$response" "Same record already exists"; then + _info "Already exists, OK" + return 0 + else + _err "Add txt record error: $response" + return 1 + fi + fi + _err "Add txt record error: POST failed: $response" + return 1 + +} + +#fulldomain txtvalue +dns_qc_rm() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_rm fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error rm GET response: $response" + return 1 + fi + + response=$(echo "$response"|jq ".result[] | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + if [ "${response}" = "" ]; then + _info "Don't need to remove." + else + record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') + _debug "record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get record id to remove." + return 1 + fi + if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then + _err "Delete record error." + return 1 + fi + _info "TXT Record ID: $record_id successfully deleted" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=1 + p=1 + + h=$(printf "%s" "$domain" | cut -d . -f2-) + _debug h "$h" + if [ -z "$h" ]; then + _err "$h ($domain) is an invalid domain" + return 1 + fi + + if ! _qc_rest GET "zones"; then + _debug "qc_rest failed" + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" || _contains "$response" "\"name\":\"$h.\""; then + _domain_id=$h + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + _domain=$h + return 0 + fi + _err "Empty domain_id $h" + return 1 + fi + _err "Missing domain_id $h" + return 1 +} + +_qc_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + email_trimmed=$(echo "$QC_API_EMAIL" | tr -d '"') + token_trimmed=$(echo "$QC_API_KEY" | tr -d '"') + + export _H1="Content-Type: application/json" + export _H2="X-Auth-Email: $email_trimmed" + export _H3="X-Auth-Key: $token_trimmed" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$QC_Api/$ep" "" "$m")" + else + response="$(_get "$QC_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 72a6a5ce047d6376744bc9a7813f58fbdbf036fc Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 27 Oct 2025 12:00:01 -0400 Subject: [PATCH 06/38] Added wiki doc --- wiki/dnsapi/dns_qc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 wiki/dnsapi/dns_qc diff --git a/wiki/dnsapi/dns_qc b/wiki/dnsapi/dns_qc new file mode 100644 index 00000000..50b1879e --- /dev/null +++ b/wiki/dnsapi/dns_qc @@ -0,0 +1,27 @@ +# Use QUIC.cloud DNS API + +This uses the QUIC.cloud DNS API. + +## Obtain an API key from the QUIC.cloude system. If you do not already have one, once logged into QUIC.cloud: + +- Select the Human icon at the top right of the screen and select **Edit Profile** +- On the left side of the screen, press the **API Access** item. +- Press the **Generate Key** button. It will present you with a token you will need below. + +## Use the API + +You will need to provide 2 environment variables to acme.sh: + +- **QC_API_KEY**: This is the API Token value obtained in the QUIC.cloud screens. +- **QC_API_EMAIL**: This is the email you used in your QUIC.cloud configuration + +## Using in OpenLiteSpeed + +This feature is fully supported and documented in version 1.9 and later of OpenLiteSpeed. See the OpenLiteSpeed (documentation)[https://docs.openlitespeed.org/config/advanced/acme/). + +## License + +Copyright: acme.sh wiki contributors + +License: GNU General Public License version 3 or any later version + From cf5fd403e85c06c1c5ec4110e8112b50308b1fa2 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 27 Oct 2025 12:04:08 -0400 Subject: [PATCH 07/38] Removed false wiki page --- wiki/dnsapi/dns_qc | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 wiki/dnsapi/dns_qc diff --git a/wiki/dnsapi/dns_qc b/wiki/dnsapi/dns_qc deleted file mode 100644 index 50b1879e..00000000 --- a/wiki/dnsapi/dns_qc +++ /dev/null @@ -1,27 +0,0 @@ -# Use QUIC.cloud DNS API - -This uses the QUIC.cloud DNS API. - -## Obtain an API key from the QUIC.cloude system. If you do not already have one, once logged into QUIC.cloud: - -- Select the Human icon at the top right of the screen and select **Edit Profile** -- On the left side of the screen, press the **API Access** item. -- Press the **Generate Key** button. It will present you with a token you will need below. - -## Use the API - -You will need to provide 2 environment variables to acme.sh: - -- **QC_API_KEY**: This is the API Token value obtained in the QUIC.cloud screens. -- **QC_API_EMAIL**: This is the email you used in your QUIC.cloud configuration - -## Using in OpenLiteSpeed - -This feature is fully supported and documented in version 1.9 and later of OpenLiteSpeed. See the OpenLiteSpeed (documentation)[https://docs.openlitespeed.org/config/advanced/acme/). - -## License - -Copyright: acme.sh wiki contributors - -License: GNU General Public License version 3 or any later version - From d0d97a40a62c85efcfdb96c9affd682847889c1f Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Wed, 19 Nov 2025 09:06:55 -0500 Subject: [PATCH 08/38] Commit to force initial test --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 64956bd5..aa4ff891 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -141,7 +141,7 @@ _get_root() { fi if ! _qc_rest GET "zones"; then - _debug "qc_rest failed" + _err "qc_rest failed" return 1 fi From b500ac3dbbd20d70de487636c2486375aade488a Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Thu, 20 Nov 2025 15:38:56 -0500 Subject: [PATCH 09/38] Updated secret and dns_qc.sh --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index aa4ff891..1eeef97f 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -107,7 +107,7 @@ dns_qc_rm() { if [ "${response}" = "" ]; then _info "Don't need to remove." else - record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') + record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "record_id" "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id to remove." From 0f42b06b48b488fc3b90fd40671673acd1066735 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 08:02:59 -0500 Subject: [PATCH 10/38] Trying again to fix shfmt error --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 1eeef97f..66444aa6 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -103,7 +103,7 @@ dns_qc_rm() { return 1 fi - response=$(echo "$response"|jq ".result[] | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + response=$(echo "$response"|jq ".result[]" | select(.content == \"$txtvalue\") | select(.type == \"TXT\")) if [ "${response}" = "" ]; then _info "Don't need to remove." else From 90d2ff8fad85f4bd92aafabaf24928435421c455 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 15:27:38 -0500 Subject: [PATCH 11/38] Better fixes for shfmt errors --- dnsapi/dns_qc.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 66444aa6..0ec53864 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -59,13 +59,13 @@ dns_qc_add() { # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) # _debug count "$count" # if [ "$count" = "0" ]; then - _info "Adding record" + _info "Adding txt record" if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then if _contains "$response" "$txtvalue"; then - _info "Added, OK" + _info "Added txt record, OK" return 0 elif _contains "$response" "Same record already exists"; then - _info "Already exists, OK" + _info "txt record already exists, OK" return 0 else _err "Add txt record error: $response" @@ -103,18 +103,19 @@ dns_qc_rm() { return 1 fi - response=$(echo "$response"|jq ".result[]" | select(.content == \"$txtvalue\") | select(.type == \"TXT\")) + response=$(echo "$response"|jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + _debug get txt response "$response" if [ "${response}" = "" ]; then - _info "Don't need to remove." + _info "Don't need to remove txt records." else record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') - _debug "record_id" "$record_id" + _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then - _err "Can not get record id to remove." + _err "Can not get txt record id to remove." return 1 fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then - _err "Delete record error." + _err "Delete txt record error." return 1 fi _info "TXT Record ID: $record_id successfully deleted" From 5e76ea820ca42f76055e57de5769325a4c34531b Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 16:01:14 -0500 Subject: [PATCH 12/38] Additional shfmt issues --- dnsapi/dns_qc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 0ec53864..ab02f17e 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -104,18 +104,18 @@ dns_qc_rm() { fi response=$(echo "$response"|jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") - _debug get txt response "$response" + _debug "get txt response" "$response" if [ "${response}" = "" ]; then _info "Don't need to remove txt records." else record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then - _err "Can not get txt record id to remove." + _info "Can not get txt record id to remove." return 1 fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then - _err "Delete txt record error." + _info "Delete txt record error." return 1 fi _info "TXT Record ID: $record_id successfully deleted" From ded539b11c365359082c3801fb2057593cae535f Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 16:04:23 -0500 Subject: [PATCH 13/38] Additional shfmt issues --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index ab02f17e..723bbea8 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -112,7 +112,7 @@ dns_qc_rm() { _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then _info "Can not get txt record id to remove." - return 1 + return 0 fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then _info "Delete txt record error." From 20ef8cd369f712d08bbb094f1dd95809065fcd6d Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 16:22:42 -0500 Subject: [PATCH 14/38] Additional shfmt issues --- dnsapi/dns_qc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 723bbea8..ef246a2b 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -111,7 +111,7 @@ dns_qc_rm() { record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then - _info "Can not get txt record id to remove." + #_info "Can not get txt record id to remove." return 0 fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then @@ -120,7 +120,7 @@ dns_qc_rm() { fi _info "TXT Record ID: $record_id successfully deleted" fi - + return 0 } #################### Private functions below ################################## From 88e9681481b3694b2fab4cf7663306eca8c6e386 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 21 Nov 2025 16:26:24 -0500 Subject: [PATCH 15/38] Additional shfmt issues --- dnsapi/dns_qc.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index ef246a2b..c30d4595 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -110,10 +110,6 @@ dns_qc_rm() { else record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "txt record_id" "$record_id" - if [ -z "$record_id" ]; then - #_info "Can not get txt record id to remove." - return 0 - fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then _info "Delete txt record error." return 1 From d3930639db5a0a0cd6fbf75d02d8c538568a1816 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 5 Dec 2025 08:58:41 -0500 Subject: [PATCH 16/38] Updated secrets and put back guards --- dnsapi/dns_qc.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index c30d4595..3ea8403c 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -110,6 +110,10 @@ dns_qc_rm() { else record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "txt record_id" "$record_id" + if [ -z "$record_id" ]; then + _err "Can not get txt record id to remove." + return 1 + fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then _info "Delete txt record error." return 1 From 67a389cbbf781492bbb9d554d8f0da45c77e5da3 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 5 Dec 2025 10:35:52 -0500 Subject: [PATCH 17/38] Minor change and setup secrets again --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 3ea8403c..6d1a9eb4 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -111,7 +111,7 @@ dns_qc_rm() { record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then - _err "Can not get txt record id to remove." + _err "Can not get txt record id to remove. Run in debug mode." return 1 fi if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then From 6b6d22c5ba0d7645444d26629793d22f51e19841 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 5 Dec 2025 13:52:59 -0500 Subject: [PATCH 18/38] shfmt updates --- dnsapi/dns_qc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 6d1a9eb4..6d4f7299 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -34,7 +34,7 @@ dns_qc_add() { _err "Please check and retry." return 1 fi - #save the api key and email to the account conf file. + #save the api key and email to the account conf file. _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" _debug "First detect the root zone" @@ -103,12 +103,12 @@ dns_qc_rm() { return 1 fi - response=$(echo "$response"|jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + response=$(echo "$response" | jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") _debug "get txt response" "$response" if [ "${response}" = "" ]; then _info "Don't need to remove txt records." else - record_id=$(echo "$response" | grep \"id\"| awk -F ' ' '{print $2}'| sed 's/,$//') + record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') _debug "txt record_id" "$record_id" if [ -z "$record_id" ]; then _err "Can not get txt record id to remove. Run in debug mode." From a1857af6de5dcee0ec109a36b45dbb4b0326e067 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 5 Dec 2025 15:53:17 -0500 Subject: [PATCH 19/38] Update error message and secrets --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 6d4f7299..cea411e8 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -24,7 +24,7 @@ dns_qc_add() { if [ "$QC_API_KEY" ]; then _savedomainconf QC_API_KEY "$QC_API_KEY" else - _err "You didn't specify a QUIC.cloud are api key and email yet." + _err "You didn't specify a QUIC.cloud api key as QC_API_KEY." _err "You can get yours from here https://my.quic.cloud/up/api." return 1 fi From ed1bd01592afba97749f2b51573b8c35f2372c31 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Fri, 5 Dec 2025 16:29:49 -0500 Subject: [PATCH 20/38] Save account information differently --- dnsapi/dns_qc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index cea411e8..7ae2f1cd 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -22,7 +22,7 @@ dns_qc_add() { QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" if [ "$QC_API_KEY" ]; then - _savedomainconf QC_API_KEY "$QC_API_KEY" + _saveaccountconf_mutable QC_API_KEY "$QC_API_KEY" else _err "You didn't specify a QUIC.cloud api key as QC_API_KEY." _err "You can get yours from here https://my.quic.cloud/up/api." @@ -35,7 +35,7 @@ dns_qc_add() { return 1 fi #save the api key and email to the account conf file. - _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" + _saveaccountconf_mutable QC_API_EMAIL "$QC_API_EMAIL" _debug "First detect the root zone" if ! _get_root "$fulldomain"; then From 875cf056b7e293c67b4ae9549455a20fa798da62 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 8 Dec 2025 08:40:44 -0500 Subject: [PATCH 21/38] Submit dns_qc.sh for review --- dns_qc.sh | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100755 dns_qc.sh diff --git a/dns_qc.sh b/dns_qc.sh new file mode 100755 index 00000000..78a243ae --- /dev/null +++ b/dns_qc.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env sh +# shellcheck disable=SC2034 +dns_qc_info='QUIC.cloud +Site: quic.cloud +Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_qc +Options: + QC_API_KEY QC API Key + QC_API_EMAIL Your account email +' + +QC_Api="https://api.quic.cloud/v2" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_qc_add() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + if [ "$QC_API_KEY" ]; then + _savedomainconf QC_API_KEY "$QC_API_KEY" + else + _err "You didn't specify a QUIC.cloud are api key and email yet." + _err "You can get yours from here https://my.quic.cloud/up/api." + return 1 + fi + + if ! _contains "$QC_API_EMAIL" "@"; then + _err "It seems that the QC_API_EMAIL=$QC_API_EMAIL is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error failed response from QC GET: $response" + return 1 + fi + + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then + _info "Adding txt record" + if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then + if _contains "$response" "$txtvalue"; then + _info "Added txt record, OK" + return 0 + elif _contains "$response" "Same record already exists"; then + _info "txt record already exists, OK" + return 0 + else + _err "Add txt record error: $response" + return 1 + fi + fi + _err "Add txt record error: POST failed: $response" + return 1 + +} + +#fulldomain txtvalue +dns_qc_rm() { + fulldomain=$1 + txtvalue=$2 + + _debug "Enter dns_qc_rm fulldomain: $fulldomain, txtvalue: $txtvalue" + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" + QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + _debug _domain_id "$_domain_id" + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug "Getting txt records" + _qc_rest GET "zones/${_domain_id}/records" + + if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then + _err "Error rm GET response: $response" + return 1 + fi + + response=$(echo "$response" | jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + _debug "get txt response" "$response" + if [ "${response}" = "" ]; then + _info "Don't need to remove txt records." + else + record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') + _debug "txt record_id" "$record_id" + + if [[ -z "$record_id" ]]; then + _err "Can not get txt record id to remove. Run in debug mode." + return 1 + fi + + if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then + _info "Delete txt record error." + return 1 + fi + + _info "TXT Record ID: $record_id successfully deleted" + fi + +} + +#################### Private functions below ################################# +#_acme-challenge.www.domain.com +#returns +#_sub_domain=_acme-challenge.www +#_domain=domain.com +#_domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + p=1 + h=$(printf "%s" "$domain" | cut -d . -f2-) + _debug h "$h" + + if [[ -z "$h" ]]; then + _err "$h ($domain) is an invalid domain" + return 1 + fi + + if ! _qc_rest GET "zones"; then + _err "qc_rest failed" + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" || _contains "$response" "\"name\":\"$h.\""; then + _domain_id=$h + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") + _domain=$h + return 0 + fi + + _err "Empty domain_id $h" + return 1 + fi + _err "Missing domain_id $h" + return 1 + +} + +_qc_rest() { + + m="$1" + ep="$2" + data="$3" + + _debug "$ep" + + email_trimmed=$(echo "$QC_API_EMAIL" | tr -d '"') + + token_trimmed=$(echo "$QC_API_KEY" | tr -d '"') + + export _H1="Content-Type: application/json" + + export _H2="X-Auth-Email: $email_trimmed" + + export _H3="X-Auth-Key: $token_trimmed" + + if [[ "$m" != "GET" ]]; then + _debug data "$data" + response="$(_post "$data" "$QC_Api/$ep" "" "$m")" + + else + response="$(_get "$QC_Api/$ep")" + + fi + + if [[ "$?" != "0" ]]; then + _err "error $ep" + return 1 + fi + + _debug2 response "$response" + +} From 5fcca7c7e0e8144e555987328caccf60eaa533d3 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 8 Dec 2025 08:48:30 -0500 Subject: [PATCH 22/38] Retry correct commit --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 7ae2f1cd..0765c697 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -17,7 +17,7 @@ dns_qc_add() { fulldomain=$1 txtvalue=$2 - _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" + _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue." QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" From e5dea48d3cc3bdb49696398e5d17edb515046279 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 9 Dec 2025 07:40:00 -0500 Subject: [PATCH 23/38] Retry pull request with HTTPS_INSECURE=1 --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 0765c697..7ae2f1cd 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -17,7 +17,7 @@ dns_qc_add() { fulldomain=$1 txtvalue=$2 - _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue." + _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" From 5017c12324967423cb44ae7d0b6d5b9ee354317d Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Thu, 11 Dec 2025 09:03:38 -0500 Subject: [PATCH 24/38] Trying verification again --- dnsapi/dns_qc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 7ae2f1cd..2376d718 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -39,7 +39,7 @@ dns_qc_add() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then - _err "invalid domain" + _err "invalid domain during add" return 1 fi _debug _domain_id "$_domain_id" @@ -59,7 +59,7 @@ dns_qc_add() { # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) # _debug count "$count" # if [ "$count" = "0" ]; then - _info "Adding txt record" + _info "Adding txt record." if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then if _contains "$response" "$txtvalue"; then _info "Added txt record, OK" @@ -88,7 +88,7 @@ dns_qc_rm() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then - _err "invalid domain" + _err "invalid domain during rm" return 1 fi _debug _domain_id "$_domain_id" From 6f66e294defe1b0e1d5de1ff57cd596308ce013a Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Thu, 11 Dec 2025 15:43:15 -0500 Subject: [PATCH 25/38] Yet another try --- dns_qc.sh | 201 ----------------------------------------------- dnsapi/dns_qc.sh | 2 +- 2 files changed, 1 insertion(+), 202 deletions(-) delete mode 100755 dns_qc.sh diff --git a/dns_qc.sh b/dns_qc.sh deleted file mode 100755 index 78a243ae..00000000 --- a/dns_qc.sh +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env sh -# shellcheck disable=SC2034 -dns_qc_info='QUIC.cloud -Site: quic.cloud -Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_qc -Options: - QC_API_KEY QC API Key - QC_API_EMAIL Your account email -' - -QC_Api="https://api.quic.cloud/v2" - -######## Public functions ##################### - -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_qc_add() { - fulldomain=$1 - txtvalue=$2 - - _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" - QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" - QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" - - if [ "$QC_API_KEY" ]; then - _savedomainconf QC_API_KEY "$QC_API_KEY" - else - _err "You didn't specify a QUIC.cloud are api key and email yet." - _err "You can get yours from here https://my.quic.cloud/up/api." - return 1 - fi - - if ! _contains "$QC_API_EMAIL" "@"; then - _err "It seems that the QC_API_EMAIL=$QC_API_EMAIL is not a valid email address." - _err "Please check and retry." - return 1 - fi - #save the api key and email to the account conf file. - _savedomainconf QC_API_EMAIL "$QC_API_EMAIL" - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" - return 1 - fi - _debug _domain_id "$_domain_id" - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - _debug "Getting txt records" - _qc_rest GET "zones/${_domain_id}/records" - - if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then - _err "Error failed response from QC GET: $response" - return 1 - fi - - # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so - # we can not use updating anymore. - # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - # _debug count "$count" - # if [ "$count" = "0" ]; then - _info "Adding txt record" - if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then - if _contains "$response" "$txtvalue"; then - _info "Added txt record, OK" - return 0 - elif _contains "$response" "Same record already exists"; then - _info "txt record already exists, OK" - return 0 - else - _err "Add txt record error: $response" - return 1 - fi - fi - _err "Add txt record error: POST failed: $response" - return 1 - -} - -#fulldomain txtvalue -dns_qc_rm() { - fulldomain=$1 - txtvalue=$2 - - _debug "Enter dns_qc_rm fulldomain: $fulldomain, txtvalue: $txtvalue" - QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" - QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" - return 1 - fi - _debug _domain_id "$_domain_id" - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - - _debug "Getting txt records" - _qc_rest GET "zones/${_domain_id}/records" - - if ! echo "$response" | tr -d " " | grep \"success\":true >/dev/null; then - _err "Error rm GET response: $response" - return 1 - fi - - response=$(echo "$response" | jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") - _debug "get txt response" "$response" - if [ "${response}" = "" ]; then - _info "Don't need to remove txt records." - else - record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') - _debug "txt record_id" "$record_id" - - if [[ -z "$record_id" ]]; then - _err "Can not get txt record id to remove. Run in debug mode." - return 1 - fi - - if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then - _info "Delete txt record error." - return 1 - fi - - _info "TXT Record ID: $record_id successfully deleted" - fi - -} - -#################### Private functions below ################################# -#_acme-challenge.www.domain.com -#returns -#_sub_domain=_acme-challenge.www -#_domain=domain.com -#_domain_id=sdjkglgdfewsdfg -_get_root() { - domain=$1 - p=1 - h=$(printf "%s" "$domain" | cut -d . -f2-) - _debug h "$h" - - if [[ -z "$h" ]]; then - _err "$h ($domain) is an invalid domain" - return 1 - fi - - if ! _qc_rest GET "zones"; then - _err "qc_rest failed" - return 1 - fi - - if _contains "$response" "\"name\":\"$h\"" || _contains "$response" "\"name\":\"$h.\""; then - _domain_id=$h - if [ "$_domain_id" ]; then - _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p") - _domain=$h - return 0 - fi - - _err "Empty domain_id $h" - return 1 - fi - _err "Missing domain_id $h" - return 1 - -} - -_qc_rest() { - - m="$1" - ep="$2" - data="$3" - - _debug "$ep" - - email_trimmed=$(echo "$QC_API_EMAIL" | tr -d '"') - - token_trimmed=$(echo "$QC_API_KEY" | tr -d '"') - - export _H1="Content-Type: application/json" - - export _H2="X-Auth-Email: $email_trimmed" - - export _H3="X-Auth-Key: $token_trimmed" - - if [[ "$m" != "GET" ]]; then - _debug data "$data" - response="$(_post "$data" "$QC_Api/$ep" "" "$m")" - - else - response="$(_get "$QC_Api/$ep")" - - fi - - if [[ "$?" != "0" ]]; then - _err "error $ep" - return 1 - fi - - _debug2 response "$response" - -} diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 2376d718..b7267f63 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -59,7 +59,7 @@ dns_qc_add() { # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) # _debug count "$count" # if [ "$count" = "0" ]; then - _info "Adding txt record." + _info "Adding txt record" if _qc_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":1800}"; then if _contains "$response" "$txtvalue"; then _info "Added txt record, OK" From 1d26d4fc913abf6358e449fc4713b16cc0781b1f Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Mon, 22 Dec 2025 16:42:26 -0500 Subject: [PATCH 26/38] Detect missing jq --- dnsapi/dns_qc.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index b7267f63..81a1e636 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -18,6 +18,11 @@ dns_qc_add() { txtvalue=$2 _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" + if ! _exists jq; then + _err "jq not found" + return 1 + fi + QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" From 94783f46ad0c512b74a4f3ac760860c4ef0c2d61 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 23 Dec 2025 07:53:33 -0500 Subject: [PATCH 27/38] Retry to pass workflow --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 81a1e636..aa58d0dc 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -19,7 +19,7 @@ dns_qc_add() { _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" if ! _exists jq; then - _err "jq not found" + _err "In dns_qc jq not found" return 1 fi From f1aac43f0f9c701e47b8ab52d64495cd9b1d0287 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 23 Dec 2025 09:10:49 -0500 Subject: [PATCH 28/38] Retry for workflow --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index aa58d0dc..ed784f28 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -19,7 +19,7 @@ dns_qc_add() { _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" if ! _exists jq; then - _err "In dns_qc jq not found" + _err "In dns_qc jq not found." return 1 fi From 162cfebbbb1122a53f3b62f46365084a5052cf05 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 15:36:06 -0500 Subject: [PATCH 29/38] Removed jq requirement --- dnsapi/dns_qc.sh | 55 ++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index ed784f28..1073200f 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -18,11 +18,6 @@ dns_qc_add() { txtvalue=$2 _debug "Enter dns_qc_add fulldomain: $fulldomain, txtvalue: $txtvalue" - if ! _exists jq; then - _err "In dns_qc jq not found." - return 1 - fi - QC_API_KEY="${QC_API_KEY:-$(_readaccountconf_mutable QC_API_KEY)}" QC_API_EMAIL="${QC_API_EMAIL:-$(_readaccountconf_mutable QC_API_EMAIL)}" @@ -108,23 +103,43 @@ dns_qc_rm() { return 1 fi - response=$(echo "$response" | jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") - _debug "get txt response" "$response" - if [ "${response}" = "" ]; then - _info "Don't need to remove txt records." - else - record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') - _debug "txt record_id" "$record_id" - if [ -z "$record_id" ]; then - _err "Can not get txt record id to remove. Run in debug mode." - return 1 - fi - if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then - _info "Delete txt record error." - return 1 + _debug "Pre-jq response:" "$response" + # Do not use jq or subsequent code + #response=$(echo "$response" | jq ".result[] | select(.id) | select(.content == \"$txtvalue\") | select(.type == \"TXT\")") + #_debug "get txt response" "$response" + #if [ "${response}" = "" ]; then + # _info "Don't need to remove txt records." + # return 0 + #fi + #record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') + #_debug "txt record_id" "$record_id" + #Instead of jq + array=$(echo $response | grep -o '\[[^]]*\]' | sed 's/^\[\(.*\)\]$/\1/') + if [ -z "$array" ]; then + _err "Expected array in QC response: $response" + return 1 + fi + # Temporary file to hold matched content (one per line) + tmpfile=$(_mktemp) + echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" + while IFS= read -r obj || [ -n "$obj" ]; do + if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then + _debug "response includes" "$obj" + record_id=$(echo $obj | sed 's/^\"id\":\([0-9]\+\).*/\1/' ) + break fi - _info "TXT Record ID: $record_id successfully deleted" + done < $tmpfile + rm $tmpfile + if [ -z "$record_id" ]; then + _info "TXT record, or $txtvalue not found, noting to remove" + return 0 + fi + #End of jq replacement + if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then + _info "Delete txt record error." + return 1 fi + _info "TXT Record ID: $record_id successfully deleted" return 0 } From e031457cfacbf3abd35a51bbc56d443c09a975b4 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 15:48:29 -0500 Subject: [PATCH 30/38] shfmt fixes --- dnsapi/dns_qc.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 1073200f..35e99fa2 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -130,15 +130,18 @@ dns_qc_rm() { fi done < $tmpfile rm $tmpfile + if [ -z "$record_id" ]; then - _info "TXT record, or $txtvalue not found, noting to remove" + _info "TXT record, or $txtvalue not found, nothing to remove" return 0 fi + #End of jq replacement if ! _qc_rest DELETE "zones/$_domain_id/records/$record_id"; then _info "Delete txt record error." return 1 fi + _info "TXT Record ID: $record_id successfully deleted" return 0 } From 185d92f1e7d7ad62d84178d06bce9ae0be49775e Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 15:50:39 -0500 Subject: [PATCH 31/38] shfmt fixes --- dnsapi/dns_qc.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 35e99fa2..68c33ebc 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -141,9 +141,10 @@ dns_qc_rm() { _info "Delete txt record error." return 1 fi - + _info "TXT Record ID: $record_id successfully deleted" return 0 + } #################### Private functions below ################################## From 0b66acf332c539466ea4039b8e473246b3a26eba Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 15:54:23 -0500 Subject: [PATCH 32/38] shfmt fixes --- dnsapi/dns_qc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 68c33ebc..9fcf977a 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -144,7 +144,7 @@ dns_qc_rm() { _info "TXT Record ID: $record_id successfully deleted" return 0 - + } #################### Private functions below ################################## From b4f30ff02678035e56339b44d0238d7583abaead Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:07:01 -0500 Subject: [PATCH 33/38] Updated for shfmt --- dnsapi/dns_qc.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 9fcf977a..65e1c90b 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -121,14 +121,14 @@ dns_qc_rm() { fi # Temporary file to hold matched content (one per line) tmpfile=$(_mktemp) - echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" + echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' >"$tmpfile" while IFS= read -r obj || [ -n "$obj" ]; do - if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then + if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue; then _debug "response includes" "$obj" - record_id=$(echo $obj | sed 's/^\"id\":\([0-9]\+\).*/\1/' ) + record_id=$(echo $obj | sed 's/^\"id\":\([0-9]\+\).*/\1/') break fi - done < $tmpfile + done <$tmpfile rm $tmpfile if [ -z "$record_id" ]; then From 397c0605e575b70f9d76c4863a40c7dde374d5b4 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:11:39 -0500 Subject: [PATCH 34/38] Double quote for globbing --- dnsapi/dns_qc.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 65e1c90b..efa54fda 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -121,15 +121,15 @@ dns_qc_rm() { fi # Temporary file to hold matched content (one per line) tmpfile=$(_mktemp) - echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' >"$tmpfile" + echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" while IFS= read -r obj || [ -n "$obj" ]; do - if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue; then + if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then _debug "response includes" "$obj" record_id=$(echo $obj | sed 's/^\"id\":\([0-9]\+\).*/\1/') break fi - done <$tmpfile - rm $tmpfile + done < "$tmpfile" + rm "$tmpfile" if [ -z "$record_id" ]; then _info "TXT record, or $txtvalue not found, nothing to remove" From 778b4a38edef9f8d4aa5f6d9494af37db838f6fc Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:17:03 -0500 Subject: [PATCH 35/38] Missed several double quote issues --- dnsapi/dns_qc.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index efa54fda..f48eeb08 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -114,18 +114,18 @@ dns_qc_rm() { #record_id=$(echo "$response" | grep \"id\" | awk -F ' ' '{print $2}' | sed 's/,$//') #_debug "txt record_id" "$record_id" #Instead of jq - array=$(echo $response | grep -o '\[[^]]*\]' | sed 's/^\[\(.*\)\]$/\1/') + array=$(echo "$response" | grep -o '\[[^]]*\]' | sed 's/^\[\(.*\)\]$/\1/') if [ -z "$array" ]; then _err "Expected array in QC response: $response" return 1 fi # Temporary file to hold matched content (one per line) tmpfile=$(_mktemp) - echo $array | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" + echo "$array" | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" while IFS= read -r obj || [ -n "$obj" ]; do - if echo $obj | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then + if echo "$obj" | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then _debug "response includes" "$obj" - record_id=$(echo $obj | sed 's/^\"id\":\([0-9]\+\).*/\1/') + record_id=$(echo "$obj" | sed 's/^\"id\":\([0-9]\+\).*/\1/') break fi done < "$tmpfile" From f9ffdbe4074bce3bab40220cdfb2fa337c303360 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:23:01 -0500 Subject: [PATCH 36/38] Initialize record_id --- dnsapi/dns_qc.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index f48eeb08..ecf5d387 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -122,6 +122,8 @@ dns_qc_rm() { # Temporary file to hold matched content (one per line) tmpfile=$(_mktemp) echo "$array" | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" + record_id="" + while IFS= read -r obj || [ -n "$obj" ]; do if echo "$obj" | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then _debug "response includes" "$obj" @@ -129,6 +131,7 @@ dns_qc_rm() { break fi done < "$tmpfile" + rm "$tmpfile" if [ -z "$record_id" ]; then From cf2f9ef2518dcefa3fa8f2bb50841fc56bb01b43 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:27:21 -0500 Subject: [PATCH 37/38] Missed additional quotes --- dnsapi/dns_qc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index ecf5d387..756939cb 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -125,13 +125,13 @@ dns_qc_rm() { record_id="" while IFS= read -r obj || [ -n "$obj" ]; do - if echo "$obj" | grep -q '"TXT"' && echo $obj | grep -q '"id"' && echo $obj | grep -q $txtvalue ; then + if echo "$obj" | grep -q '"TXT"' && echo "$obj" | grep -q '"id"' && echo "$obj" | grep -q "$txtvalue" ; then _debug "response includes" "$obj" record_id=$(echo "$obj" | sed 's/^\"id\":\([0-9]\+\).*/\1/') break fi done < "$tmpfile" - + rm "$tmpfile" if [ -z "$record_id" ]; then From 6a37f23b143f8701b19631c7d3052a63b450f602 Mon Sep 17 00:00:00 2001 From: Bob Perper Date: Tue, 30 Dec 2025 16:31:16 -0500 Subject: [PATCH 38/38] Ran shfmt locally --- dnsapi/dns_qc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_qc.sh b/dnsapi/dns_qc.sh index 756939cb..78756a35 100755 --- a/dnsapi/dns_qc.sh +++ b/dnsapi/dns_qc.sh @@ -121,16 +121,16 @@ dns_qc_rm() { fi # Temporary file to hold matched content (one per line) tmpfile=$(_mktemp) - echo "$array" | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' > "$tmpfile" + echo "$array" | grep -o '{[^}]*}' | sed 's/^{//;s/}$//' >"$tmpfile" record_id="" while IFS= read -r obj || [ -n "$obj" ]; do - if echo "$obj" | grep -q '"TXT"' && echo "$obj" | grep -q '"id"' && echo "$obj" | grep -q "$txtvalue" ; then + if echo "$obj" | grep -q '"TXT"' && echo "$obj" | grep -q '"id"' && echo "$obj" | grep -q "$txtvalue"; then _debug "response includes" "$obj" record_id=$(echo "$obj" | sed 's/^\"id\":\([0-9]\+\).*/\1/') break fi - done < "$tmpfile" + done <"$tmpfile" rm "$tmpfile"