diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index cd21b65a..0a121fd7 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -58,6 +58,7 @@ Never use raw shell commands when acme.sh provides a wrapper function. This is t
| Instead of | Use |
|---|---|
| `tr '[:upper:]' '[:lower:]'` | `_lower_case()` |
+| `tr '[:lower:]' '[:upper:]'` | `_upper_case()` |
| `head -n 1` | `_head_n 1` |
| `openssl dgst` / `openssl` | `_digest()` / `_hmac()` |
| `date` | `_utc_date()` with `sed`/`tr` |
@@ -68,6 +69,14 @@ Never use raw shell commands when acme.sh provides a wrapper function. This is t
| `grep -E` / `grep -Po` | `_egrep_o()` |
| `printf` | `echo` |
| `idn` command | `_idn()` / `_is_idn()` |
+| `mktemp` | `_mktemp()` |
+| `[:space:]` | ` ` |
+| `[:alnum:]` | `A-Za-z0-9` |
+| `[:alpha:]` | `A-Za-z` |
+| `[:digit:]` | `0-9` |
+| `awk` | `cut` / `sed` / `while read` loops |
+
+
When fixing a pattern issue, fix **all instances** in the file, not just the one highlighted.
diff --git a/acme.sh b/acme.sh
index b57228d3..e780f61d 100755
--- a/acme.sh
+++ b/acme.sh
@@ -65,7 +65,7 @@ ID_TYPE_IP="ip"
LOCAL_ANY_ADDRESS="0.0.0.0"
-DEFAULT_RENEW=30
+DEFAULT_RENEW="${DEFAULT_RENEW:-30}"
NO_VALUE="no"
@@ -1599,6 +1599,7 @@ createCSR() {
domain="$1"
domainlist="$2"
_isEcc="$3"
+ _csreku="$4"
_initpath "$domain" "$_isEcc"
@@ -1612,7 +1613,7 @@ createCSR() {
_err "Please create it first."
return 1
fi
- _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
+ _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF" "" "$_csreku"
}
@@ -5324,6 +5325,11 @@ $_authorizations_map"
_link_cert_retry="$(_math $_link_cert_retry + 1)"
done
+ # cover case where the final poll returned 'valid'
+ if [ -z "$Le_LinkCert" ] && _contains "$response" "\"status\":\"valid\""; then
+ Le_LinkCert="$(echo "$response" | _egrep_o '"certificate" *: *"[^"]*"' | cut -d '"' -f 4)"
+ fi
+
if [ -z "$Le_LinkCert" ]; then
_err "Signing failed. Could not get Le_LinkCert, and stopped retrying after reaching the retry limit."
_err "$response"
@@ -5667,7 +5673,7 @@ renewAll() {
_set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT}
_debug "_set_level" "$_set_level"
export _ACME_IN_RENEWALL=1
- for di in "${CERT_HOME}"/*[.:]*/; do
+ for di in "${CERT_HOME}"/*.* "${CERT_HOME}"/*:*; do
_debug di "$di"
if ! [ -d "$di" ]; then
_debug "Not a directory, skipping: $di"
@@ -8254,7 +8260,7 @@ _process() {
createDomainKey "$_domain" "$_keylength"
;;
createCSR)
- createCSR "$_domain" "$_altdomains" "$_ecc"
+ createCSR "$_domain" "$_altdomains" "$_ecc" "$_extended_key_usage"
;;
setnotify)
setnotify "$_notify_hook" "$_notify_level" "$_notify_mode" "$_notify_source"
diff --git a/deploy/panos.sh b/deploy/panos.sh
index 019d8c62..00badffc 100644
--- a/deploy/panos.sh
+++ b/deploy/panos.sh
@@ -68,8 +68,8 @@ deployer() {
# Get Version Info to test key
content="type=version&key=$_panos_key"
## Exclude all scopes for the empty commit
- #_exclude_scope="excludeexcludeexclude"
- #content="type=commit&action=partial&key=$_panos_key&cmd=$_exclude_scopeacmekeytest"
+ #_exclude_scope="excludedexcluded"
+ #content="type=commit&action=partial&key=$_panos_key&cmd=$_exclude_scope$_panos_user"
fi
# Generate API Key
@@ -128,10 +128,9 @@ deployer() {
#Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
if [ "$FORCE" ]; then
_debug "Force switch detected. Committing ALL changes to the firewall."
- cmd=$(printf "%s" "$_panos_user" | _url_encode)
+ cmd=$(printf "%s" "$_panos_user" | _url_encode)
else
- _exclude_scope="excludeexclude"
- cmd=$(printf "%s" "$_exclude_scope$_panos_user" | _url_encode)
+ cmd=$(printf "%s" "$_panos_user" | _url_encode)
fi
content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
fi
diff --git a/deploy/synology_dsm.sh b/deploy/synology_dsm.sh
index f6cc2cd0..e28a4036 100644
--- a/deploy/synology_dsm.sh
+++ b/deploy/synology_dsm.sh
@@ -387,7 +387,7 @@ synology_dsm_deploy() {
if echo "$response" | grep '"restart_httpd":true' >/dev/null; then
_info "Restart HTTP services succeeded."
else
- _info "Restart HTTP services failed."
+ _info "Restart HTTP services not necessary."
fi
_temp_admin_cleanup "$SYNO_USE_TEMP_ADMIN" "$SYNO_USERNAME"
_logout
diff --git a/dnsapi/dns_baidu.sh b/dnsapi/dns_baidu.sh
new file mode 100644
index 00000000..8651deab
--- /dev/null
+++ b/dnsapi/dns_baidu.sh
@@ -0,0 +1,548 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+
+# Global variables for returning results (avoid stdout pollution from logging)
+_BAIDU_FIND_RESULT=""
+_BAIDU_BCE_AUTH_RESULT=""
+
+: "${BAIDU_LOG_LEVEL:=2}"
+
+_baidu_log_ts() {
+ date
+}
+
+_baidu_log_ge() {
+ _want="$1"
+ [ "${BAIDU_LOG_LEVEL:-0}" -ge "$_want" ]
+}
+
+_baidu_log() {
+ _lvl="$1"
+ _tag="$2"
+ _msg="$3"
+ if [ "$_lvl" = "0" ] || _baidu_log_ge "$_lvl"; then
+ printf -- "[%s] %s %s\n" "$(_baidu_log_ts)" "$_tag" "$_msg"
+ fi
+}
+
+_baidu_err() {
+ _baidu_log 0 "baidu_bcd.err" "$1"
+ return 1
+}
+
+_baidu_info() {
+ _baidu_log 1 "baidu_bcd.info" "$1"
+ return 0
+}
+
+_baidu_debug() {
+ _baidu_log 2 "$1" "$2"
+ return 0
+}
+
+dns_baidu_info='Baidu Cloud BCD DNS
+Site: cloud.baidu.com
+Docs: https://cloud.baidu.com/doc/BCD/
+Signature: https://cloud.baidu.com/doc/Reference/s/njwvz1yfu
+Options:
+ Baidu_AK AccessKeyId
+ Baidu_SK SecretAccessKey
+OptionsAlt:
+ Baidu_BCD_Host API host, default: bcd.baidubce.com
+ Baidu_BCD_Version API version number, default: 1
+ Baidu_BCD_Expire Signature expiration seconds, default: 3600
+ Baidu_View Resolve view, default: DEFAULT
+ Baidu_TTL Resolve ttl seconds, default: 300
+ Baidu_RM_Max Max records to delete in one run, default: 20
+'
+
+BAIDU_BCD_DEFAULT_HOST="bcd.baidubce.com"
+
+# --- Public API ---
+dns_baidu_add() {
+ fulldomain=$(_idn "$1")
+ txtvalue=$2
+
+ if ! _baidu_prepare_record "$fulldomain"; then
+ _baidu_err "baidu_prepare_record failed for add: $fulldomain"
+ return 1
+ fi
+
+ if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
+ _baidu_err "baidu_find_record_ids failed for add: $_record_domain.$_zone_name"
+ return 1
+ fi
+ _existing_ids="$_BAIDU_FIND_RESULT"
+ if [ "$_existing_ids" ]; then
+ _baidu_info "txt exists, skip add: $_record_domain.$_zone_name"
+ return 0
+ fi
+
+ _ttl="${Baidu_TTL:-300}"
+ _ttl="$(_baidu_trim_ws "$_ttl")"
+ case "$_ttl" in
+ "" | *[!0-9]*)
+ _ttl="300"
+ ;;
+ esac
+ _view="$(_baidu_trim_ws "${Baidu_View:-DEFAULT}")"
+ txtvalue="$(_baidu_trim_ws "$txtvalue")"
+ _record_domain="$(_baidu_trim_ws "$_record_domain")"
+ _zone_name="$(_baidu_trim_ws "$_zone_name")"
+
+ _body="$(_baidu_payload_add_txt "$_zone_name" "$_record_domain" "$txtvalue" "$_ttl" "$_view")"
+
+ if ! _baidu_bcd_post "/domain/resolve/add" "$_body"; then
+ _baidu_err "baidu_bcd_post failed: add record"
+ return 1
+ fi
+
+ if _baidu_is_api_error "$response"; then
+ _baidu_err "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+dns_baidu_rm() {
+ fulldomain=$(_idn "$1")
+ txtvalue=$2
+
+ if ! _baidu_prepare_record "$fulldomain"; then
+ _baidu_err "baidu_prepare_record failed for delete: $fulldomain"
+ return 1
+ fi
+
+ if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
+ _baidu_err "baidu_find_record_ids failed for delete: $_record_domain.$_zone_name"
+ return 1
+ fi
+ _ids="$_BAIDU_FIND_RESULT"
+ if [ -z "$_ids" ]; then
+ _baidu_info "no matching txt to delete: $_record_domain.$_zone_name"
+ return 0
+ fi
+
+ _rm_max="${Baidu_RM_Max:-20}"
+ _rm_max="$(_baidu_trim_ws "$_rm_max")"
+ case "$_rm_max" in
+ "" | *[!0-9]*)
+ _rm_max="20"
+ ;;
+ esac
+ _rm_cnt="$(printf "%s\n" "$_ids" | sed '/^$/d' | wc -l | tr -d ' ')"
+ if [ "$_rm_cnt" ] && [ "$_rm_cnt" -gt "$_rm_max" ]; then
+ _baidu_err "Refusing to delete $_rm_cnt records (limit: $_rm_max)"
+ return 1
+ fi
+
+ for _rid in $_ids; do
+ _body="$(_baidu_payload_delete "$_zone_name" "$_rid")"
+ if ! _baidu_bcd_post "/domain/resolve/delete" "$_body"; then
+ _baidu_err "baidu_bcd_post failed: delete recordId=$_rid"
+ return 1
+ fi
+ if _baidu_is_api_error "$response"; then
+ _baidu_err "$response"
+ return 1
+ fi
+ done
+
+ if ! _baidu_find_record_ids "$_zone_name" "$_record_domain" "TXT" "$txtvalue"; then
+ _baidu_err "baidu_find_record_ids failed for delete verify: $_record_domain.$_zone_name"
+ return 1
+ fi
+ _left_ids="$_BAIDU_FIND_RESULT"
+ if [ -z "$_left_ids" ]; then
+ return 0
+ fi
+ if [ -n "$_left_ids" ]; then
+ _baidu_err "delete verification failed: $_record_domain.$_zone_name still has TXT records"
+ return 1
+ fi
+
+ return 0
+}
+
+# --- Config / Record Context ---
+_baidu_load_credentials() {
+ Baidu_AK="${Baidu_AK:-$(_readaccountconf_mutable Baidu_AK)}"
+ Baidu_SK="${Baidu_SK:-$(_readaccountconf_mutable Baidu_SK)}"
+
+ Baidu_AK="$(_baidu_trim_ws "$Baidu_AK")"
+ Baidu_SK="$(_baidu_trim_ws "$Baidu_SK")"
+
+ if [ -z "$Baidu_AK" ] || [ -z "$Baidu_SK" ]; then
+ _baidu_err "Baidu_AK and Baidu_SK are required"
+ return 1
+ fi
+
+ _saveaccountconf_mutable Baidu_AK "$Baidu_AK"
+ _saveaccountconf_mutable Baidu_SK "$Baidu_SK"
+
+ BAIDU_BCD_HOST="${Baidu_BCD_Host:-$BAIDU_BCD_DEFAULT_HOST}"
+ BAIDU_BCD_VERSION="${Baidu_BCD_Version:-1}"
+
+ return 0
+}
+
+_baidu_prepare_record() {
+ _fulldomain="$1"
+ if ! _baidu_load_credentials; then
+ _baidu_err "baidu_load_credentials failed"
+ return 1
+ fi
+ if ! _baidu_get_root "$_fulldomain"; then
+ _baidu_err "Could not find zone for $_fulldomain"
+ return 1
+ fi
+ _record_domain="$_sub_domain"
+ _zone_name="$_domain"
+ return 0
+}
+
+# --- Zone / Records ---
+_baidu_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ _baidu_err "invalid domain: $domain"
+ return 1
+ fi
+
+ if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$h" 1 1)"; then
+ _baidu_err "baidu_bcd_post failed: list zones"
+ return 1
+ fi
+ if ! _baidu_is_api_error "$response" && (_contains "$response" "\"totalCount\"" || _contains "$response" "\"result\""); then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain=$h
+ if [ "$_sub_domain" = "$_domain" ]; then
+ _sub_domain="@"
+ fi
+ _baidu_info "zone matched: $_domain (host: $_sub_domain)"
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+}
+
+_baidu_find_record_ids() {
+ _zone_name="$1"
+ _record_domain="$2"
+ _rdtype="$3"
+ _rdata="$4"
+
+ # Reset global result variable
+ _BAIDU_FIND_RESULT=""
+
+ _zone_name_e="$(_baidu_json_escape "$_zone_name")"
+ _record_domain_e="$(_baidu_json_escape "$_record_domain")"
+ _rdtype_e="$(_baidu_json_escape "$_rdtype")"
+ _rdata_e="$(_baidu_json_escape "$_rdata")"
+
+ _page=1
+ _page_size=100
+ _ids=""
+
+ _max_page=""
+ while true; do
+ if ! _baidu_bcd_post "/domain/resolve/list" "$(_baidu_payload_list "$_zone_name" "$_page" "$_page_size")"; then
+ _baidu_err "baidu_bcd_post failed: list records"
+ return 1
+ fi
+
+ if _baidu_is_api_error "$response"; then
+ _baidu_err "baidu_bcd error: $(_baidu_json_get_str "$response" "code") $(_baidu_json_get_str "$response" "message")"
+ return 1
+ fi
+
+ _normalized="$(
+ printf "%s" "$response" | _normalizeJson
+ )"
+
+ if [ -z "$_max_page" ]; then
+ _total="$(_baidu_parse_totalcount "$_normalized")"
+ _max_page="$(_baidu_calc_max_page "$_total" "$_page_size")"
+ fi
+
+ _records=$(printf "%s" "$_normalized" | sed 's/},{/}\n{/g')
+ while IFS= read -r _line; do
+ _id="$(_baidu_match_record_id "$_line" "$_record_domain_e" "$_rdtype_e" "$_rdata_e")"
+ if [ "$_id" ]; then
+ _ids="$_ids $_id"
+ fi
+ done <...."
+ _debug2 "add response" "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+# Usage: dns_bhosted_rm _acme-challenge.www.example.com "txt-value"
+dns_bhosted_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _debug "fulldomain" "$fulldomain"
+ _debug "txtvalue" "$txtvalue"
+
+ _bhosted_load_credentials || return 1
+ _bhosted_get_root "$fulldomain" || return 1
+
+ _hash="$(_bhosted_cache_hash "$fulldomain" "$txtvalue")"
+ _rec_id="$(_bhosted_mem_get_id "$_hash")"
+
+ if [ -z "$_rec_id" ]; then
+ _err "No cached bHosted record id found for cleanup."
+ _err "Please delete TXT manually in bHosted DNS for: ${_bhosted_name}.${_domain}"
+ return 1
+ fi
+
+ _info "Removing TXT record id=${_rec_id}: ${_bhosted_name}.${_domain}"
+ _bhosted_api_del_record "$_bhosted_sld" "$_bhosted_tld" "$_rec_id" || return 1
+
+ return 0
+}
+
+######## Private functions #####################
+
+_bhosted_load_credentials() {
+ BHOSTED_Username="${BHOSTED_Username:-$(_readaccountconf_mutable BHOSTED_Username)}"
+ BHOSTED_Password="${BHOSTED_Password:-$(_readaccountconf_mutable BHOSTED_Password)}"
+
+ if [ -z "$BHOSTED_Username" ] || [ -z "$BHOSTED_Password" ]; then
+ BHOSTED_Username=""
+ BHOSTED_Password=""
+ _err "You didn't specify bHosted credentials."
+ _err "Please export BHOSTED_Username and BHOSTED_Password (MD5 hash)."
+ return 1
+ fi
+
+ _saveaccountconf_mutable BHOSTED_Username "$BHOSTED_Username"
+ _saveaccountconf_mutable BHOSTED_Password "$BHOSTED_Password"
+
+ return 0
+}
+
+# Determine root zone and host part
+# Supports simple domains automatically (example.com, example.nl)
+# For multi-part TLDs (example.co.uk), set:
+# BHOSTED_SLD=example
+# BHOSTED_TLD=co.uk
+_bhosted_get_root() {
+ domain="$1"
+
+ BHOSTED_SLD="${BHOSTED_SLD:-$(_readdomainconf BHOSTED_SLD)}"
+ BHOSTED_TLD="${BHOSTED_TLD:-$(_readdomainconf BHOSTED_TLD)}"
+
+ if [ -n "$BHOSTED_SLD" ] && [ -n "$BHOSTED_TLD" ]; then
+ _savedomainconf BHOSTED_SLD "$BHOSTED_SLD"
+ _savedomainconf BHOSTED_TLD "$BHOSTED_TLD"
+
+ _domain="${BHOSTED_SLD}.${BHOSTED_TLD}"
+ case "$domain" in
+ *."$_domain") ;;
+ "$_domain") ;;
+ *)
+ _err "BHOSTED_SLD/BHOSTED_TLD do not match requested domain: $domain"
+ return 1
+ ;;
+ esac
+
+ _bhosted_sld="$BHOSTED_SLD"
+ _bhosted_tld="$BHOSTED_TLD"
+ _bhosted_name="${domain%."$_domain"}"
+ if [ "$_bhosted_name" = "$domain" ]; then
+ _bhosted_name=""
+ fi
+
+ [ -n "$_bhosted_name" ] || _bhosted_name="@"
+
+ _debug "_domain" "$_domain"
+ _debug "_bhosted_sld" "$_bhosted_sld"
+ _debug "_bhosted_tld" "$_bhosted_tld"
+ _debug "_bhosted_name" "$_bhosted_name"
+ return 0
+ fi
+
+ # Auto-parse: assume last label = tld, label before = sld
+ # Works for .nl / .com / .org etc.
+ _bhosted_tld="$(printf "%s" "$domain" | awk -F. '{print $NF}')"
+ _bhosted_sld="$(printf "%s" "$domain" | awk -F. '{print $(NF-1)}')"
+
+ if [ -z "$_bhosted_sld" ] || [ -z "$_bhosted_tld" ]; then
+ _err "Could not parse SLD/TLD from domain: $domain"
+ return 1
+ fi
+
+ _domain="${_bhosted_sld}.${_bhosted_tld}"
+ _bhosted_name="${domain%."$_domain"}"
+ if [ "$_bhosted_name" = "$domain" ]; then
+ _bhosted_name=""
+ fi
+
+ [ -n "$_bhosted_name" ] || _bhosted_name="@"
+
+ _debug "_domain" "$_domain"
+ _debug "_bhosted_sld" "$_bhosted_sld"
+ _debug "_bhosted_tld" "$_bhosted_tld"
+ _debug "_bhosted_name" "$_bhosted_name"
+
+ return 0
+}
+
+_bhosted_api_add_txt() {
+ _sld="$1"
+ _tld="$2"
+ _name="$3"
+ _content="$4"
+ _ttl="$5"
+
+ _u_user="$(printf "%s" "$BHOSTED_Username" | _url_encode)"
+ _u_pass="$(printf "%s" "$BHOSTED_Password" | _url_encode)"
+ _u_sld="$(printf "%s" "$_sld" | _url_encode)"
+ _u_tld="$(printf "%s" "$_tld" | _url_encode)"
+ _u_name="$(printf "%s" "$_name" | _url_encode)"
+ _u_content="$(printf "%s" "$_content" | _url_encode)"
+ _u_ttl="$(printf "%s" "$_ttl" | _url_encode)"
+
+ _data="user=${_u_user}&password=${_u_pass}&tld=${_u_tld}&sld=${_u_sld}&type=TXT&name=${_u_name}&content=${_u_content}&ttl=${_u_ttl}"
+
+ _debug "bHosted add endpoint" "${BHOSTED_API_ROOT}/addrecord"
+ response="$(_post "$_data" "${BHOSTED_API_ROOT}/addrecord")"
+ _ret="$?"
+
+ _debug2 "bHosted add response" "$response"
+
+ if [ "$_ret" != "0" ]; then
+ _err "bHosted addrecord request failed"
+ return 1
+ fi
+
+ if _bhosted_response_has_error "$response"; then
+ _err "bHosted addrecord returned an error"
+ _debug2 "response" "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+_bhosted_api_del_record() {
+ _sld="$1"
+ _tld="$2"
+ _id="$3"
+
+ _u_user="$(printf "%s" "$BHOSTED_Username" | _url_encode)"
+ _u_pass="$(printf "%s" "$BHOSTED_Password" | _url_encode)"
+ _u_sld="$(printf "%s" "$_sld" | _url_encode)"
+ _u_tld="$(printf "%s" "$_tld" | _url_encode)"
+ _u_id="$(printf "%s" "$_id" | _url_encode)"
+
+ _url="${BHOSTED_API_ROOT}/delrecord"
+ _data="user=${_u_user}&password=${_u_pass}&tld=${_u_tld}&sld=${_u_sld}&id=${_u_id}"
+
+ _debug "bHosted delete endpoint" "$_url"
+ response="$(_post "$_data" "$_url")"
+ _ret="$?"
+
+ _debug2 "bHosted delete response" "$response"
+
+ if [ "$_ret" != "0" ]; then
+ _err "bHosted delrecord request failed"
+ return 1
+ fi
+
+ if _bhosted_response_has_error "$response"; then
+ _err "bHosted delrecord returned an error"
+ _debug2 "response" "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+# Extract XML tag value from response, e.g. 12345
+_bhosted_xml_value() {
+ _tag="$1"
+ _resp="$2"
+
+ # Flatten response to simplify parsing
+ _flat="$(printf "%s" "$_resp" | tr -d '\r\n\t')"
+ printf "%s" "$_flat" | sed -n "s:.*<${_tag}>\\([^<]*\\)${_tag}>.*:\\1:p" | _head_n 1
+}
+
+# Return code convention:
+# return 0 => response HAS error
+# return 1 => response has NO error (success)
+_bhosted_response_has_error() {
+ _resp="$1"
+
+ # Empty response = error
+ if [ -z "$_resp" ]; then
+ _debug "Empty API response"
+ return 0
+ fi
+
+ # Prefer explicit bHosted XML response fields
+ if _contains "$_resp" ""; then
+ _errors="$(_bhosted_xml_value "errors" "$_resp")"
+ _done="$(_bhosted_xml_value "done" "$_resp")"
+ _subcommand="$(_bhosted_xml_value "subcommand" "$_resp")"
+ _id="$(_bhosted_xml_value "id" "$_resp")"
+
+ _debug "bHosted XML subcommand" "$_subcommand"
+ _debug "bHosted XML id" "$_id"
+ _debug "bHosted XML errors" "$_errors"
+ _debug "bHosted XML done" "$_done"
+
+ # Success according to provided format
+ if [ "$_errors" = "0" ] && [ "$_done" = "true" ]; then
+ return 1
+ fi
+
+ _debug "bHosted XML indicates failure"
+ return 0
+ fi
+
+ # Fallback for unexpected/non-XML responses
+ _resp_lc="$(_lower_case "$_resp")"
+
+ if _contains "$_resp_lc" "error"; then
+ _debug "Detected 'error' in response"
+ return 0
+ fi
+ if _contains "$_resp_lc" "fout"; then
+ _debug "Detected 'fout' in response"
+ return 0
+ fi
+ if _contains "$_resp_lc" "invalid"; then
+ _debug "Detected 'invalid' in response"
+ return 0
+ fi
+ if _contains "$_resp_lc" "failed"; then
+ _debug "Detected 'failed' in response"
+ return 0
+ fi
+ if _contains "$_resp_lc" "denied"; then
+ _debug "Detected 'denied' in response"
+ return 0
+ fi
+
+ # If no explicit error markers found, assume success
+ return 1
+}
+
+# Extract record id from response
+# Supports bHosted XML first, then generic fallbacks
+_bhosted_extract_id() {
+ _resp="$1"
+
+ # bHosted XML: 12345
+ _id="$(_bhosted_xml_value "id" "$_resp" | tr -cd '0-9')"
+ if [ -n "$_id" ]; then
+ printf "%s" "$_id"
+ return 0
+ fi
+
+ # JSON: "id":12345
+ _id="$(printf "%s" "$_resp" | _egrep_o '"id"[[:space:]]*:[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ if [ -n "$_id" ]; then
+ printf "%s" "$_id"
+ return 0
+ fi
+
+ # key=value: id=12345
+ _id="$(printf "%s" "$_resp" | _egrep_o '(^|[[:space:][:punct:]])id[[:space:]]*=[[:space:]]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ if [ -n "$_id" ]; then
+ printf "%s" "$_id"
+ return 0
+ fi
+
+ # "record id 12345" / "recordid 12345"
+ _id="$(printf "%s" "$_resp" | _egrep_o '(record[[:space:]]*id|recordid)[^0-9]*[0-9]+' | _head_n 1 | tr -cd '0-9')"
+ if [ -n "$_id" ]; then
+ printf "%s" "$_id"
+ return 0
+ fi
+
+ return 1
+}
+
+# Create a unique config key for cached record ids
+_bhosted_cache_hash() {
+ _fd="$1"
+ _tv="$2"
+ # md5 hex of fulldomain|txtvalue
+ printf "%s|%s" "$_fd" "$_tv" | _digest md5 hex
+}
+
+_bhosted_cache_key() {
+ _hash="$1"
+ printf "%s" "BHOSTED_TXT_ID_${_hash}"
+}
+
+_bhosted_mem_set_id() {
+ _hash="$1"
+ _id="$2"
+ _key="$(_bhosted_cache_key "$_hash")"
+ _savedomainconf "$_key" "$_id"
+}
+
+_bhosted_mem_get_id() {
+ _hash="$1"
+ _key="$(_bhosted_cache_key "$_hash")"
+ _readdomainconf "$_key"
+}
diff --git a/dnsapi/dns_gname.sh b/dnsapi/dns_gname.sh
new file mode 100644
index 00000000..886b3dc5
--- /dev/null
+++ b/dnsapi/dns_gname.sh
@@ -0,0 +1,303 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_gname_info='GNAME
+Site: www.gname.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_gname
+Options:
+ GNAME_APPID Your APPID
+ GNAME_APPKEY Your APPKEY
+ GNAME_TTL DNS resolution record TTL value, default 120.
+Issues: github.com/acmesh-official/acme.sh/issues/6874
+Author: GNDevProd
+'
+
+GNAME_TLD_Api="https://www.gname.com/request/tlds?lx=all"
+GNAME_Api="https://api.gname.com"
+GNAME_TLDS_CACHE=""
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "T1rxqRBosdIK90xWCG3KLZNf6q_0HG9i01zxXp5CAS3"
+dns_gname_add() {
+ fulldomain=$1
+ txtvalue=$(printf "%s" "$2" | _url_encode)
+ #Compatible with gname API RFC 1738 standard URL encoding
+ txtvalue=$(printf '%s' "$txtvalue" | sed 's/%20/+/g')
+
+ GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
+ GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
+ GNAME_TTL="${GNAME_TTL:-$(_readaccountconf_mutable GNAME_TTL)}"
+ GNAME_TTL="${GNAME_TTL:-120}"
+
+ if [ -z "$GNAME_APPID" ] || [ -z "$GNAME_APPKEY" ]; then
+ GNAME_APPID=""
+ GNAME_APPKEY=""
+ _err "You have not configured the APPID and APPKEY for the GNAME API."
+ _err "You can get yours from here https://www.gname.com/domain/api."
+ return 1
+ fi
+
+ _saveaccountconf_mutable GNAME_APPID "$GNAME_APPID"
+ _saveaccountconf_mutable GNAME_APPKEY "$GNAME_APPKEY"
+ _saveaccountconf_mutable GNAME_TTL "$GNAME_TTL"
+
+ if ! _extract_domain "$fulldomain"; then
+ _err "Failed to extract domain. Please check your network or API response."
+ return 1
+ fi
+
+ gntime=$(date +%s)
+
+ #If the hostname is empty, you need to replace it with @.
+ final_hostname=$(printf "%s" "${ext_hostname:-@}" | _url_encode)
+
+ # Parameters need to be sorted by key
+ body="appid=$GNAME_APPID&exist=1&gntime=$gntime&jlz=$txtvalue&lang=us&lx=TXT&mx=0&ttl=$GNAME_TTL&xl=0&ym=$ext_domain&zj=$final_hostname"
+
+ _info "Adding TXT record for $ext_domain, host: $final_hostname"
+
+ if _post_to_api "/api/resolution/add" "$body"; then
+ _info "Successfully added DNS record."
+ return 0
+ else
+ _err "Failed to add DNS record via Gname API."
+ return 1
+ fi
+}
+
+#Usage: remove _acme-challenge.www.domain.com "T1rxqRBosdIK90xWCG3KLZNf6q_0HG9i01zxXp5CASc"
+dns_gname_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
+ GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
+
+ if [ -z "$GNAME_APPID" ] || [ -z "$GNAME_APPKEY" ]; then
+ GNAME_APPID=""
+ GNAME_APPKEY=""
+ _err "You have not configured the APPID and APPKEY for the GNAME API."
+ _err "You can get yours from here https://www.gname.com/domain/api."
+ return 1
+ fi
+
+ _saveaccountconf_mutable GNAME_APPID "$GNAME_APPID"
+ _saveaccountconf_mutable GNAME_APPKEY "$GNAME_APPKEY"
+
+ if ! _extract_domain "$fulldomain"; then
+ _err "Failed to extract domain. Please check your network or API response."
+ return 1
+ fi
+
+ final_hostname="${ext_hostname:-@}"
+
+ _debug "Query DNS record ID $ext_domain $final_hostname $txtvalue"
+
+ if ! record_id=$(_get_record_id "$ext_domain" "$final_hostname" "$txtvalue"); then
+ _err "Error occurred during record lookup. Skipping deletion to avoid errors."
+ return 1
+ fi
+
+ if [ -z "$record_id" ]; then
+ _info "DNS record not found, skip removing."
+ return 0
+ fi
+
+ _debug "DNS record ID:$record_id"
+ gntime=$(date +%s)
+ body="appid=$GNAME_APPID&gntime=$gntime&jxid=$record_id&lang=us&ym=$ext_domain"
+
+ if ! _post_to_api "/api/resolution/delete" "$body"; then
+ _err "DNS record deletion failed"
+ return 1
+ fi
+
+ _info "DNS record deletion successful"
+ return 0
+}
+
+# Find the DNS record ID by hostname, record type, and record value.
+_get_record_id() {
+ target_ym="$1"
+ target_zjt="$2"
+ target_jxz="$3"
+ target_lx="TXT"
+
+ GNAME_APPID="${GNAME_APPID:-$(_readaccountconf_mutable GNAME_APPID)}"
+ GNAME_APPKEY="${GNAME_APPKEY:-$(_readaccountconf_mutable GNAME_APPKEY)}"
+ gntime=$(date +%s)
+ body="appid=$GNAME_APPID&gntime=$gntime&limit=1000&lx=$target_lx&page=1&ym=$target_ym"
+
+ if ! _post_to_api "/api/resolution/list" "$body"; then
+ _err "Query and parsing records failed"
+ return 1
+ fi
+
+ clean_response=$(echo "$post_response" | tr -d '\r')
+ records=$(echo "$clean_response" | sed 's/.*"data":\[//; s/\],"count".*//; s/},/}\n/g' | grep "^{")
+ matched_rows=$(echo "$records" | grep -Fi "\"zjt\":\"$target_zjt\"")
+
+ if [ -z "$matched_rows" ]; then
+ _debug "No records found for host: $target_zjt"
+ return 0
+ fi
+
+ exact_row=$(echo "$matched_rows" | grep -F "\"jxz\":\"$target_jxz\"" | _head_n 1)
+ dns_record_id=""
+ if [ -n "$exact_row" ]; then
+ dns_record_id=$(echo "$exact_row" | _egrep_o "\"id\":\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '"')
+ fi
+
+ if [ -n "$dns_record_id" ]; then
+ _debug "Successfully found exact record ID: $dns_record_id"
+ printf "%s" "$dns_record_id"
+ return 0
+ fi
+
+ _debug "Can not find exact DNS record match for: $target_zjt"
+ return 0
+}
+
+# Request GNAME API,post_response: Response content
+_post_to_api() {
+ uri=$1
+ body=$2
+ url="$GNAME_Api$uri"
+ gntoken=$(_gntoken "$body")
+ body="$body&gntoken=$gntoken"
+ post_response="$(_post "$body" "$url" "" "POST" "application/x-www-form-urlencoded")"
+
+ http_err_code=$?
+ if [ "$http_err_code" != "0" ]; then
+ _err "POST API $url request failed:$http_err_code"
+ return 1
+ fi
+
+ normalized_response="$(echo "$post_response" | _normalizeJson)"
+ if [ -z "$normalized_response" ]; then
+ _err "Failed to normalize JSON response for [$uri]"
+ return 1
+ fi
+
+ ret_code=$(echo "$normalized_response" | sed 's/.*"code":\([-0-9]*\).*/\1/')
+
+ if [ "$ret_code" = "1" ]; then
+ return 0
+ fi
+
+ if [ "$uri" = "/api/resolution/add" ]; then
+ if _contains "$normalized_response" "the same host records and record values"; then
+ _info "DNS record already exists, treat as success."
+ return 0
+ fi
+ fi
+
+ ret_msg=$(echo "$normalized_response" | sed 's/.*"msg":"\([^"]*\)".*/\1/')
+ _err "POST API $url error: [$ret_code] $ret_msg"
+ _debug "Full response: $normalized_response"
+ return 1
+}
+
+# Split the complete domain into a host and a main domain.
+# example, www.gname.com can be split into ext_hostname=www,ext_domain=gname.com
+_extract_domain() {
+
+ host="$1"
+
+ # Prioritize reading from the cache and reduce network caching
+ if [ -z "$GNAME_TLDS_CACHE" ]; then
+ GNAME_TLDS_CACHE=$(_get_suffixes_json)
+ fi
+
+ if [ -z "$GNAME_TLDS_CACHE" ]; then
+ _err "The list of domain suffixes is empty after retrieval; cannot extract domain"
+ return 1
+ fi
+
+ main_part=$(echo "$GNAME_TLDS_CACHE" | sed 's/.*"main":\[\([^]]*\)\].*/\1/' | tr -d '"' | tr ',' ' ')
+ sub_part=$(echo "$GNAME_TLDS_CACHE" | sed 's/.*"sub":\[\([^]]*\)\].*/\1/' | tr -d '"' | tr ',' ' ')
+ suffix_list=$(echo "$main_part $sub_part" | tr -s ' ' | sed 's/^[ ]//;s/[ ]$//')
+
+ dot_count=$(echo "$host" | _egrep_o "\." | wc -l)
+
+ if [ "$dot_count" -eq 0 ]; then
+ _err "Invalid domain format: $host (missing dot)"
+ return 1
+ fi
+
+ if [ "$dot_count" -eq 1 ]; then
+ ext_hostname=""
+ ext_domain="$host"
+
+ elif [ "$dot_count" -gt 1 ]; then
+ matched_suffix=""
+ for suffix in $suffix_list; do
+ case "$host" in
+ *".$suffix")
+ if [ -z "$matched_suffix" ] || [ "${#suffix}" -gt "${#matched_suffix}" ]; then
+ matched_suffix="$suffix"
+ fi
+ ;;
+ esac
+ done
+
+ if [ -n "$matched_suffix" ]; then
+ prefix="${host%."$matched_suffix"}"
+ main_name="${prefix##*.}"
+ ext_domain="$main_name.$matched_suffix"
+ else
+ _tld="${host##*.}"
+ _tmp="${host%.*}"
+ _main="${_tmp##*.}"
+ ext_domain="$_main.$_tld"
+ fi
+
+ if [ "$host" = "$ext_domain" ]; then
+ ext_hostname=""
+ else
+ ext_hostname="${host%."$ext_domain"}"
+ fi
+
+ fi
+ _debug "ext_hostname:$ext_hostname"
+ _debug "ext_domain:$ext_domain"
+ return 0
+}
+
+# Obtain the list of domain suffixes via API
+_get_suffixes_json() {
+ _debug "GET request URL: $GNAME_TLD_Api Retrieves a list of domain suffixes."
+
+ if ! response="$(_get "$GNAME_TLD_Api")"; then
+ _err "Failed to retrieve list of domain suffixes"
+ return 1
+ fi
+
+ if [ -z "$response" ]; then
+ _err "The list of domain suffixes is empty"
+ return 1
+ fi
+
+ normalized_response="$(echo "$response" | _normalizeJson)"
+ if [ -z "$normalized_response" ]; then
+ _err "Failed to normalize JSON response for domain suffix list"
+ return 1
+ fi
+
+ if ! _contains "$normalized_response" "\"code\":1"; then
+ _err "Failed to retrieve list of domain name suffixes; code is not 1"
+ return 1
+ fi
+
+ echo "$normalized_response"
+ return 0
+}
+
+# Generate API authentication signature
+_gntoken() {
+ data_to_sign="$1"
+ full_data="${data_to_sign}${GNAME_APPKEY}"
+ hash=$(printf "%s" "$full_data" | _digest md5 hex | tr -d ' ')
+ hash_upper=$(echo "$hash" | _upper_case)
+ printf "%s" "$hash_upper"
+}
diff --git a/dnsapi/dns_sitehost.sh b/dnsapi/dns_sitehost.sh
new file mode 100755
index 00000000..94a0ee93
--- /dev/null
+++ b/dnsapi/dns_sitehost.sh
@@ -0,0 +1,220 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_sitehost_info='SiteHost
+Site: sitehost.nz
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_sitehost
+Options:
+ SITEHOST_API_KEY API Key
+ SITEHOST_CLIENT_ID Client ID. The numeric client ID for your SiteHost account.
+Issues: github.com/acmesh-official/acme.sh/issues/6892
+Author: Jordan Russell
+'
+
+SITEHOST_API="https://api.sitehost.nz/1.5"
+
+######## Public functions #####################
+
+# Usage: dns_sitehost_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_sitehost_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _sitehost_load_creds; then
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # SiteHost expects the full record name as the name parameter
+ _info "Adding TXT record for ${fulldomain}"
+ if _sitehost_rest POST "dns/add_record.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)&type=TXT&name=$(printf '%s' "${fulldomain}" | _url_encode)&content=$(printf '%s' "${txtvalue}" | _url_encode)"; then
+ if _contains "$response" '"status":true'; then
+ _info "TXT record added successfully."
+ return 0
+ fi
+ fi
+
+ _err "Could not add TXT record for ${fulldomain}"
+ _err "$response"
+ return 1
+}
+
+# Usage: dns_sitehost_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Remove the txt record after validation.
+dns_sitehost_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _sitehost_load_creds; then
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting TXT records for ${_domain}"
+ if ! _sitehost_rest GET "dns/list_records.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)"; then
+ _err "Could not list DNS records"
+ _err "$response"
+ return 1
+ fi
+
+ if ! _contains "$response" '"status":true'; then
+ _err "Error listing DNS records"
+ _err "$response"
+ return 1
+ fi
+
+ # Extract record ID matching our fulldomain, type TXT, and txtvalue
+ # Response format: {"return":[{"id":"123","name":"...","type":"TXT","content":"..."},...]}
+ # SiteHost returns flat single-line JSON objects in the records array
+ # Escape regex metacharacters in values before grep matching
+ _fulldomain_grep="$(printf "%s" "$fulldomain" | sed 's/[][\\.^$*]/\\&/g')"
+ _txtvalue_grep="$(printf "%s" "$txtvalue" | sed 's/[][\\.^$*]/\\&/g')"
+ # Use field-specific matching to avoid false positives from substring matches
+ _record_id="$(echo "$response" | _egrep_o '\{[^}]*\}' | grep '"name" *: *"'"${_fulldomain_grep}"'"' | grep '"type" *: *"TXT"' | grep '"content" *: *"'"${_txtvalue_grep}"'"' | _head_n 1 | _egrep_o '"id" *: *"?[0-9]+"?' | _egrep_o '[0-9]+')"
+
+ if [ -z "$_record_id" ]; then
+ _info "TXT record not found, nothing to remove."
+ return 0
+ fi
+
+ _debug _record_id "$_record_id"
+
+ _info "Deleting TXT record ${_record_id} for ${fulldomain}"
+ if _sitehost_rest POST "dns/delete_record.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&domain=$(printf '%s' "${_domain}" | _url_encode)&record_id=$(printf '%s' "${_record_id}" | _url_encode)"; then
+ if _contains "$response" '"status":true'; then
+ _info "TXT record deleted successfully."
+ return 0
+ fi
+ fi
+
+ _err "Could not delete TXT record for ${fulldomain}"
+ _err "$response"
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_sitehost_load_creds() {
+ SITEHOST_API_KEY="${SITEHOST_API_KEY:-$(_readaccountconf_mutable SITEHOST_API_KEY)}"
+ SITEHOST_CLIENT_ID="${SITEHOST_CLIENT_ID:-$(_readaccountconf_mutable SITEHOST_CLIENT_ID)}"
+
+ if [ -z "$SITEHOST_API_KEY" ] || [ -z "$SITEHOST_CLIENT_ID" ]; then
+ SITEHOST_API_KEY=""
+ SITEHOST_CLIENT_ID=""
+ _err "You didn't specify SITEHOST_API_KEY and/or SITEHOST_CLIENT_ID."
+ _err "Please export them and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable SITEHOST_API_KEY "$SITEHOST_API_KEY"
+ _saveaccountconf_mutable SITEHOST_CLIENT_ID "$SITEHOST_CLIENT_ID"
+ return 0
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+
+ _debug "Getting domain list"
+
+ # Fetch ALL pages of domains first so we can match the most specific zone
+ # (a more specific zone on a later page must take precedence over a broader match)
+ _all_domains=""
+ _page=1
+
+ while true; do
+ if ! _sitehost_rest GET "dns/list_domains.json" "client_id=$(printf '%s' "${SITEHOST_CLIENT_ID}" | _url_encode)&filters%5Bpage_number%5D=${_page}"; then
+ _err "Could not list domains"
+ return 1
+ fi
+
+ if ! _contains "$response" '"status":true'; then
+ _err "Error listing domains"
+ _err "$response"
+ return 1
+ fi
+
+ _all_domains="${_all_domains} ${response}"
+
+ _total_pages=$(echo "$response" | _egrep_o '"total_pages" *: *[0-9]+' | _egrep_o '[0-9]+')
+ if [ -z "$_total_pages" ] || [ "$_page" -ge "$_total_pages" ]; then
+ break
+ fi
+
+ _page=$(_math "$_page" + 1)
+ done
+
+ # Try each subdomain level, most specific first
+ _i=1
+ _p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "${_i}"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ if echo "$_all_domains" | grep -F "\"${h}\"" >/dev/null 2>&1; then
+ if [ "$_i" = "1" ]; then
+ # DNS alias mode - fulldomain is the zone itself
+ _sub_domain=""
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"${_p}")
+ fi
+ _domain="${h}"
+ return 0
+ fi
+
+ _p="${_i}"
+ _i=$(_math "$_i" + 1)
+ done
+
+ return 1
+}
+
+# Usage: _sitehost_rest method endpoint data
+_sitehost_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ url="${SITEHOST_API}/${ep}"
+
+ _debug url "$url"
+
+ _apikey="$(printf "%s" "${SITEHOST_API_KEY}" | _url_encode)"
+
+ if [ "$m" = "GET" ]; then
+ response="$(_get "${url}?apikey=${_apikey}&${data}")"
+ else
+ _debug2 data "$data"
+ response="$(_post "apikey=${_apikey}&${data}" "$url")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error ${ep}"
+ return 1
+ fi
+
+ response="$(printf '%s' "$response" | tr -d '\r')"
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_world4you.sh b/dnsapi/dns_world4you.sh
index dc295330..f59715ac 100644
--- a/dnsapi/dns_world4you.sh
+++ b/dnsapi/dns_world4you.sh
@@ -61,7 +61,7 @@ dns_world4you_add() {
if _contains "$res" "successfully"; then
return 0
else
- msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
+ msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to add record: Unknown error"
echo "$ret" >'error-01.html'
@@ -110,7 +110,7 @@ dns_world4you_rm() {
return 3
fi
- recordid=$(printf "TXT:%s.:\"%s\"" "$fqdn" "$value" | _base64)
+ recordid=$(echo "$form" | grep 'data-records="' | sed 's/.*"\([^"]*\)".*/\1/;s/"/"/g;s/},{/}\n{/g' | grep '"type":"TXT"' | grep "\"name\":\"$fqdn\"" | grep "\"value\":\"$value\"" | sed 's/^.*"id":"\([^"]*\)".*$/\1/')
_debug recordid "$recordid"
_resethttp
@@ -125,7 +125,7 @@ dns_world4you_rm() {
if _contains "$res" "successfully"; then
return 0
else
- msg=$(echo "$res" | grep -A 15 'data-type="danger"' | grep "]*>[^<]" | sed 's/<[^>]*>//g' | sed 's/^\s*//g')
+ msg=$(echo "$res" | grep -A 20 'alert-notification' | grep 'class="weak-title">[^<]' | sed 's/<[^>]*>//g;s/^\s*//g')
if [ "$msg" = '' ]; then
_err "Unable to remove record: Unknown error"
echo "$ret" >'error-01.html'