|')"
+ _debug2 table "${table}"
+ names=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*' | sed 's|||; s|.*>||')
+ ids=$(echo "${table}" | _egrep_o 'id="[0-9]+\.name">[^<]*' | sed 's|\.name">.*||; s|id="||')
+ types=$(echo "${table}" | _egrep_o 'id="[0-9]+\.type">[^<]*' | sed 's|||; s|.*>||')
+ values=$(echo "${table}" | _egrep_o 'id="[0-9]+\.content">[^<]*' | sed 's|||; s|.*>||')
+
+ _debug2 names "${names}"
+ _debug2 ids "${ids}"
+ _debug2 types "${types}"
+ _debug2 values "${values}"
+
+ # look for line whose name is ${full_domain}, whose type is TXT, and whose value is ${txt_value}
+ line_num="$(echo "${values}" | grep -F -n -- "${txt_value}" | _head_n 1 | cut -d ':' -f 1)"
+ _debug2 line_num "${line_num}"
+ found_id=
+ if [ -n "$line_num" ]; then
+ type=$(echo "${types}" | sed -n "${line_num}p")
+ name=$(echo "${names}" | sed -n "${line_num}p")
+ id=$(echo "${ids}" | sed -n "${line_num}p")
+
+ _debug2 type "$type"
+ _debug2 name "$name"
+ _debug2 id "$id"
+ _debug2 full_domain "$full_domain"
+
+ if [ "${type}" = "TXT" ] && [ "${name}" = "${full_domain}" ]; then
+ found_id=${id}
+ fi
+ fi
+
+ if [ "${found_id}" = "" ]; then
+ _err "Can not find record id."
+ return 0
+ fi
+
+ # Remove the record
+ body="id=${zone_id}&record_id=${found_id}"
+ response=$(do_post "$body" "https://www.geoscaling.com/dns2/ajax/delete_record.php")
+ exit_code="$?"
+ if [ "$exit_code" -eq 0 ]; then
+ _info "Record removed successfully."
+ else
+ _err "Could not clean (remove) up the record. Please go to Geoscaling administration interface and clean it by hand."
+ fi
+ do_logout
+ return "${exit_code}"
+}
+
+########################## PRIVATE FUNCTIONS ###########################
+
+do_get() {
+ _url=$1
+ export _H1="Cookie: $geoscaling_phpsessid_cookie"
+ _get "${_url}"
+}
+
+do_post() {
+ _body=$1
+ _url=$2
+ export _H1="Cookie: $geoscaling_phpsessid_cookie"
+ _post "${_body}" "${_url}"
+}
+
+do_login() {
+
+ _info "Logging in..."
+
+ username_encoded="$(printf "%s" "${GEOSCALING_Username}" | _url_encode)"
+ password_encoded="$(printf "%s" "${GEOSCALING_Password}" | _url_encode)"
+ body="username=${username_encoded}&password=${password_encoded}"
+
+ response=$(_post "$body" "https://www.geoscaling.com/dns2/index.php?module=auth")
+ _debug2 response "${response}"
+
+ #retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | _egrep_o '[0-9]+$')
+ retcode=$(grep '^HTTP[^ ]*' "${HTTP_HEADER}" | _head_n 1 | cut -d ' ' -f 2)
+
+ if [ "$retcode" != "302" ]; then
+ _err "Geoscaling login failed for user ${GEOSCALING_Username}. Check ${HTTP_HEADER} file"
+ return 1
+ fi
+
+ geoscaling_phpsessid_cookie="$(grep -i '^set-cookie:' "${HTTP_HEADER}" | _egrep_o 'PHPSESSID=[^;]*;' | tr -d ';')"
+ return 0
+
+}
+
+do_logout() {
+ _info "Logging out."
+ response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=auth")"
+ _debug2 response "$response"
+ return 0
+}
+
+find_zone() {
+ domain="$1"
+
+ # do login
+ do_login || return 1
+
+ # get zones
+ response="$(do_get "https://www.geoscaling.com/dns2/index.php?module=domains")"
+
+ table="$(echo "${response}" | tr -d '\n' | sed 's|.*
Your domains
|')"
+ _debug2 table "${table}"
+ zone_names="$(echo "${table}" | _egrep_o '
[^<]*' | sed 's|
||;s|||')"
+ _debug2 _matches "${zone_names}"
+ # Zone names and zone IDs are in same order
+ zone_ids=$(echo "${table}" | _egrep_o '
' | sed 's|.*id=||;s|. .*||')
+
+ _debug2 "These are the zones on this Geoscaling account:"
+ _debug2 "zone_names" "${zone_names}"
+ _debug2 "And these are their respective IDs:"
+ _debug2 "zone_ids" "${zone_ids}"
+ if [ -z "${zone_names}" ] || [ -z "${zone_ids}" ]; then
+ _err "Can not get zone names or IDs."
+ return 1
+ fi
+ # Walk through all possible zone names
+ strip_counter=1
+ while true; do
+ attempted_zone=$(echo "${domain}" | cut -d . -f ${strip_counter}-)
+
+ # All possible zone names have been tried
+ if [ -z "${attempted_zone}" ]; then
+ _err "No zone for domain '${domain}' found."
+ return 1
+ fi
+
+ _debug "Looking for zone '${attempted_zone}'"
+
+ line_num="$(echo "${zone_names}" | grep -n "^${attempted_zone}\$" | _head_n 1 | cut -d : -f 1)"
+ _debug2 line_num "${line_num}"
+ if [ "$line_num" ]; then
+ zone_id=$(echo "${zone_ids}" | sed -n "${line_num}p")
+ zone_name=$(echo "${zone_names}" | sed -n "${line_num}p")
+ if [ -z "${zone_id}" ]; then
+ _err "Can not find zone id."
+ return 1
+ fi
+ _debug "Found relevant zone '${attempted_zone}' with id '${zone_id}' - will be used for domain '${domain}'."
+ return 0
+ fi
+
+ _debug "Zone '${attempted_zone}' doesn't exist, let's try a less specific zone."
+ strip_counter=$(_math "${strip_counter}" + 1)
+ done
+}
+# vim: et:ts=2:sw=2:
diff --git a/acme.sh-master/dnsapi/dns_he.sh b/acme.sh-master/dnsapi/dns_he.sh
new file mode 100644
index 0000000..bf4a503
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_he.sh
@@ -0,0 +1,173 @@
+#!/usr/bin/env sh
+
+########################################################################
+# Hurricane Electric hook script for acme.sh
+#
+# Environment variables:
+#
+# - $HE_Username (your dns.he.net username)
+# - $HE_Password (your dns.he.net password)
+#
+# Author: Ondrej Simek
+# Git repo: https://github.com/angel333/acme.sh
+
+#-- dns_he_add() - Add TXT record --------------------------------------
+# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..."
+
+dns_he_add() {
+ _full_domain=$1
+ _txt_value=$2
+ _info "Using DNS-01 Hurricane Electric hook"
+
+ HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
+ HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
+ if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then
+ HE_Username=
+ HE_Password=
+ _err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password environment variables."
+ return 1
+ fi
+ _saveaccountconf_mutable HE_Username "$HE_Username"
+ _saveaccountconf_mutable HE_Password "$HE_Password"
+
+ # Fills in the $_zone_id
+ _find_zone "$_full_domain" || return 1
+ _debug "Zone id \"$_zone_id\" will be used."
+ username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)"
+ password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)"
+ body="email=${username_encoded}&pass=${password_encoded}"
+ body="$body&account="
+ body="$body&menu=edit_zone"
+ body="$body&Type=TXT"
+ body="$body&hosted_dns_zoneid=$_zone_id"
+ body="$body&hosted_dns_recordid="
+ body="$body&hosted_dns_editzone=1"
+ body="$body&Priority="
+ body="$body&Name=$_full_domain"
+ body="$body&Content=$_txt_value"
+ body="$body&TTL=300"
+ body="$body&hosted_dns_editrecord=Submit"
+ response="$(_post "$body" "https://dns.he.net/")"
+ exit_code="$?"
+ if [ "$exit_code" -eq 0 ]; then
+ _info "TXT record added successfully."
+ else
+ _err "Couldn't add the TXT record."
+ fi
+ _debug2 response "$response"
+ return "$exit_code"
+}
+
+#-- dns_he_rm() - Remove TXT record ------------------------------------
+# Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..."
+
+dns_he_rm() {
+ _full_domain=$1
+ _txt_value=$2
+ _info "Cleaning up after DNS-01 Hurricane Electric hook"
+ HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
+ HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
+ # fills in the $_zone_id
+ _find_zone "$_full_domain" || return 1
+ _debug "Zone id \"$_zone_id\" will be used."
+
+ # Find the record id to clean
+ username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)"
+ password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)"
+ body="email=${username_encoded}&pass=${password_encoded}"
+ body="$body&hosted_dns_zoneid=$_zone_id"
+ body="$body&menu=edit_zone"
+ body="$body&hosted_dns_editzone="
+
+ response="$(_post "$body" "https://dns.he.net/")"
+ _debug2 "response" "$response"
+ if ! _contains "$response" "$_txt_value"; then
+ _debug "The txt record is not found, just skip"
+ return 0
+ fi
+ _record_id="$(echo "$response" | tr -d "#" | sed "s/Successfully removed record.' \
+ >/dev/null
+ exit_code="$?"
+ if [ "$exit_code" -eq 0 ]; then
+ _info "Record removed successfully."
+ else
+ _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand."
+ return "$exit_code"
+ fi
+}
+
+########################## PRIVATE FUNCTIONS ###########################
+
+_find_zone() {
+ _domain="$1"
+ username_encoded="$(printf "%s" "${HE_Username}" | _url_encode)"
+ password_encoded="$(printf "%s" "${HE_Password}" | _url_encode)"
+ body="email=${username_encoded}&pass=${password_encoded}"
+ response="$(_post "$body" "https://dns.he.net/")"
+ _debug2 response "$response"
+ if _contains "$response" '>Incorrect<'; then
+ _err "Unable to login to dns.he.net please check username and password"
+ return 1
+ fi
+ _table="$(echo "$response" | tr -d "#" | sed "s//dev/null
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ if [ "$_code" != "202" ]; then
+ _err "dns_huaweicloud: http code ${_code}"
+ return 1
+ fi
+ return 0
+}
+
+# _rm_record $token $zoneid $recordid
+# assume ${dns_api} exist
+# no output
+# return 0
+_rm_record() {
+ _token=$1
+ _zone_id=$2
+ _record_id=$3
+
+ export _H2="Content-Type: application/json"
+ export _H1="X-Auth-Token: ${_token}"
+
+ _post "" "${dns_api}/v2/zones/${_zone_id}/recordsets/${_record_id}" false "DELETE" >/dev/null
+ return $?
+}
+
+_get_token() {
+ _username=$1
+ _password=$2
+ _domain_name=$3
+
+ _debug "Getting Token"
+ body="{
+ \"auth\": {
+ \"identity\": {
+ \"methods\": [
+ \"password\"
+ ],
+ \"password\": {
+ \"user\": {
+ \"name\": \"${_username}\",
+ \"password\": \"${_password}\",
+ \"domain\": {
+ \"name\": \"${_domain_name}\"
+ }
+ }
+ }
+ },
+ \"scope\": {
+ \"project\": {
+ \"name\": \"ap-southeast-1\"
+ }
+ }
+ }
+ }"
+ export _H1="Content-Type: application/json;charset=utf8"
+ _post "${body}" "${iam_api}/v3/auth/tokens" >/dev/null
+ _code=$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")
+ _token=$(grep "^X-Subject-Token" "$HTTP_HEADER" | cut -d " " -f 2-)
+ _secure_debug "${_code}"
+ printf "%s" "${_token}"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_infoblox.sh b/acme.sh-master/dnsapi/dns_infoblox.sh
new file mode 100644
index 0000000..6bfd36e
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_infoblox.sh
@@ -0,0 +1,111 @@
+#!/usr/bin/env sh
+
+## Infoblox API integration by Jason Keller and Elijah Tenai
+##
+## Report any bugs via https://github.com/jasonkeller/acme.sh
+
+dns_infoblox_add() {
+
+ ## Nothing to see here, just some housekeeping
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using Infoblox API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ ## Check for the credentials
+ if [ -z "$Infoblox_Creds" ] || [ -z "$Infoblox_Server" ]; then
+ Infoblox_Creds=""
+ Infoblox_Server=""
+ _err "You didn't specify the Infoblox credentials or server (Infoblox_Creds; Infoblox_Server)."
+ _err "Please set them via EXPORT Infoblox_Creds=username:password or EXPORT Infoblox_server=ip/hostname and try again."
+ return 1
+ fi
+
+ if [ -z "$Infoblox_View" ]; then
+ _info "No Infoblox_View set, using fallback value 'default'"
+ Infoblox_View="default"
+ fi
+
+ ## Save the credentials to the account file
+ _saveaccountconf Infoblox_Creds "$Infoblox_Creds"
+ _saveaccountconf Infoblox_Server "$Infoblox_Server"
+ _saveaccountconf Infoblox_View "$Infoblox_View"
+
+ ## URLencode Infoblox View to deal with e.g. spaces
+ Infoblox_ViewEncoded=$(printf "%b" "$Infoblox_View" | _url_encode)
+
+ ## Base64 encode the credentials
+ Infoblox_CredsEncoded=$(printf "%b" "$Infoblox_Creds" | _base64)
+
+ ## Construct the HTTP Authorization header
+ export _H1="Accept-Language:en-US"
+ export _H2="Authorization: Basic $Infoblox_CredsEncoded"
+
+ ## Construct the request URL
+ baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=${Infoblox_ViewEncoded}"
+
+ ## Add the challenge record to the Infoblox grid member
+ result="$(_post "" "$baseurlnObject" "" "POST")"
+
+ ## Let's see if we get something intelligible back from the unit
+ if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
+ _info "Successfully created the txt record"
+ return 0
+ else
+ _err "Error encountered during record addition"
+ _err "$result"
+ return 1
+ fi
+
+}
+
+dns_infoblox_rm() {
+
+ ## Nothing to see here, just some housekeeping
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using Infoblox API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ ## URLencode Infoblox View to deal with e.g. spaces
+ Infoblox_ViewEncoded=$(printf "%b" "$Infoblox_View" | _url_encode)
+
+ ## Base64 encode the credentials
+ Infoblox_CredsEncoded="$(printf "%b" "$Infoblox_Creds" | _base64)"
+
+ ## Construct the HTTP Authorization header
+ export _H1="Accept-Language:en-US"
+ export _H2="Authorization: Basic $Infoblox_CredsEncoded"
+
+ ## Does the record exist? Let's check.
+ baseurlnObject="https://$Infoblox_Server/wapi/v2.2.2/record:txt?name=$fulldomain&text=$txtvalue&view=${Infoblox_ViewEncoded}&_return_type=xml-pretty"
+ result="$(_get "$baseurlnObject")"
+
+ ## Let's see if we get something intelligible back from the grid
+ if [ "$(echo "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
+ ## Extract the object reference
+ objRef="$(printf "%b" "$result" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")"
+ objRmUrl="https://$Infoblox_Server/wapi/v2.2.2/$objRef"
+ ## Delete them! All the stale records!
+ rmResult="$(_post "" "$objRmUrl" "" "DELETE")"
+ ## Let's see if that worked
+ if [ "$(echo "$rmResult" | _egrep_o "record:txt/.*:.*/${Infoblox_ViewEncoded}")" ]; then
+ _info "Successfully deleted $objRef"
+ return 0
+ else
+ _err "Error occurred during txt record delete"
+ _err "$rmResult"
+ return 1
+ fi
+ else
+ _err "Record to delete didn't match an existing record"
+ _err "$result"
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
diff --git a/acme.sh-master/dnsapi/dns_infomaniak.sh b/acme.sh-master/dnsapi/dns_infomaniak.sh
new file mode 100644
index 0000000..a005132
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_infomaniak.sh
@@ -0,0 +1,199 @@
+#!/usr/bin/env sh
+
+###############################################################################
+# Infomaniak API integration
+#
+# To use this API you need visit the API dashboard of your account
+# once logged into https://manager.infomaniak.com add /api/dashboard to the URL
+#
+# Please report bugs to
+# https://github.com/acmesh-official/acme.sh/issues/3188
+#
+# Note: the URL looks like this:
+# https://manager.infomaniak.com/v3//api/dashboard
+# Then generate a token with the scope Domain
+# this is given as an environment variable INFOMANIAK_API_TOKEN
+###############################################################################
+
+# base variables
+
+DEFAULT_INFOMANIAK_API_URL="https://api.infomaniak.com"
+DEFAULT_INFOMANIAK_TTL=300
+
+######## Public functions #####################
+
+#Usage: dns_infomaniak_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_infomaniak_add() {
+
+ INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}"
+ INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}"
+ INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}"
+
+ if [ -z "$INFOMANIAK_API_TOKEN" ]; then
+ INFOMANIAK_API_TOKEN=""
+ _err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN"
+ return 1
+ fi
+
+ if [ -z "$INFOMANIAK_API_URL" ]; then
+ INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL"
+ fi
+
+ if [ -z "$INFOMANIAK_TTL" ]; then
+ INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL"
+ fi
+
+ #save the token to the account conf file.
+ _saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN"
+
+ if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then
+ _saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL"
+ fi
+
+ if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then
+ _saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL"
+ fi
+
+ export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN"
+ export _H2="Content-Type: application/json"
+
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _info "Infomaniak DNS API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ fqdn=${fulldomain#_acme-challenge.}
+
+ # guess which base domain to add record to
+ zone_and_id=$(_find_zone "$fqdn")
+ if [ -z "$zone_and_id" ]; then
+ _err "cannot find zone to modify"
+ return 1
+ fi
+ zone=${zone_and_id% *}
+ domain_id=${zone_and_id#* }
+
+ # extract first part of domain
+ key=${fulldomain%."$zone"}
+
+ _debug "zone:$zone id:$domain_id key:$key"
+
+ # payload
+ data="{\"type\": \"TXT\", \"source\": \"$key\", \"target\": \"$txtvalue\", \"ttl\": $INFOMANIAK_TTL}"
+
+ # API call
+ response=$(_post "$data" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record")
+ if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
+ _info "Record added"
+ _debug "Response: $response"
+ return 0
+ fi
+ _err "could not create record"
+ _debug "Response: $response"
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_infomaniak_rm() {
+
+ INFOMANIAK_API_TOKEN="${INFOMANIAK_API_TOKEN:-$(_readaccountconf_mutable INFOMANIAK_API_TOKEN)}"
+ INFOMANIAK_API_URL="${INFOMANIAK_API_URL:-$(_readaccountconf_mutable INFOMANIAK_API_URL)}"
+ INFOMANIAK_TTL="${INFOMANIAK_TTL:-$(_readaccountconf_mutable INFOMANIAK_TTL)}"
+
+ if [ -z "$INFOMANIAK_API_TOKEN" ]; then
+ INFOMANIAK_API_TOKEN=""
+ _err "Please provide a valid Infomaniak API token in variable INFOMANIAK_API_TOKEN"
+ return 1
+ fi
+
+ if [ -z "$INFOMANIAK_API_URL" ]; then
+ INFOMANIAK_API_URL="$DEFAULT_INFOMANIAK_API_URL"
+ fi
+
+ if [ -z "$INFOMANIAK_TTL" ]; then
+ INFOMANIAK_TTL="$DEFAULT_INFOMANIAK_TTL"
+ fi
+
+ #save the token to the account conf file.
+ _saveaccountconf_mutable INFOMANIAK_API_TOKEN "$INFOMANIAK_API_TOKEN"
+
+ if [ "$INFOMANIAK_API_URL" != "$DEFAULT_INFOMANIAK_API_URL" ]; then
+ _saveaccountconf_mutable INFOMANIAK_API_URL "$INFOMANIAK_API_URL"
+ fi
+
+ if [ "$INFOMANIAK_TTL" != "$DEFAULT_INFOMANIAK_TTL" ]; then
+ _saveaccountconf_mutable INFOMANIAK_TTL "$INFOMANIAK_TTL"
+ fi
+
+ export _H1="Authorization: Bearer $INFOMANIAK_API_TOKEN"
+ export _H2="ContentType: application/json"
+
+ fulldomain=$1
+ txtvalue=$2
+ _info "Infomaniak DNS API"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ fqdn=${fulldomain#_acme-challenge.}
+
+ # guess which base domain to add record to
+ zone_and_id=$(_find_zone "$fqdn")
+ if [ -z "$zone_and_id" ]; then
+ _err "cannot find zone to modify"
+ return 1
+ fi
+ zone=${zone_and_id% *}
+ domain_id=${zone_and_id#* }
+
+ # extract first part of domain
+ key=${fulldomain%."$zone"}
+
+ _debug "zone:$zone id:$domain_id key:$key"
+
+ # find previous record
+ # shellcheck disable=SC1004
+ record_id=$(_get "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record" | sed 's/.*"data":\[\(.*\)\]}/\1/; s/},{/}\
+{/g' | sed -n 's/.*"id":"*\([0-9]*\)"*.*"source_idn":"'"$fulldomain"'".*"target_idn":"'"$txtvalue"'".*/\1/p')
+ if [ -z "$record_id" ]; then
+ _err "could not find record to delete"
+ return 1
+ fi
+ _debug "record_id: $record_id"
+
+ # API call
+ response=$(_post "" "${INFOMANIAK_API_URL}/1/domain/$domain_id/dns/record/$record_id" "" DELETE)
+ if [ -n "$response" ] && echo "$response" | _contains '"result":"success"'; then
+ _info "Record deleted"
+ return 0
+ fi
+ _err "could not delete record"
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_get_domain_id() {
+ domain="$1"
+
+ # shellcheck disable=SC1004
+ _get "${INFOMANIAK_API_URL}/1/product?service_name=domain&customer_name=$domain" | sed 's/.*"data":\[{\(.*\)}\]}/\1/; s/,/\
+/g' | sed -n 's/^"id":\(.*\)/\1/p'
+}
+
+_find_zone() {
+ zone="$1"
+
+ # find domain in list, removing . parts sequentialy
+ while _contains "$zone" '\.'; do
+ _debug "testing $zone"
+ id=$(_get_domain_id "$zone")
+ if [ -n "$id" ]; then
+ echo "$zone $id"
+ return
+ fi
+ zone=${zone#*.}
+ done
+}
diff --git a/acme.sh-master/dnsapi/dns_internetbs.sh b/acme.sh-master/dnsapi/dns_internetbs.sh
new file mode 100644
index 0000000..ae6b9e1
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_internetbs.sh
@@ -0,0 +1,180 @@
+#!/usr/bin/env sh
+
+#This is the Internet.BS api wrapper for acme.sh
+#
+#Author: Ne-Lexa
+#Report Bugs here: https://github.com/Ne-Lexa/acme.sh
+
+#INTERNETBS_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#INTERNETBS_API_PASSWORD="sdfsdfsdfljlbjkljlkjsdfoiwje"
+
+INTERNETBS_API_URL="https://api.internet.bs"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_internetbs_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ INTERNETBS_API_KEY="${INTERNETBS_API_KEY:-$(_readaccountconf_mutable INTERNETBS_API_KEY)}"
+ INTERNETBS_API_PASSWORD="${INTERNETBS_API_PASSWORD:-$(_readaccountconf_mutable INTERNETBS_API_PASSWORD)}"
+
+ if [ -z "$INTERNETBS_API_KEY" ] || [ -z "$INTERNETBS_API_PASSWORD" ]; then
+ INTERNETBS_API_KEY=""
+ INTERNETBS_API_PASSWORD=""
+ _err "You didn't specify the INTERNET.BS api key and password yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable INTERNETBS_API_KEY "$INTERNETBS_API_KEY"
+ _saveaccountconf_mutable INTERNETBS_API_PASSWORD "$INTERNETBS_API_PASSWORD"
+
+ _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"
+
+ # https://testapi.internet.bs/Domain/DnsRecord/Add?ApiKey=testapi&Password=testpass&FullRecordName=w3.test-api-domain7.net&Type=CNAME&Value=www.internet.bs%&ResponseFormat=json
+ if _internetbs_rest POST "Domain/DnsRecord/Add" "FullRecordName=${_sub_domain}.${_domain}&Type=TXT&Value=${txtvalue}&ResponseFormat=json"; then
+ if ! _contains "$response" "\"status\":\"SUCCESS\""; then
+ _err "ERROR add TXT record"
+ _err "$response"
+ return 1
+ fi
+
+ _info "txt record add success."
+ return 0
+ fi
+
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_internetbs_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ INTERNETBS_API_KEY="${INTERNETBS_API_KEY:-$(_readaccountconf_mutable INTERNETBS_API_KEY)}"
+ INTERNETBS_API_PASSWORD="${INTERNETBS_API_PASSWORD:-$(_readaccountconf_mutable INTERNETBS_API_PASSWORD)}"
+
+ if [ -z "$INTERNETBS_API_KEY" ] || [ -z "$INTERNETBS_API_PASSWORD" ]; then
+ INTERNETBS_API_KEY=""
+ INTERNETBS_API_PASSWORD=""
+ _err "You didn't specify the INTERNET.BS api key and password yet."
+ _err "Please create you key and try again."
+ 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"
+ # https://testapi.internet.bs/Domain/DnsRecord/List?ApiKey=testapi&Password=testpass&Domain=test-api-domain7.net&FilterType=CNAME&ResponseFormat=json
+ _internetbs_rest POST "Domain/DnsRecord/List" "Domain=$_domain&FilterType=TXT&ResponseFormat=json"
+
+ if ! _contains "$response" "\"status\":\"SUCCESS\""; then
+ _err "ERROR list dns records"
+ _err "$response"
+ return 1
+ fi
+
+ if _contains "$response" "\name\":\"${_sub_domain}.${_domain}\""; then
+ _info "txt record find."
+
+ # https://testapi.internet.bs/Domain/DnsRecord/Remove?ApiKey=testapi&Password=testpass&FullRecordName=www.test-api-domain7.net&Type=cname&ResponseFormat=json
+ _internetbs_rest POST "Domain/DnsRecord/Remove" "FullRecordName=${_sub_domain}.${_domain}&Type=TXT&ResponseFormat=json"
+
+ if ! _contains "$response" "\"status\":\"SUCCESS\""; then
+ _err "ERROR remove dns record"
+ _err "$response"
+ return 1
+ fi
+
+ _info "txt record deleted success."
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ # https://testapi.internet.bs/Domain/List?ApiKey=testapi&Password=testpass&CompactList=yes&ResponseFormat=json
+ if _internetbs_rest POST "Domain/List" "CompactList=yes&ResponseFormat=json"; then
+
+ if ! _contains "$response" "\"status\":\"SUCCESS\""; then
+ _err "ERROR fetch domain list"
+ _err "$response"
+ return 1
+ fi
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f ${i}-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-${p})
+ _domain=${h}
+ return 0
+ fi
+
+ p=${i}
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#Usage: method URI data
+_internetbs_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ url="${INTERNETBS_API_URL}/${ep}"
+
+ _debug url "$url"
+
+ apiKey="$(printf "%s" "${INTERNETBS_API_KEY}" | _url_encode)"
+ password="$(printf "%s" "${INTERNETBS_API_PASSWORD}" | _url_encode)"
+
+ if [ "$m" = "GET" ]; then
+ response="$(_get "${url}?ApiKey=${apiKey}&Password=${password}&${data}" | tr -d '\r')"
+ else
+ _debug2 data "$data"
+ response="$(_post "$data" "${url}?ApiKey=${apiKey}&Password=${password}" | tr -d '\r')"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_inwx.sh b/acme.sh-master/dnsapi/dns_inwx.sh
new file mode 100644
index 0000000..ba789da
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_inwx.sh
@@ -0,0 +1,420 @@
+#!/usr/bin/env sh
+
+#
+#INWX_User="username"
+#
+#INWX_Password="password"
+#
+# Dependencies:
+# -------------
+# - oathtool (When using 2 Factor Authentication)
+
+INWX_Api="https://api.domrobot.com/xmlrpc/"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_inwx_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
+ INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
+ INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
+ if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
+ INWX_User=""
+ INWX_Password=""
+ _err "You don't specify inwx user and password yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable INWX_User "$INWX_User"
+ _saveaccountconf_mutable INWX_Password "$INWX_Password"
+ _saveaccountconf_mutable INWX_Shared_Secret "$INWX_Shared_Secret"
+
+ if ! _inwx_login; 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"
+
+ _info "Adding record"
+ _inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
+
+}
+
+#fulldomain txtvalue
+dns_inwx_rm() {
+
+ fulldomain=$1
+ txtvalue=$2
+
+ INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
+ INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
+ INWX_Shared_Secret="${INWX_Shared_Secret:-$(_readaccountconf_mutable INWX_Shared_Secret)}"
+ if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
+ INWX_User=""
+ INWX_Password=""
+ _err "You don't specify inwx user and password yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ if ! _inwx_login; 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"
+
+ xml_content=$(printf '
+
+ nameserver.info
+
+
+
+
+
+ domain
+
+ %s
+
+
+
+ type
+
+ TXT
+
+
+
+ name
+
+ %s
+
+
+
+
+
+
+ ' "$_domain" "$_sub_domain")
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! _contains "$response" "Command completed successfully"; then
+ _err "Error could not get txt records"
+ return 1
+ fi
+
+ if ! printf "%s" "$response" | grep "count" >/dev/null; then
+ _info "Do not need to delete record"
+ else
+ _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+')
+ _info "Deleting record"
+ _inwx_delete_record "$_record_id"
+ fi
+
+}
+
+#################### Private functions below ##################################
+
+_inwx_check_cookie() {
+ INWX_Cookie="${INWX_Cookie:-$(_readaccountconf_mutable INWX_Cookie)}"
+ if [ -z "$INWX_Cookie" ]; then
+ _debug "No cached cookie found"
+ return 1
+ fi
+ _H1="$INWX_Cookie"
+ export _H1
+
+ xml_content=$(printf '
+
+ account.info
+ ')
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if _contains "$response" "code1000"; then
+ _debug "Cached cookie still valid"
+ return 0
+ fi
+
+ _debug "Cached cookie no longer valid"
+ _H1=""
+ export _H1
+ INWX_Cookie=""
+ _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
+ return 1
+}
+
+_inwx_login() {
+
+ if _inwx_check_cookie; then
+ _debug "Already logged in"
+ return 0
+ fi
+
+ xml_content=$(printf '
+
+ account.login
+
+
+
+
+
+ user
+
+ %s
+
+
+
+ pass
+
+ %s
+
+
+
+
+
+
+ ' "$INWX_User" "$INWX_Password")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+ _H1=$INWX_Cookie
+ export _H1
+ export INWX_Cookie
+ _saveaccountconf_mutable INWX_Cookie "$INWX_Cookie"
+
+ if ! _contains "$response" "code1000"; then
+ _err "INWX API: Authentication error (username/password correct?)"
+ return 1
+ fi
+
+ #https://github.com/inwx/php-client/blob/master/INWX/Domrobot.php#L71
+ if _contains "$response" "tfaGOOGLE-AUTH"; then
+ if [ -z "$INWX_Shared_Secret" ]; then
+ _err "INWX API: Mobile TAN detected."
+ _err "Please define a shared secret."
+ return 1
+ fi
+
+ if ! _exists oathtool; then
+ _err "Please install oathtool to use 2 Factor Authentication."
+ _err ""
+ return 1
+ fi
+
+ tan="$(oathtool --base32 --totp "${INWX_Shared_Secret}" 2>/dev/null)"
+
+ xml_content=$(printf '
+
+ account.unlock
+
+
+
+
+
+ tan
+
+ %s
+
+
+
+
+
+
+ ' "$tan")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! _contains "$response" "code1000"; then
+ _err "INWX API: Mobile TAN not correct."
+ return 1
+ fi
+ fi
+
+}
+
+_get_root() {
+ domain=$1
+ _debug "get root"
+
+ domain=$1
+ i=2
+ p=1
+
+ xml_content='
+
+ nameserver.list
+
+
+
+
+
+ pagelimit
+
+ 9999
+
+
+
+
+
+
+ '
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "$h"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+
+}
+
+_inwx_delete_record() {
+ record_id=$1
+ xml_content=$(printf '
+
+ nameserver.deleteRecord
+
+
+
+
+
+ id
+
+ %s
+
+
+
+
+
+
+ ' "$record_id")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+
+}
+
+_inwx_update_record() {
+ record_id=$1
+ txtval=$2
+ xml_content=$(printf '
+
+ nameserver.updateRecord
+
+
+
+
+
+ content
+
+ %s
+
+
+
+ id
+
+ %s
+
+
+
+
+
+
+ ' "$txtval" "$record_id")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+
+}
+
+_inwx_add_record() {
+
+ domain=$1
+ sub_domain=$2
+ txtval=$3
+
+ xml_content=$(printf '
+
+ nameserver.createRecord
+
+
+
+
+
+ domain
+
+ %s
+
+
+
+ type
+
+ TXT
+
+
+
+ content
+
+ %s
+
+
+
+ name
+
+ %s
+
+
+
+
+
+
+ ' "$domain" "$txtval" "$sub_domain")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_ionos.sh b/acme.sh-master/dnsapi/dns_ionos.sh
new file mode 100644
index 0000000..e4ad331
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_ionos.sh
@@ -0,0 +1,171 @@
+#!/usr/bin/env sh
+
+# Supports IONOS DNS API v1.0.1
+#
+# Usage:
+# Export IONOS_PREFIX and IONOS_SECRET before calling acme.sh:
+#
+# $ export IONOS_PREFIX="..."
+# $ export IONOS_SECRET="..."
+#
+# $ acme.sh --issue --dns dns_ionos ...
+
+IONOS_API="https://api.hosting.ionos.com/dns"
+IONOS_ROUTE_ZONES="/v1/zones"
+
+IONOS_TXT_TTL=60 # minimum accepted by API
+IONOS_TXT_PRIO=10
+
+dns_ionos_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _ionos_init; then
+ return 1
+ fi
+
+ _body="[{\"name\":\"$_sub_domain.$_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":$IONOS_TXT_TTL,\"prio\":$IONOS_TXT_PRIO,\"disabled\":false}]"
+
+ if _ionos_rest POST "$IONOS_ROUTE_ZONES/$_zone_id/records" "$_body" && [ "$_code" = "201" ]; then
+ _info "TXT record has been created successfully."
+ return 0
+ fi
+
+ return 1
+}
+
+dns_ionos_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _ionos_init; then
+ return 1
+ fi
+
+ if ! _ionos_get_record "$fulldomain" "$_zone_id" "$txtvalue"; then
+ _err "Could not find _acme-challenge TXT record."
+ return 1
+ fi
+
+ if _ionos_rest DELETE "$IONOS_ROUTE_ZONES/$_zone_id/records/$_record_id" && [ "$_code" = "200" ]; then
+ _info "TXT record has been deleted successfully."
+ return 0
+ fi
+
+ return 1
+}
+
+_ionos_init() {
+ IONOS_PREFIX="${IONOS_PREFIX:-$(_readaccountconf_mutable IONOS_PREFIX)}"
+ IONOS_SECRET="${IONOS_SECRET:-$(_readaccountconf_mutable IONOS_SECRET)}"
+
+ if [ -z "$IONOS_PREFIX" ] || [ -z "$IONOS_SECRET" ]; then
+ _err "You didn't specify an IONOS api prefix and secret yet."
+ _err "Read https://beta.developer.hosting.ionos.de/docs/getstarted to learn how to get a prefix and secret."
+ _err ""
+ _err "Then set them before calling acme.sh:"
+ _err "\$ export IONOS_PREFIX=\"...\""
+ _err "\$ export IONOS_SECRET=\"...\""
+ _err "\$ acme.sh --issue -d ... --dns dns_ionos"
+ return 1
+ fi
+
+ _saveaccountconf_mutable IONOS_PREFIX "$IONOS_PREFIX"
+ _saveaccountconf_mutable IONOS_SECRET "$IONOS_SECRET"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Cannot find this domain in your IONOS account."
+ return 1
+ fi
+}
+
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ if _ionos_rest GET "$IONOS_ROUTE_ZONES"; then
+ _response="$(echo "$_response" | tr -d "\n")"
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ _zone="$(echo "$_response" | _egrep_o "\"name\":\"$h\".*\}")"
+ if [ "$_zone" ]; then
+ _zone_id=$(printf "%s\n" "$_zone" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
+ if [ "$_zone_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+
+ return 0
+ fi
+
+ return 1
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+
+ return 1
+}
+
+_ionos_get_record() {
+ fulldomain=$1
+ zone_id=$2
+ txtrecord=$3
+
+ if _ionos_rest GET "$IONOS_ROUTE_ZONES/$zone_id?recordName=$fulldomain&recordType=TXT"; then
+ _response="$(echo "$_response" | tr -d "\n")"
+
+ _record="$(echo "$_response" | _egrep_o "\"name\":\"$fulldomain\"[^\}]*\"type\":\"TXT\"[^\}]*\"content\":\"\\\\\"$txtrecord\\\\\"\".*\}")"
+ if [ "$_record" ]; then
+ _record_id=$(printf "%s\n" "$_record" | _egrep_o "\"id\":\"[a-fA-F0-9\-]*\"" | _head_n 1 | cut -d : -f 2 | tr -d '\"')
+
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+_ionos_rest() {
+ method="$1"
+ route="$2"
+ data="$3"
+
+ IONOS_API_KEY="$(printf "%s.%s" "$IONOS_PREFIX" "$IONOS_SECRET")"
+
+ export _H1="X-API-Key: $IONOS_API_KEY"
+
+ # clear headers
+ : >"$HTTP_HEADER"
+
+ if [ "$method" != "GET" ]; then
+ export _H2="Accept: application/json"
+ export _H3="Content-Type: application/json"
+
+ _response="$(_post "$data" "$IONOS_API$route" "" "$method" "application/json")"
+ else
+ export _H2="Accept: */*"
+ export _H3=
+
+ _response="$(_get "$IONOS_API$route")"
+ fi
+
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+
+ if [ "$?" != "0" ]; then
+ _err "Error $route: $_response"
+ return 1
+ fi
+
+ _debug2 "_response" "$_response"
+ _debug2 "_code" "$_code"
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_ipv64.sh b/acme.sh-master/dnsapi/dns_ipv64.sh
new file mode 100644
index 0000000..5447011
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_ipv64.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+#Created by Roman Lumetsberger, to use ipv64.net's API to add/remove text records
+#2022/11/29
+
+# Pass credentials before "acme.sh --issue --dns dns_ipv64 ..."
+# --
+# export IPv64_Token="aaaaaaaaaaaaaaaaaaaaaaaaaa"
+# --
+#
+
+IPv64_API="https://ipv64.net/api"
+
+######## Public functions ######################
+
+#Usage: dns_ipv64_add _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ipv64_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
+ if [ -z "$IPv64_Token" ]; then
+ _err "You must export variable: IPv64_Token"
+ _err "The API Key for your IPv64 account is necessary."
+ _err "You can look it up in your IPv64 account."
+ return 1
+ fi
+
+ # Now save the credentials.
+ _saveaccountconf_mutable IPv64_Token "$IPv64_Token"
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # convert to lower case
+ _domain="$(echo "$_domain" | _lower_case)"
+ _sub_domain="$(echo "$_sub_domain" | _lower_case)"
+ # Now add the TXT record
+ _info "Trying to add TXT record"
+ if _ipv64_rest "POST" "add_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
+ _info "TXT record has been successfully added."
+ return 0
+ else
+ _err "Errors happened during adding the TXT record, response=$_response"
+ return 1
+ fi
+
+}
+
+#Usage: fulldomain txtvalue
+#Usage: dns_ipv64_rm _acme-challenge.domain.ipv64.net "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+#Remove the txt record after validation.
+dns_ipv64_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ IPv64_Token="${IPv64_Token:-$(_readaccountconf_mutable IPv64_Token)}"
+ if [ -z "$IPv64_Token" ]; then
+ _err "You must export variable: IPv64_Token"
+ _err "The API Key for your IPv64 account is necessary."
+ _err "You can look it up in your IPv64 account."
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # convert to lower case
+ _domain="$(echo "$_domain" | _lower_case)"
+ _sub_domain="$(echo "$_sub_domain" | _lower_case)"
+ # Now delete the TXT record
+ _info "Trying to delete TXT record"
+ if _ipv64_rest "DELETE" "del_record=$_domain&praefix=$_sub_domain&type=TXT&content=$txtvalue"; then
+ _info "TXT record has been successfully deleted."
+ return 0
+ else
+ _err "Errors happened during deleting the TXT record, response=$_response"
+ return 1
+ fi
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ i=1
+ p=1
+
+ _ipv64_get "get_domains"
+ domain_data=$_response
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ #if _contains "$domain_data" "\""$h"\"\:"; then
+ if _contains "$domain_data" "\"""$h""\"\:"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+#send get request to api
+# $1 has to set the api-function
+_ipv64_get() {
+ url="$IPv64_API?$1"
+ export _H1="Authorization: Bearer $IPv64_Token"
+
+ _response=$(_get "$url")
+ _response="$(echo "$_response" | _normalizeJson)"
+
+ if _contains "$_response" "429 Too Many Requests"; then
+ _info "API throttled, sleeping to reset the limit"
+ _sleep 10
+ _response=$(_get "$url")
+ _response="$(echo "$_response" | _normalizeJson)"
+ fi
+}
+
+_ipv64_rest() {
+ url="$IPv64_API"
+ export _H1="Authorization: Bearer $IPv64_Token"
+ export _H2="Content-Type: application/x-www-form-urlencoded"
+ _response=$(_post "$2" "$url" "" "$1")
+
+ if _contains "$_response" "429 Too Many Requests"; then
+ _info "API throttled, sleeping to reset the limit"
+ _sleep 10
+ _response=$(_post "$2" "$url" "" "$1")
+ fi
+
+ if ! _contains "$_response" "\"info\":\"success\""; then
+ return 1
+ fi
+ _debug2 response "$_response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_ispconfig.sh b/acme.sh-master/dnsapi/dns_ispconfig.sh
new file mode 100644
index 0000000..560f073
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_ispconfig.sh
@@ -0,0 +1,194 @@
+#!/usr/bin/env sh
+
+# ISPConfig 3.1 API
+# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
+# - DNS txt Functions
+
+# Report bugs to https://github.com/sjau/acme.sh
+
+# Values to export:
+# export ISPC_User="remoteUser"
+# export ISPC_Password="remotePassword"
+# export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
+# export ISPC_Api_Insecure=1 # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ispconfig_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+ _debug "Calling: dns_ispconfig_add() '${fulldomain}' '${txtvalue}'"
+ _ISPC_credentials && _ISPC_login && _ISPC_getZoneInfo && _ISPC_addTxt
+}
+
+#Usage: dns_myapi_rm _acme-challenge.www.domain.com
+dns_ispconfig_rm() {
+ fulldomain="${1}"
+ _debug "Calling: dns_ispconfig_rm() '${fulldomain}'"
+ _ISPC_credentials && _ISPC_login && _ISPC_rmTxt
+}
+
+#################### Private functions below ##################################
+
+_ISPC_credentials() {
+ ISPC_User="${ISPC_User:-$(_readaccountconf_mutable ISPC_User)}"
+ ISPC_Password="${ISPC_Password:-$(_readaccountconf_mutable ISPC_Password)}"
+ ISPC_Api="${ISPC_Api:-$(_readaccountconf_mutable ISPC_Api)}"
+ ISPC_Api_Insecure="${ISPC_Api_Insecure:-$(_readaccountconf_mutable ISPC_Api_Insecure)}"
+ if [ -z "${ISPC_User}" ] || [ -z "${ISPC_Password}" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
+ ISPC_User=""
+ ISPC_Password=""
+ ISPC_Api=""
+ ISPC_Api_Insecure=""
+ _err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
+ return 1
+ else
+ _saveaccountconf_mutable ISPC_User "${ISPC_User}"
+ _saveaccountconf_mutable ISPC_Password "${ISPC_Password}"
+ _saveaccountconf_mutable ISPC_Api "${ISPC_Api}"
+ _saveaccountconf_mutable ISPC_Api_Insecure "${ISPC_Api_Insecure}"
+ # Set whether curl should use secure or insecure mode
+ export HTTPS_INSECURE="${ISPC_Api_Insecure}"
+ fi
+}
+
+_ISPC_login() {
+ _info "Getting Session ID"
+ curData="{\"username\":\"${ISPC_User}\",\"password\":\"${ISPC_Password}\",\"client_login\":false}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?login")"
+ _debug "Calling _ISPC_login: '${curData}' '${ISPC_Api}?login'"
+ _debug "Result of _ISPC_login: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ sessionID=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _info "Retrieved Session ID."
+ _debug "Session ID: '${sessionID}'"
+ else
+ _err "Couldn't retrieve the Session ID."
+ return 1
+ fi
+}
+
+_ISPC_getZoneInfo() {
+ _info "Getting Zoneinfo"
+ zoneEnd=false
+ curZone="${fulldomain}"
+ while [ "${zoneEnd}" = false ]; do
+ # we can strip the first part of the fulldomain, since it's just the _acme-challenge string
+ curZone="${curZone#*.}"
+ # suffix . needed for zone -> domain.tld.
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")"
+ _debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?dns_zone_get'"
+ _debug "Result of _ISPC_getZoneInfo: '$curResult'"
+ if _contains "${curResult}" '"id":"'; then
+ zoneFound=true
+ zoneEnd=true
+ _info "Retrieved zone data."
+ _debug "Zone data: '${curResult}'"
+ fi
+ if [ "${curZone#*.}" != "$curZone" ]; then
+ _debug2 "$curZone still contains a '.' - so we can check next higher level"
+ else
+ zoneEnd=true
+ _err "Couldn't retrieve zone data."
+ return 1
+ fi
+ done
+ if [ "${zoneFound}" ]; then
+ server_id=$(echo "${curResult}" | _egrep_o "server_id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Server ID: '${server_id}'"
+ case "${server_id}" in
+ '' | *[!0-9]*)
+ _err "Server ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Server ID" ;;
+ esac
+ zone=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Zone: '${zone}'"
+ case "${zone}" in
+ '' | *[!0-9]*)
+ _err "Zone ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Zone ID" ;;
+ esac
+ sys_userid=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "SYS User ID: '${sys_userid}'"
+ case "${sys_userid}" in
+ '' | *[!0-9]*)
+ _err "SYS User ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved SYS User ID." ;;
+ esac
+ zoneFound=""
+ zoneEnd=""
+ fi
+ # Need to get client_id as it is different from sys_userid
+ curData="{\"session_id\":\"${sessionID}\",\"sys_userid\":\"${sys_userid}\"}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?client_get_id")"
+ _debug "Calling _ISPC_ClientGetID: '${curData}' '${ISPC_Api}?client_get_id'"
+ _debug "Result of _ISPC_ClientGetID: '$curResult'"
+ client_id=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2 | tr -d '{}')
+ _debug "Client ID: '${client_id}'"
+ case "${client_id}" in
+ '' | *[!0-9]*)
+ _err "Client ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Client ID." ;;
+ esac
+}
+
+_ISPC_addTxt() {
+ curSerial="$(date +%s)"
+ curStamp="$(date +'%F %T')"
+ params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\""
+ curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}},\"update_serial\":true}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")"
+ _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'"
+ _debug "Result of _ISPC_addTxt: '$curResult'"
+ record_id=$(echo "${curResult}" | _egrep_o "\"response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Record ID: '${record_id}'"
+ case "${record_id}" in
+ '' | *[!0-9]*)
+ _err "Couldn't add ACME Challenge TXT record to zone."
+ return 1
+ ;;
+ *) _info "Added ACME Challenge TXT record to zone." ;;
+ esac
+}
+
+_ISPC_rmTxt() {
+ # Need to get the record ID.
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"name\":\"${fulldomain}.\",\"type\":\"TXT\"}}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_get")"
+ _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_get'"
+ _debug "Result of _ISPC_rmTxt: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ record_id=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Record ID: '${record_id}'"
+ case "${record_id}" in
+ '' | *[!0-9]*)
+ _err "Record ID is not numeric."
+ return 1
+ ;;
+ *)
+ unset IFS
+ _info "Retrieved Record ID."
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\",\"update_serial\":true}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")"
+ _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'"
+ _debug "Result of _ISPC_rmTxt: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ _info "Removed ACME Challenge TXT record from zone."
+ else
+ _err "Couldn't remove ACME Challenge TXT record from zone."
+ return 1
+ fi
+ ;;
+ esac
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_jd.sh b/acme.sh-master/dnsapi/dns_jd.sh
new file mode 100644
index 0000000..d0f2a50
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_jd.sh
@@ -0,0 +1,286 @@
+#!/usr/bin/env sh
+
+#
+#JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#JD_ACCESS_KEY_SECRET="xxxxxxx"
+#JD_REGION="cn-north-1"
+
+_JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey"
+
+_JD_PROD="clouddnsservice"
+_JD_API="jdcloud-api.com"
+
+_JD_API_VERSION="v1"
+_JD_DEFAULT_REGION="cn-north-1"
+
+_JD_HOST="$_JD_PROD.$_JD_API"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_jd_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}"
+ JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}"
+ JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}"
+
+ if [ -z "$JD_ACCESS_KEY_ID" ] || [ -z "$JD_ACCESS_KEY_SECRET" ]; then
+ JD_ACCESS_KEY_ID=""
+ JD_ACCESS_KEY_SECRET=""
+ _err "You haven't specifed the jdcloud api key id or api key secret yet."
+ _err "Please create your key and try again. see $(__green $_JD_ACCOUNT)"
+ return 1
+ fi
+
+ _saveaccountconf_mutable JD_ACCESS_KEY_ID "$JD_ACCESS_KEY_ID"
+ _saveaccountconf_mutable JD_ACCESS_KEY_SECRET "$JD_ACCESS_KEY_SECRET"
+ if [ -z "$JD_REGION" ]; then
+ _debug "Using default region: $_JD_DEFAULT_REGION"
+ JD_REGION="$_JD_DEFAULT_REGION"
+ else
+ _saveaccountconf_mutable JD_REGION "$JD_REGION"
+ fi
+ _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION"
+
+ _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 getViewTree"
+
+ _debug "Adding records"
+
+ _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":300,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}"
+ #_addrr='{"req":{"hostRecord":"xx","hostValue":"\"value4\"","jcloudRes":false,"mxPriority":null,"port":null,"ttl":300,"type":"TXT","weight":null,"viewValue":-1},"regionId":"cn-north-1","domainId":"8824"}'
+ if jd_rest POST "domain/$_domain_id/RRAdd" "" "$_addrr"; then
+ _rid="$(echo "$response" | tr '{},' '\n' | grep '"id":' | cut -d : -f 2)"
+ if [ -z "$_rid" ]; then
+ _err "Can not find record id from the result."
+ return 1
+ fi
+ _info "TXT record added successfully."
+ _srid="$(_readdomainconf "JD_CLOUD_RIDS")"
+ if [ "$_srid" ]; then
+ _rid="$_srid,$_rid"
+ fi
+ _savedomainconf "JD_CLOUD_RIDS" "$_rid"
+ return 0
+ fi
+
+ return 1
+}
+
+dns_jd_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}"
+ JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}"
+ JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}"
+
+ if [ -z "$JD_REGION" ]; then
+ _debug "Using default region: $_JD_DEFAULT_REGION"
+ JD_REGION="$_JD_DEFAULT_REGION"
+ fi
+
+ _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION"
+
+ _info "Getting existing records for $fulldomain"
+ _srid="$(_readdomainconf "JD_CLOUD_RIDS")"
+ _debug _srid "$_srid"
+
+ if [ -z "$_srid" ]; then
+ _err "Not rid skip"
+ return 0
+ fi
+
+ _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"
+
+ _cleardomainconf JD_CLOUD_RIDS
+
+ _aws_tmpl_xml="{\"ids\":[$_srid],\"action\":\"del\",\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}"
+
+ if jd_rest POST "domain/$_domain_id/RROperate" "" "$_aws_tmpl_xml" && _contains "$response" "\"code\":\"OK\""; then
+ _info "TXT record deleted successfully."
+ return 0
+ fi
+ return 1
+
+}
+
+#################### Private functions below ##################################
+
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug2 "Checking domain: $h"
+ if ! jd_rest GET "domain"; then
+ _err "error get domain list"
+ return 1
+ fi
+ if [ -z "$h" ]; then
+ #not valid
+ _err "Invalid domain"
+ return 1
+ fi
+
+ if _contains "$response" "\"domainName\":\"$h\""; then
+ hostedzone="$(echo "$response" | tr '{}' '\n' | grep "\"domainName\":\"$h\"")"
+ _debug hostedzone "$hostedzone"
+ if [ "$hostedzone" ]; then
+ _domain_id="$(echo "$hostedzone" | tr ',' '\n' | grep "\"id\":" | cut -d : -f 2)"
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ fi
+ _err "Can't find domain with id: $h"
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+#method uri qstr data
+jd_rest() {
+ mtd="$1"
+ ep="$2"
+ qsr="$3"
+ data="$4"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+ _debug qsr "$qsr"
+ _debug data "$data"
+
+ CanonicalURI="/$_JD_BASE_URI/$ep"
+ _debug2 CanonicalURI "$CanonicalURI"
+
+ CanonicalQueryString="$qsr"
+ _debug2 CanonicalQueryString "$CanonicalQueryString"
+
+ RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")"
+ #RequestDate="20190713T082155Z" ######################################################
+ _debug2 RequestDate "$RequestDate"
+ export _H1="X-Jdcloud-Date: $RequestDate"
+
+ RequestNonce="2bd0852a-8bae-4087-b2d5-$(_time)"
+ #RequestNonce="894baff5-72d4-4244-883a-7b2eb51e7fbe" #################################
+ _debug2 RequestNonce "$RequestNonce"
+ export _H2="X-Jdcloud-Nonce: $RequestNonce"
+
+ if [ "$data" ]; then
+ CanonicalHeaders="content-type:application/json\n"
+ SignedHeaders="content-type;"
+ else
+ CanonicalHeaders=""
+ SignedHeaders=""
+ fi
+ CanonicalHeaders="${CanonicalHeaders}host:$_JD_HOST\nx-jdcloud-date:$RequestDate\nx-jdcloud-nonce:$RequestNonce\n"
+ SignedHeaders="${SignedHeaders}host;x-jdcloud-date;x-jdcloud-nonce"
+
+ _debug2 CanonicalHeaders "$CanonicalHeaders"
+ _debug2 SignedHeaders "$SignedHeaders"
+
+ Hash="sha256"
+
+ RequestPayload="$data"
+ _debug2 RequestPayload "$RequestPayload"
+
+ RequestPayloadHash="$(printf "%s" "$RequestPayload" | _digest "$Hash" hex | _lower_case)"
+ _debug2 RequestPayloadHash "$RequestPayloadHash"
+
+ CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$RequestPayloadHash"
+ _debug2 CanonicalRequest "$CanonicalRequest"
+
+ HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)"
+ _debug2 HashedCanonicalRequest "$HashedCanonicalRequest"
+
+ Algorithm="JDCLOUD2-HMAC-SHA256"
+ _debug2 Algorithm "$Algorithm"
+
+ RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)"
+ _debug2 RequestDateOnly "$RequestDateOnly"
+
+ Region="$JD_REGION"
+ Service="$_JD_PROD"
+
+ CredentialScope="$RequestDateOnly/$Region/$Service/jdcloud2_request"
+ _debug2 CredentialScope "$CredentialScope"
+
+ StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest"
+
+ _debug2 StringToSign "$StringToSign"
+
+ kSecret="JDCLOUD2$JD_ACCESS_KEY_SECRET"
+
+ _secure_debug2 kSecret "$kSecret"
+
+ kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
+ _secure_debug2 kSecretH "$kSecretH"
+
+ kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
+ _debug2 kDateH "$kDateH"
+
+ kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)"
+ _debug2 kRegionH "$kRegionH"
+
+ kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)"
+ _debug2 kServiceH "$kServiceH"
+
+ kSigningH="$(printf "%s" "jdcloud2_request" | _hmac "$Hash" "$kServiceH" hex)"
+ _debug2 kSigningH "$kSigningH"
+
+ signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)"
+ _debug2 signature "$signature"
+
+ Authorization="$Algorithm Credential=$JD_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature"
+ _debug2 Authorization "$Authorization"
+
+ _H3="Authorization: $Authorization"
+ _debug _H3 "$_H3"
+
+ url="https://$_JD_HOST$CanonicalURI"
+ if [ "$qsr" ]; then
+ url="https://$_JD_HOST$CanonicalURI?$qsr"
+ fi
+
+ if [ "$mtd" = "GET" ]; then
+ response="$(_get "$url")"
+ else
+ response="$(_post "$data" "$url" "" "$mtd" "application/json")"
+ fi
+
+ _ret="$?"
+ _debug2 response "$response"
+ if [ "$_ret" = "0" ]; then
+ if _contains "$response" "\"error\""; then
+ _err "Response error:$response"
+ return 1
+ fi
+ fi
+
+ return "$_ret"
+}
diff --git a/acme.sh-master/dnsapi/dns_joker.sh b/acme.sh-master/dnsapi/dns_joker.sh
new file mode 100644
index 0000000..78399a1
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_joker.sh
@@ -0,0 +1,129 @@
+#!/usr/bin/env sh
+
+# Joker.com API for acme.sh
+#
+# This script adds the necessary TXT record to a domain in Joker.com.
+#
+# You must activate Dynamic DNS in Joker.com DNS configuration first.
+# Username and password below refer to Dynamic DNS authentication,
+# not your Joker.com login credentials.
+# See: https://joker.com/faq/content/11/427/en/what-is-dynamic-dns-dyndns.html
+#
+# NOTE: This script does not support wildcard certificates, because
+# Joker.com API does not support adding two TXT records with the same
+# subdomain. Adding the second record will overwrite the first one.
+# See: https://joker.com/faq/content/6/496/en/let_s-encrypt-support.html
+# "... this request will replace all TXT records for the specified
+# label by the provided content"
+#
+# Author: aattww (https://github.com/aattww/)
+#
+# Report bugs to https://github.com/acmesh-official/acme.sh/issues/2840
+#
+# JOKER_USERNAME="xxxx"
+# JOKER_PASSWORD="xxxx"
+
+JOKER_API="https://svc.joker.com/nic/replace"
+
+######## Public functions #####################
+
+#Usage: dns_joker_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_joker_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JOKER_USERNAME="${JOKER_USERNAME:-$(_readaccountconf_mutable JOKER_USERNAME)}"
+ JOKER_PASSWORD="${JOKER_PASSWORD:-$(_readaccountconf_mutable JOKER_PASSWORD)}"
+
+ if [ -z "$JOKER_USERNAME" ] || [ -z "$JOKER_PASSWORD" ]; then
+ _err "No Joker.com username and password specified."
+ return 1
+ fi
+
+ _saveaccountconf_mutable JOKER_USERNAME "$JOKER_USERNAME"
+ _saveaccountconf_mutable JOKER_PASSWORD "$JOKER_PASSWORD"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _info "Adding TXT record"
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value=$txtvalue"; then
+ if _startswith "$response" "OK"; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
+ _err "Error adding TXT record."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_joker_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ JOKER_USERNAME="${JOKER_USERNAME:-$(_readaccountconf_mutable JOKER_USERNAME)}"
+ JOKER_PASSWORD="${JOKER_PASSWORD:-$(_readaccountconf_mutable JOKER_PASSWORD)}"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _info "Removing TXT record"
+ # TXT record is removed by setting its value to empty.
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$_domain&label=$_sub_domain&type=TXT&value="; then
+ if _startswith "$response" "OK"; then
+ _info "Removed, OK"
+ return 0
+ fi
+ fi
+ _err "Error removing TXT record."
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=1
+ while true; do
+ h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ # Try to remove a test record. With correct root domain, username and password this will return "OK: ..." regardless
+ # of record in question existing or not.
+ if _joker_rest "username=$JOKER_USERNAME&password=$JOKER_PASSWORD&zone=$h&label=jokerTXTUpdateTest&type=TXT&value="; then
+ if _startswith "$response" "OK"; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
+ _domain=$h
+ return 0
+ fi
+ fi
+
+ i=$(_math "$i" + 1)
+ done
+
+ _debug "Root domain not found"
+ return 1
+}
+
+_joker_rest() {
+ data="$1"
+ _debug data "$data"
+
+ if ! response="$(_post "$data" "$JOKER_API" "" "POST")"; then
+ _err "Error POSTing"
+ return 1
+ fi
+ _debug response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_kappernet.sh b/acme.sh-master/dnsapi/dns_kappernet.sh
new file mode 100644
index 0000000..83a7e5f
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_kappernet.sh
@@ -0,0 +1,150 @@
+#!/usr/bin/env sh
+
+# kapper.net domain api
+# for further questions please contact: support@kapper.net
+# please report issues here: https://github.com/acmesh-official/acme.sh/issues/2977
+
+#KAPPERNETDNS_Key="yourKAPPERNETapikey"
+#KAPPERNETDNS_Secret="yourKAPPERNETapisecret"
+
+KAPPERNETDNS_Api="https://dnspanel.kapper.net/API/1.2?APIKey=$KAPPERNETDNS_Key&APISecret=$KAPPERNETDNS_Secret"
+
+###############################################################################
+# called with
+# fullhostname: something.example.com
+# txtvalue: someacmegenerated string
+dns_kappernet_add() {
+ fullhostname=$1
+ txtvalue=$2
+
+ KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
+ KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+
+ if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
+ KAPPERNETDNS_Key=""
+ KAPPERNETDNS_Secret=""
+ _err "Please specify your kapper.net api key and secret."
+ _err "If you have not received yours - send your mail to"
+ _err "support@kapper.net to get your key and secret."
+ return 1
+ fi
+
+ #store the api key and email to the account conf file.
+ _saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key"
+ _saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
+ _debug "Checking Domain ..."
+ if ! _get_root "$fullhostname"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _sub_domain "SUBDOMAIN: $_sub_domain"
+ _debug _domain "DOMAIN: $_domain"
+
+ _info "Trying to add TXT DNS Record"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ if _kappernet_api GET "action=new&subject=$_domain&data=$data"; then
+
+ if _contains "$response" "{\"OK\":true"; then
+ _info "Waiting 120 seconds for DNS to spread the new record"
+ _sleep 120
+ return 0
+ else
+ _err "Error creating a TXT DNS Record: $fullhostname TXT $txtvalue"
+ _err "Error Message: $response"
+ return 1
+ fi
+ fi
+ _err "Failed creating TXT Record"
+}
+
+###############################################################################
+# called with
+# fullhostname: something.example.com
+dns_kappernet_rm() {
+ fullhostname=$1
+ txtvalue=$2
+
+ KAPPERNETDNS_Key="${KAPPERNETDNS_Key:-$(_readaccountconf_mutable KAPPERNETDNS_Key)}"
+ KAPPERNETDNS_Secret="${KAPPERNETDNS_Secret:-$(_readaccountconf_mutable KAPPERNETDNS_Secret)}"
+
+ if [ -z "$KAPPERNETDNS_Key" ] || [ -z "$KAPPERNETDNS_Secret" ]; then
+ KAPPERNETDNS_Key=""
+ KAPPERNETDNS_Secret=""
+ _err "Please specify your kapper.net api key and secret."
+ _err "If you have not received yours - send your mail to"
+ _err "support@kapper.net to get your key and secret."
+ return 1
+ fi
+
+ #store the api key and email to the account conf file.
+ _saveaccountconf_mutable KAPPERNETDNS_Key "$KAPPERNETDNS_Key"
+ _saveaccountconf_mutable KAPPERNETDNS_Secret "$KAPPERNETDNS_Secret"
+
+ _info "Trying to remove the TXT Record: $fullhostname containing $txtvalue"
+ data="%7B%22name%22%3A%22$fullhostname%22%2C%22type%22%3A%22TXT%22%2C%22content%22%3A%22$txtvalue%22%2C%22ttl%22%3A%223600%22%2C%22prio%22%3A%22%22%7D"
+ if _kappernet_api GET "action=del&subject=$fullhostname&data=$data"; then
+ if _contains "$response" "{\"OK\":true"; then
+ return 0
+ else
+ _err "Error deleting DNS Record: $fullhostname containing $txtvalue"
+ _err "Problem: $response"
+ return 1
+ fi
+ fi
+ _err "Problem deleting TXT DNS record"
+}
+
+#################### Private functions below ##################################
+# called with hostname
+# e.g._acme-challenge.www.domain.com returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+ if ! _kappernet_api GET "action=list&subject=$h"; then
+ return 1
+ fi
+ if _contains "$response" '"OK":false'; then
+ _debug "$h not found"
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+################################################################################
+# calls the kapper.net DNS Panel API
+# with
+# method
+# param
+_kappernet_api() {
+ method=$1
+ param="$2"
+
+ _debug param "PARAMETER=$param"
+ url="$KAPPERNETDNS_Api&$param"
+ _debug url "URL=$url"
+
+ if [ "$method" = "GET" ]; then
+ response="$(_get "$url")"
+ else
+ _err "Unsupported method"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_kas.sh b/acme.sh-master/dnsapi/dns_kas.sh
new file mode 100644
index 0000000..053abd2
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_kas.sh
@@ -0,0 +1,281 @@
+#!/usr/bin/env sh
+########################################################################
+# All-inkl Kasserver hook script for acme.sh
+#
+# Environment variables:
+#
+# - $KAS_Login (Kasserver API login name)
+# - $KAS_Authtype (Kasserver API auth type. Default: plain)
+# - $KAS_Authdata (Kasserver API auth data.)
+#
+# Last update: squared GmbH
+# Credits:
+# - dns_he.sh. Thanks a lot man!
+# - Martin Kammerlander, Phlegx Systems OG
+# - Marc-Oliver Lange
+# - https://github.com/o1oo11oo/kasapi.sh
+########################################################################
+KAS_Api_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasApi.wsdl")"
+KAS_Api="$(echo "$KAS_Api_GET" | tr -d ' ' | grep -i "//g")"
+_info "[KAS] -> API URL $KAS_Api"
+
+KAS_Auth_GET="$(_get "https://kasapi.kasserver.com/soap/wsdl/KasAuth.wsdl")"
+KAS_Auth="$(echo "$KAS_Auth_GET" | tr -d ' ' | grep -i "//g")"
+_info "[KAS] -> AUTH URL $KAS_Auth"
+
+KAS_default_ratelimit=5 # TODO - Every response delivers a ratelimit (seconds) where KASAPI is blocking a request.
+
+######## Public functions #####################
+dns_kas_add() {
+ _fulldomain=$1
+ _txtvalue=$2
+
+ _info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
+ _info "[KAS] -> Check and Save Props"
+ _check_and_save
+
+ _info "[KAS] -> Adding $_fulldomain DNS TXT entry on all-inkl.com/Kasserver"
+ _info "[KAS] -> Retriving Credential Token"
+ _get_credential_token
+
+ _info "[KAS] -> Checking Zone and Record_Name"
+ _get_zone_and_record_name "$_fulldomain"
+
+ _info "[KAS] -> Checking for existing Record entries"
+ _get_record_id
+
+ # If there is a record_id, delete the entry
+ if [ -n "$_record_id" ]; then
+ _info "[KAS] -> Existing records found. Now deleting old entries"
+ for i in $_record_id; do
+ _delete_RecordByID "$i"
+ done
+ else
+ _info "[KAS] -> No record found."
+ fi
+
+ _info "[KAS] -> Creating TXT DNS record"
+ action="add_dns_settings"
+ kasReqParam="\"record_name\":\"$_record_name\""
+ kasReqParam="$kasReqParam,\"record_type\":\"TXT\""
+ kasReqParam="$kasReqParam,\"record_data\":\"$_txtvalue\""
+ kasReqParam="$kasReqParam,\"record_aux\":\"0\""
+ kasReqParam="$kasReqParam,\"zone_host\":\"$_zone\""
+ response="$(_callAPI "$action" "$kasReqParam")"
+ _debug2 "[KAS] -> Response" "$response"
+
+ if [ -z "$response" ]; then
+ _info "[KAS] -> Response was empty, please check manually."
+ return 1
+ elif _contains "$response" ""; then
+ faultstring="$(echo "$response" | tr -d '\n\r' | sed "s//\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
+ case "${faultstring}" in
+ "record_already_exists")
+ _info "[KAS] -> The record already exists, which must not be a problem. Please check manually."
+ ;;
+ *)
+ _err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
+ return 1
+ ;;
+ esac
+ elif ! _contains "$response" "- ReturnStringTRUE
"; then
+ _err "[KAS] -> An unknown error occurred, please check manually."
+ return 1
+ fi
+ return 0
+}
+
+dns_kas_rm() {
+ _fulldomain=$1
+ _txtvalue=$2
+
+ _info "[KAS] -> Using DNS-01 All-inkl/Kasserver hook"
+ _info "[KAS] -> Check and Save Props"
+ _check_and_save
+
+ _info "[KAS] -> Cleaning up after All-inkl/Kasserver hook"
+ _info "[KAS] -> Removing $_fulldomain DNS TXT entry on All-inkl/Kasserver"
+ _info "[KAS] -> Retriving Credential Token"
+ _get_credential_token
+
+ _info "[KAS] -> Checking Zone and Record_Name"
+ _get_zone_and_record_name "$_fulldomain"
+
+ _info "[KAS] -> Getting Record ID"
+ _get_record_id
+
+ _info "[KAS] -> Removing entries with ID: $_record_id"
+ # If there is a record_id, delete the entry
+ if [ -n "$_record_id" ]; then
+ for i in $_record_id; do
+ _delete_RecordByID "$i"
+ done
+ else # Cannot delete or unkown error
+ _info "[KAS] -> No record_id found that can be deleted. Please check manually."
+ fi
+ return 0
+}
+
+########################## PRIVATE FUNCTIONS ###########################
+# Delete Record ID
+_delete_RecordByID() {
+ recId=$1
+ action="delete_dns_settings"
+ kasReqParam="\"record_id\":\"$recId\""
+ response="$(_callAPI "$action" "$kasReqParam")"
+ _debug2 "[KAS] -> Response" "$response"
+
+ if [ -z "$response" ]; then
+ _info "[KAS] -> Response was empty, please check manually."
+ return 1
+ elif _contains "$response" ""; then
+ faultstring="$(echo "$response" | tr -d '\n\r' | sed "s//\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
+ case "${faultstring}" in
+ "record_id_not_found")
+ _info "[KAS] -> The record was not found, which perhaps is not a problem. Please check manually."
+ ;;
+ *)
+ _err "[KAS] -> An error =>$faultstring<= occurred, please check manually."
+ return 1
+ ;;
+ esac
+ elif ! _contains "$response" "- ReturnStringTRUE
"; then
+ _err "[KAS] -> An unknown error occurred, please check manually."
+ return 1
+ fi
+}
+# Checks for the ENV variables and saves them
+_check_and_save() {
+ KAS_Login="${KAS_Login:-$(_readaccountconf_mutable KAS_Login)}"
+ KAS_Authtype="${KAS_Authtype:-$(_readaccountconf_mutable KAS_Authtype)}"
+ KAS_Authdata="${KAS_Authdata:-$(_readaccountconf_mutable KAS_Authdata)}"
+
+ if [ -z "$KAS_Login" ] || [ -z "$KAS_Authtype" ] || [ -z "$KAS_Authdata" ]; then
+ KAS_Login=
+ KAS_Authtype=
+ KAS_Authdata=
+ _err "[KAS] -> No auth details provided. Please set user credentials using the \$KAS_Login, \$KAS_Authtype, and \$KAS_Authdata environment variables."
+ return 1
+ fi
+ _saveaccountconf_mutable KAS_Login "$KAS_Login"
+ _saveaccountconf_mutable KAS_Authtype "$KAS_Authtype"
+ _saveaccountconf_mutable KAS_Authdata "$KAS_Authdata"
+ return 0
+}
+
+# Gets back the base domain/zone and record name.
+# See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
+_get_zone_and_record_name() {
+ action="get_domains"
+ response="$(_callAPI "$action")"
+ _debug2 "[KAS] -> Response" "$response"
+
+ if [ -z "$response" ]; then
+ _info "[KAS] -> Response was empty, please check manually."
+ return 1
+ elif _contains "$response" ""; then
+ faultstring="$(echo "$response" | tr -d '\n\r' | sed "s//\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
+ _err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
+ return 1
+ fi
+
+ zonen="$(echo "$response" | sed 's/- /\n/g' | sed -r 's/(.*domain_name<\/key>)(.*)(<\/value.*)/\2/' | sed '/^ Zone:" "$_zone"
+ _debug "[KAS] -> Domain:" "$domain"
+ _debug "[KAS] -> Record_Name:" "$_record_name"
+ return 0
+}
+
+# Retrieve the DNS record ID
+_get_record_id() {
+ action="get_dns_settings"
+ kasReqParam="\"zone_host\":\"$_zone\""
+ response="$(_callAPI "$action" "$kasReqParam")"
+ _debug2 "[KAS] -> Response" "$response"
+
+ if [ -z "$response" ]; then
+ _info "[KAS] -> Response was empty, please check manually."
+ return 1
+ elif _contains "$response" ""; then
+ faultstring="$(echo "$response" | tr -d '\n\r' | sed "s//\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
+ _err "[KAS] -> Either no domains were found or another error =>$faultstring<= occurred, please check manually."
+ return 1
+ fi
+
+ _record_id="$(echo "$response" | tr -d '\n\r' | sed "s/
- /\n/g" | grep -i "$_record_name" | grep -i ">TXT<" | sed "s/
- record_id<\/key>/=>/g" | sed "s/<\/value><\/item>/\n/g" | grep "=>" | sed "s/=>//g")"
+ _debug "[KAS] -> Record Id: " "$_record_id"
+ return 0
+}
+
+# Retrieve credential token
+_get_credential_token() {
+ baseParamAuth="\"kas_login\":\"$KAS_Login\""
+ baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"$KAS_Authtype\""
+ baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$KAS_Authdata\""
+ baseParamAuth="$baseParamAuth,\"session_lifetime\":600"
+ baseParamAuth="$baseParamAuth,\"session_update_lifetime\":\"Y\""
+
+ data='{'
+ data="$data$baseParamAuth}"
+
+ _debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
+ _sleep $KAS_default_ratelimit
+
+ contentType="text/xml"
+ export _H1="SOAPAction: urn:xmethodsKasApiAuthentication#KasAuth"
+ response="$(_post "$data" "$KAS_Auth" "" "POST" "$contentType")"
+ _debug2 "[KAS] -> Response" "$response"
+
+ if [ -z "$response" ]; then
+ _info "[KAS] -> Response was empty, please check manually."
+ return 1
+ elif _contains "$response" ""; then
+ faultstring="$(echo "$response" | tr -d '\n\r' | sed "s//\n=> /g" | sed "s/<\/faultstring>/\n/g" | grep "=>" | sed "s/=> //g")"
+ _err "[KAS] -> Could not retrieve login token or antoher error =>$faultstring<= occurred, please check manually."
+ return 1
+ fi
+
+ _credential_token="$(echo "$response" | tr '\n' ' ' | sed 's/.*return xsi:type="xsd:string">\(.*\)<\/return>/\1/' | sed 's/<\/ns1:KasAuthResponse\(.*\)Envelope>.*//')"
+ _debug "[KAS] -> Credential Token: " "$_credential_token"
+ return 0
+}
+
+_callAPI() {
+ kasaction=$1
+ kasReqParams=$2
+
+ baseParamAuth="\"kas_login\":\"$KAS_Login\""
+ baseParamAuth="$baseParamAuth,\"kas_auth_type\":\"session\""
+ baseParamAuth="$baseParamAuth,\"kas_auth_data\":\"$_credential_token\""
+
+ data='{'
+ data="$data$baseParamAuth,\"kas_action\":\"$kasaction\""
+ if [ -n "$kasReqParams" ]; then
+ data="$data,\"KasRequestParams\":{$kasReqParams}"
+ fi
+ data="$data}"
+
+ _debug2 "[KAS] -> Request" "$data"
+
+ _debug "[KAS] -> Be friendly and wait $KAS_default_ratelimit seconds by default before calling KAS API."
+ _sleep $KAS_default_ratelimit
+
+ contentType="text/xml"
+ export _H1="SOAPAction: urn:xmethodsKasApi#KasApi"
+ response="$(_post "$data" "$KAS_Api" "" "POST" "$contentType")"
+ _debug2 "[KAS] -> Response" "$response"
+ echo "$response"
+}
diff --git a/acme.sh-master/dnsapi/dns_kinghost.sh b/acme.sh-master/dnsapi/dns_kinghost.sh
new file mode 100644
index 0000000..f640242
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_kinghost.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env sh
+
+############################################################
+# KingHost API support #
+# https://api.kinghost.net/doc/ #
+# #
+# Author: Felipe Keller Braz #
+# Report Bugs here: https://github.com/kinghost/acme.sh #
+# #
+# Values to export: #
+# export KINGHOST_Username="email@provider.com" #
+# export KINGHOST_Password="xxxxxxxxxx" #
+############################################################
+
+KING_Api="https://api.kinghost.net/acme"
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_kinghost_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}"
+ KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}"
+ if [ -z "$KINGHOST_Username" ] || [ -z "$KINGHOST_Password" ]; then
+ KINGHOST_Username=""
+ KINGHOST_Password=""
+ _err "You don't specify KingHost api password and email yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable KINGHOST_Username "$KINGHOST_Username"
+ _saveaccountconf_mutable KINGHOST_Password "$KINGHOST_Password"
+
+ _debug "Getting txt records"
+ _kinghost_rest GET "dns" "name=$fulldomain&content=$txtvalue"
+
+ #This API call returns "status":"ok" if dns record does not exist
+ #We are creating a new txt record here, so we expect the "ok" status
+ if ! echo "$response" | grep '"status":"ok"' >/dev/null; then
+ _err "Error"
+ _err "$response"
+ return 1
+ fi
+
+ _kinghost_rest POST "dns" "name=$fulldomain&content=$txtvalue"
+ if ! echo "$response" | grep '"status":"ok"' >/dev/null; then
+ _err "Error"
+ _err "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+# Usage: fulldomain txtvalue
+# Used to remove the txt record after validation
+dns_kinghost_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ KINGHOST_Password="${KINGHOST_Password:-$(_readaccountconf_mutable KINGHOST_Password)}"
+ KINGHOST_Username="${KINGHOST_Username:-$(_readaccountconf_mutable KINGHOST_Username)}"
+ if [ -z "$KINGHOST_Password" ] || [ -z "$KINGHOST_Username" ]; then
+ KINGHOST_Password=""
+ KINGHOST_Username=""
+ _err "You don't specify KingHost api key and email yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _kinghost_rest DELETE "dns" "name=$fulldomain&content=$txtvalue"
+ if ! echo "$response" | grep '"status":"ok"' >/dev/null; then
+ _err "Error"
+ _err "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+_kinghost_rest() {
+ method=$1
+ uri="$2"
+ data="$3"
+ _debug "$uri"
+
+ export _H1="X-Auth-Email: $KINGHOST_Username"
+ export _H2="X-Auth-Key: $KINGHOST_Password"
+
+ if [ "$method" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$KING_Api/$uri.json" "" "$method")"
+ else
+ response="$(_get "$KING_Api/$uri.json?$data")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $uri"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_knot.sh b/acme.sh-master/dnsapi/dns_knot.sh
new file mode 100644
index 0000000..729a89c
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_knot.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env sh
+
+######## Public functions #####################
+
+#Usage: dns_knot_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_knot_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _checkKey || return 1
+ [ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost"
+ # save the dns server and key to the account.conf file.
+ _saveaccountconf KNOT_SERVER "${KNOT_SERVER}"
+ _saveaccountconf KNOT_KEY "${KNOT_KEY}"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+
+ _info "Adding ${fulldomain}. 60 TXT \"${txtvalue}\""
+
+ knsupdate <
+#Utilize leaseweb.com API to finish dns-01 verifications.
+#Requires a Leaseweb API Key (export LSW_Key="Your Key")
+#See https://developer.leaseweb.com for more information.
+######## Public functions #####################
+
+LSW_API="https://api.leaseweb.com/hosting/v2/domains/"
+
+#Usage: dns_leaseweb_add _acme-challenge.www.domain.com
+dns_leaseweb_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}"
+ if [ -z "$LSW_Key" ]; then
+ LSW_Key=""
+ _err "You don't specify Leaseweb api key yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable LSW_Key "$LSW_Key"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _root_domain "$_domain"
+ _debug _domain "$fulldomain"
+
+ if _lsw_api "POST" "$_domain" "$fulldomain" "$txtvalue"; then
+ if [ "$_code" = "201" ]; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error, invalid code. Code: $_code"
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_leaseweb_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _root_domain "$_domain"
+ _debug _domain "$fulldomain"
+
+ if _lsw_api "DELETE" "$_domain" "$fulldomain" "$txtvalue"; then
+ if [ "$_code" = "204" ]; then
+ _info "Deleted, OK"
+ return 0
+ else
+ _err "Delete txt record error."
+ return 1
+ fi
+ fi
+ _err "Delete txt record error."
+
+ return 1
+}
+
+#################### Private functions below ##################################
+# _acme-challenge.www.domain.com
+# returns
+# _domain=domain.com
+_get_root() {
+ rdomain=$1
+ i="$(echo "$rdomain" | tr '.' ' ' | wc -w)"
+ i=$(_math "$i" - 1)
+
+ while true; do
+ h=$(printf "%s" "$rdomain" | cut -d . -f "$i"-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1 #not valid domain
+ fi
+
+ #Check API if domain exists
+ if _lsw_api "GET" "$h"; then
+ if [ "$_code" = "200" ]; then
+ _domain="$h"
+ return 0
+ fi
+ fi
+ i=$(_math "$i" - 1)
+ if [ "$i" -lt 2 ]; then
+ return 1 #not found, no need to check _acme-challenge.sub.domain in leaseweb api.
+ fi
+ done
+
+ return 1
+}
+
+_lsw_api() {
+ cmd=$1
+ d=$2
+ fd=$3
+ tvalue=$4
+
+ # Construct the HTTP Authorization header
+ export _H2="Content-Type: application/json"
+ export _H1="X-Lsw-Auth: ${LSW_Key}"
+
+ if [ "$cmd" = "GET" ]; then
+ response="$(_get "$LSW_API/$d")"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+ _debug response "$response"
+ return 0
+ fi
+
+ if [ "$cmd" = "POST" ]; then
+ data="{\"name\": \"$fd.\",\"type\": \"TXT\",\"content\": [\"$tvalue\"],\"ttl\": 60}"
+ response="$(_post "$data" "$LSW_API/$d/resourceRecordSets" "$data" "POST")"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+ _debug response "$response"
+ return 0
+ fi
+
+ if [ "$cmd" = "DELETE" ]; then
+ response="$(_post "" "$LSW_API/$d/resourceRecordSets/$fd/TXT" "" "DELETE")"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+ _debug response "$response"
+ return 0
+ fi
+
+ return 1
+}
diff --git a/acme.sh-master/dnsapi/dns_lexicon.sh b/acme.sh-master/dnsapi/dns_lexicon.sh
new file mode 100644
index 0000000..1970234
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_lexicon.sh
@@ -0,0 +1,113 @@
+#!/usr/bin/env sh
+
+# dns api wrapper of lexicon for acme.sh
+
+# https://github.com/AnalogJ/lexicon
+lexicon_cmd="lexicon"
+
+wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-lexicon-dns-api"
+
+_lexicon_init() {
+ if ! _exists "$lexicon_cmd"; then
+ _err "Please install $lexicon_cmd first: $wiki"
+ return 1
+ fi
+
+ PROVIDER="${PROVIDER:-$(_readdomainconf PROVIDER)}"
+ if [ -z "$PROVIDER" ]; then
+ PROVIDER=""
+ _err "Please define env PROVIDER first: $wiki"
+ return 1
+ fi
+
+ _savedomainconf PROVIDER "$PROVIDER"
+ export PROVIDER
+
+ # e.g. busybox-ash does not know [:upper:]
+ # shellcheck disable=SC2018,SC2019
+ Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
+ eval "$Lx_name=\${$Lx_name:-$(_readaccountconf_mutable "$Lx_name")}"
+ Lx_name_v=$(eval echo \$"$Lx_name")
+ _secure_debug "$Lx_name" "$Lx_name_v"
+ if [ "$Lx_name_v" ]; then
+ _saveaccountconf_mutable "$Lx_name" "$Lx_name_v"
+ eval export "$Lx_name"
+ fi
+
+ # shellcheck disable=SC2018,SC2019
+ Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
+ eval "$Lx_token=\${$Lx_token:-$(_readaccountconf_mutable "$Lx_token")}"
+ Lx_token_v=$(eval echo \$"$Lx_token")
+ _secure_debug "$Lx_token" "$Lx_token_v"
+ if [ "$Lx_token_v" ]; then
+ _saveaccountconf_mutable "$Lx_token" "$Lx_token_v"
+ eval export "$Lx_token"
+ fi
+
+ # shellcheck disable=SC2018,SC2019
+ Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
+ eval "$Lx_password=\${$Lx_password:-$(_readaccountconf_mutable "$Lx_password")}"
+ Lx_password_v=$(eval echo \$"$Lx_password")
+ _secure_debug "$Lx_password" "$Lx_password_v"
+ if [ "$Lx_password_v" ]; then
+ _saveaccountconf_mutable "$Lx_password" "$Lx_password_v"
+ eval export "$Lx_password"
+ fi
+
+ # shellcheck disable=SC2018,SC2019
+ Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
+ eval "$Lx_domaintoken=\${$Lx_domaintoken:-$(_readaccountconf_mutable "$Lx_domaintoken")}"
+ Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
+ _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
+ if [ "$Lx_domaintoken_v" ]; then
+ _saveaccountconf_mutable "$Lx_domaintoken" "$Lx_domaintoken_v"
+ eval export "$Lx_domaintoken"
+ fi
+
+ # shellcheck disable=SC2018,SC2019
+ Lx_api_key=$(echo LEXICON_"${PROVIDER}"_API_KEY | tr 'a-z' 'A-Z')
+ eval "$Lx_api_key=\${$Lx_api_key:-$(_readaccountconf_mutable "$Lx_api_key")}"
+ Lx_api_key_v=$(eval echo \$"$Lx_api_key")
+ _secure_debug "$Lx_api_key" "$Lx_api_key_v"
+ if [ "$Lx_api_key_v" ]; then
+ _saveaccountconf_mutable "$Lx_api_key" "$Lx_api_key_v"
+ eval export "$Lx_api_key"
+ fi
+}
+
+######## Public functions #####################
+
+#Usage: dns_lexicon_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_lexicon_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _lexicon_init; then
+ return 1
+ fi
+
+ domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
+
+ _secure_debug LEXICON_OPTS "$LEXICON_OPTS"
+ _savedomainconf LEXICON_OPTS "$LEXICON_OPTS"
+
+ # shellcheck disable=SC2086
+ $lexicon_cmd "$PROVIDER" $LEXICON_OPTS create "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" --output QUIET
+
+}
+
+#Usage: dns_lexicon_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_lexicon_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _lexicon_init; then
+ return 1
+ fi
+
+ domain=$(printf "%s" "$fulldomain" | cut -d . -f 2-999)
+
+ # shellcheck disable=SC2086
+ $lexicon_cmd "$PROVIDER" $LEXICON_OPTS delete "${domain}" TXT --name="_acme-challenge.${domain}." --content="${txtvalue}" --output QUIET
+
+}
diff --git a/acme.sh-master/dnsapi/dns_linode.sh b/acme.sh-master/dnsapi/dns_linode.sh
new file mode 100644
index 0000000..ead5b16
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_linode.sh
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#Author: Philipp Grosswiler
+
+LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
+
+######## Public functions #####################
+
+#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
+
+ if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error adding the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully added."
+ return 0
+ fi
+
+ return 1
+}
+
+#Usage: dns_linode_rm _acme-challenge.www.domain.com
+dns_linode_rm() {
+ fulldomain="${1}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id"
+
+ if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+
+ resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
+ if [ "$resource" ]; then
+ _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_resource_id" ]; then
+ _debug _resource_id "$_resource_id"
+
+ _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
+
+ if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error deleting the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully deleted."
+ return 0
+ fi
+ fi
+
+ return 1
+ fi
+
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_Linode_API() {
+ if [ -z "$LINODE_API_KEY" ]; then
+ LINODE_API_KEY=""
+
+ _err "You didn't specify the Linode API key yet."
+ _err "Please create your key and try again."
+
+ return 1
+ fi
+
+ _saveaccountconf LINODE_API_KEY "$LINODE_API_KEY"
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _rest GET "domain.list"; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method method action data
+_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
+ else
+ response="$(_get "$LINODE_API_URL$ep$data")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_linode_v4.sh b/acme.sh-master/dnsapi/dns_linode_v4.sh
new file mode 100644
index 0000000..9504afb
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_linode_v4.sh
@@ -0,0 +1,187 @@
+#!/usr/bin/env sh
+
+#Original Author: Philipp Grosswiler
+#v4 Update Author: Aaron W. Swenson
+
+LINODE_V4_API_URL="https://api.linode.com/v4/domains"
+
+######## Public functions #####################
+
+#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_v4_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _payload="{
+ \"type\": \"TXT\",
+ \"name\": \"$_sub_domain\",
+ \"target\": \"$txtvalue\",
+ \"ttl_sec\": 300
+ }"
+
+ if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error adding the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully added."
+ return 0
+ fi
+
+ return 1
+}
+
+#Usage: dns_linode_rm _acme-challenge.www.domain.com
+dns_linode_v4_rm() {
+ fulldomain="${1}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if _rest GET "/$_domain_id/records" && [ -n "$response" ]; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+
+ resource="$(echo "$response" | _egrep_o "\{.*\"name\": *\"$_sub_domain\".*}")"
+ if [ "$resource" ]; then
+ _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_resource_id" ]; then
+ _debug _resource_id "$_resource_id"
+
+ if _rest DELETE "/$_domain_id/records/$_resource_id" && [ -n "$response" ]; then
+ # On 200/OK, empty set is returned. Check for error, if any.
+ _error_response=$(printf "%s\n" "$response" | _egrep_o "\"errors\"" | cut -d : -f 2 | tr -d " " | _head_n 1)
+
+ if [ -n "$_error_response" ]; then
+ _err "Error deleting the domain resource: $_error_response"
+ return 1
+ fi
+
+ _info "Domain resource successfully deleted."
+ return 0
+ fi
+ fi
+
+ return 1
+ fi
+
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_Linode_API() {
+ LINODE_V4_API_KEY="${LINODE_V4_API_KEY:-$(_readaccountconf_mutable LINODE_V4_API_KEY)}"
+ if [ -z "$LINODE_V4_API_KEY" ]; then
+ LINODE_V4_API_KEY=""
+
+ _err "You didn't specify the Linode v4 API key yet."
+ _err "Please create your key and try again."
+
+ return 1
+ fi
+
+ _saveaccountconf_mutable LINODE_V4_API_KEY "$LINODE_V4_API_KEY"
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _rest GET; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "\{.*\"domain\": *\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method method action data
+_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+ export _H3="Authorization: Bearer $LINODE_V4_API_KEY"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$LINODE_V4_API_URL$ep" "" "$mtd")"
+ else
+ response="$(_get "$LINODE_V4_API_URL$ep$data")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_loopia.sh b/acme.sh-master/dnsapi/dns_loopia.sh
new file mode 100644
index 0000000..399c786
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_loopia.sh
@@ -0,0 +1,326 @@
+#!/usr/bin/env sh
+
+#
+#LOOPIA_User="username"
+#
+#LOOPIA_Password="password"
+#
+#LOOPIA_Api="https://api.loopia./RPCSERV"
+
+LOOPIA_Api_Default="https://api.loopia.se/RPCSERV"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_loopia_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _loopia_load_config; then
+ return 1
+ fi
+
+ _loopia_save_config
+
+ _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"
+
+ _info "Adding record"
+
+ if ! _loopia_add_sub_domain "$_domain" "$_sub_domain"; then
+ return 1
+ fi
+ if ! _loopia_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
+ return 1
+ fi
+
+}
+
+dns_loopia_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _loopia_load_config; then
+ return 1
+ fi
+
+ _loopia_save_config
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ xml_content=$(printf '
+
+ removeSubdomain
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ ' "$LOOPIA_User" "$Encoded_Password" "$_domain" "$_sub_domain")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+
+ if ! _contains "$response" "OK"; then
+ err_response=$(echo "$response" | sed 's/.*\(.*\)<\/string>.*/\1/')
+ _err "Error could not get txt records: $err_response"
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+
+_loopia_load_config() {
+ LOOPIA_Api="${LOOPIA_Api:-$(_readaccountconf_mutable LOOPIA_Api)}"
+ LOOPIA_User="${LOOPIA_User:-$(_readaccountconf_mutable LOOPIA_User)}"
+ LOOPIA_Password="${LOOPIA_Password:-$(_readaccountconf_mutable LOOPIA_Password)}"
+
+ if [ -z "$LOOPIA_Api" ]; then
+ LOOPIA_Api="$LOOPIA_Api_Default"
+ fi
+
+ if [ -z "$LOOPIA_User" ] || [ -z "$LOOPIA_Password" ]; then
+ LOOPIA_User=""
+ LOOPIA_Password=""
+
+ _err "A valid Loopia API user and password not provided."
+ _err "Please provide a valid API user and try again."
+
+ return 1
+ fi
+
+ if _contains "$LOOPIA_Password" "'" || _contains "$LOOPIA_Password" '"'; then
+ _err "Password contains quoute or double quoute and this is not supported by dns_loopia.sh"
+ return 1
+ fi
+
+ Encoded_Password=$(_xml_encode "$LOOPIA_Password")
+ return 0
+}
+
+_loopia_save_config() {
+ if [ "$LOOPIA_Api" != "$LOOPIA_Api_Default" ]; then
+ _saveaccountconf_mutable LOOPIA_Api "$LOOPIA_Api"
+ fi
+ _saveaccountconf_mutable LOOPIA_User "$LOOPIA_User"
+ _saveaccountconf_mutable LOOPIA_Password "$LOOPIA_Password"
+}
+
+_loopia_get_records() {
+ domain=$1
+ sub_domain=$2
+
+ xml_content=$(printf '
+
+ getZoneRecords
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ ' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+ if ! _contains "$response" ""; then
+ err_response=$(echo "$response" | sed 's/.*\(.*\)<\/string>.*/\1/')
+ _err "Error: $err_response"
+ return 1
+ fi
+ return 0
+}
+
+_get_root() {
+ domain=$1
+ _debug "get root"
+
+ domain=$1
+ i=2
+ p=1
+
+ xml_content=$(printf '
+
+ getDomains
+
+
+ %s
+
+
+ %s
+
+
+ ' "$LOOPIA_User" "$Encoded_Password")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+ while true; do
+ h=$(echo "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "$h"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+
+}
+
+_loopia_add_record() {
+ domain=$1
+ sub_domain=$2
+ txtval=$3
+
+ xml_content=$(printf '
+
+ addZoneRecord
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+
+
+
+ type
+ TXT
+
+
+ priority
+ 0
+
+
+ ttl
+ 300
+
+
+ rdata
+ %s
+
+
+
+
+
+ ' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain" "$txtval")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+
+ if ! _contains "$response" "OK"; then
+ err_response=$(echo "$response" | sed 's/.*\(.*\)<\/string>.*/\1/')
+ _err "Error: $err_response"
+ return 1
+ fi
+ return 0
+}
+
+_sub_domain_exists() {
+ domain=$1
+ sub_domain=$2
+
+ xml_content=$(printf '
+
+ getSubdomains
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ ' "$LOOPIA_User" "$Encoded_Password" "$domain")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+
+ if _contains "$response" "$sub_domain"; then
+ return 0
+ fi
+ return 1
+}
+
+_loopia_add_sub_domain() {
+ domain=$1
+ sub_domain=$2
+
+ if _sub_domain_exists "$domain" "$sub_domain"; then
+ return 0
+ fi
+
+ xml_content=$(printf '
+
+ addSubdomain
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ %s
+
+
+ ' "$LOOPIA_User" "$Encoded_Password" "$domain" "$sub_domain")
+
+ response="$(_post "$xml_content" "$LOOPIA_Api" "" "POST")"
+
+ if ! _contains "$response" "OK"; then
+ err_response=$(echo "$response" | sed 's/.*\(.*\)<\/string>.*/\1/')
+ _err "Error: $err_response"
+ return 1
+ fi
+ return 0
+}
+
+_xml_encode() {
+ encoded_string=$1
+ encoded_string=$(echo "$encoded_string" | sed 's/&/\&/')
+ encoded_string=$(echo "$encoded_string" | sed 's/\</')
+ encoded_string=$(echo "$encoded_string" | sed 's/>/\>/')
+ printf "%s" "$encoded_string"
+}
diff --git a/acme.sh-master/dnsapi/dns_lua.sh b/acme.sh-master/dnsapi/dns_lua.sh
new file mode 100644
index 0000000..30c1557
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_lua.sh
@@ -0,0 +1,154 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+#
+#LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#LUA_Email="user@luadns.net"
+
+LUA_Api="https://api.luadns.com/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_lua_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+ LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
+ LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
+
+ if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
+ LUA_Key=""
+ LUA_Email=""
+ _err "You don't specify luadns api key and email yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable LUA_Key "$LUA_Key"
+ _saveaccountconf_mutable LUA_Email "$LUA_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"
+
+ _info "Adding record"
+ if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+ if _contains "$response" "$fulldomain"; then
+ _info "Added"
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+}
+
+#fulldomain
+dns_lua_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+ LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
+ LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
+ _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"
+ _LUA_rest GET "zones/${_domain_id}/records"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _LUA_rest DELETE "/zones/$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" "$record_id"
+ 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=2
+ p=1
+ if ! _LUA_rest GET "zones"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
+ _debug _domain_id "$_domain_id"
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_LUA_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Authorization: Basic $LUA_auth"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$LUA_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_maradns.sh b/acme.sh-master/dnsapi/dns_maradns.sh
new file mode 100644
index 0000000..4ff6ca2
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_maradns.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env sh
+
+#Usage: dns_maradns_add _acme-challenge.www.domain.com "token"
+dns_maradns_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ MARA_ZONE_FILE="${MARA_ZONE_FILE:-$(_readaccountconf_mutable MARA_ZONE_FILE)}"
+ MARA_DUENDE_PID_PATH="${MARA_DUENDE_PID_PATH:-$(_readaccountconf_mutable MARA_DUENDE_PID_PATH)}"
+
+ _check_zone_file "$MARA_ZONE_FILE" || return 1
+ _check_duende_pid_path "$MARA_DUENDE_PID_PATH" || return 1
+
+ _saveaccountconf_mutable MARA_ZONE_FILE "$MARA_ZONE_FILE"
+ _saveaccountconf_mutable MARA_DUENDE_PID_PATH "$MARA_DUENDE_PID_PATH"
+
+ printf "%s. TXT '%s' ~\n" "$fulldomain" "$txtvalue" >>"$MARA_ZONE_FILE"
+ _reload_maradns "$MARA_DUENDE_PID_PATH" || return 1
+}
+
+#Usage: dns_maradns_rm _acme-challenge.www.domain.com "token"
+dns_maradns_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ MARA_ZONE_FILE="${MARA_ZONE_FILE:-$(_readaccountconf_mutable MARA_ZONE_FILE)}"
+ MARA_DUENDE_PID_PATH="${MARA_DUENDE_PID_PATH:-$(_readaccountconf_mutable MARA_DUENDE_PID_PATH)}"
+
+ _check_zone_file "$MARA_ZONE_FILE" || return 1
+ _check_duende_pid_path "$MARA_DUENDE_PID_PATH" || return 1
+
+ _saveaccountconf_mutable MARA_ZONE_FILE "$MARA_ZONE_FILE"
+ _saveaccountconf_mutable MARA_DUENDE_PID_PATH "$MARA_DUENDE_PID_PATH"
+
+ _sed_i "/^$fulldomain.\+TXT '$txtvalue' ~/d" "$MARA_ZONE_FILE"
+ _reload_maradns "$MARA_DUENDE_PID_PATH" || return 1
+}
+
+_check_zone_file() {
+ zonefile="$1"
+ if [ -z "$zonefile" ]; then
+ _err "MARA_ZONE_FILE not passed!"
+ return 1
+ elif [ ! -w "$zonefile" ]; then
+ _err "MARA_ZONE_FILE not writable: $zonefile"
+ return 1
+ fi
+}
+
+_check_duende_pid_path() {
+ pidpath="$1"
+ if [ -z "$pidpath" ]; then
+ _err "MARA_DUENDE_PID_PATH not passed!"
+ return 1
+ fi
+ if [ ! -r "$pidpath" ]; then
+ _err "MARA_DUENDE_PID_PATH not readable: $pidpath"
+ return 1
+ fi
+}
+
+_reload_maradns() {
+ pidpath="$1"
+ kill -s HUP -- "$(cat "$pidpath")"
+ if [ $? -ne 0 ]; then
+ _err "Unable to reload MaraDNS, kill returned $?"
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_me.sh b/acme.sh-master/dnsapi/dns_me.sh
new file mode 100644
index 0000000..4900740
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_me.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+# ME_Key=qmlkdjflmkqdjf
+# ME_Secret=qmsdlkqmlksdvnnpae
+
+ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_me_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ]; then
+ ME_Key=""
+ ME_Secret=""
+ _err "You didn't specify DNSMadeEasy api key and secret yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf ME_Key "$ME_Key"
+ _saveaccountconf ME_Secret "$ME_Secret"
+
+ _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"
+ _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
+
+ if ! _contains "$response" "\"totalRecords\":"; then
+ _err "Error"
+ return 1
+ fi
+
+ _info "Adding record"
+ if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then
+ if printf -- "%s" "$response" | grep \"id\": >/dev/null; then
+ _info "Added"
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+
+}
+
+#fulldomain
+dns_me_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _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"
+ _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o ",\"value\":\"..$txtvalue..\",\"id\":[^,]*" | cut -d : -f 3 | head -n 1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _me_rest DELETE "$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" ''
+ 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=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _me_rest GET "name?domainname=$h"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(printf "%s\n" "$response" | sed 's/^{//; s/}$//; s/{.*}//' | sed -r 's/^.*"id":([0-9]+).*$/\1/')
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_me_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ cdate=$(LANG=C date -u +"%a, %d %b %Y %T %Z")
+ hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
+
+ export _H1="x-dnsme-apiKey: $ME_Key"
+ export _H2="x-dnsme-requestDate: $cdate"
+ export _H3="x-dnsme-hmac: $hmac"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$ME_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_miab.sh b/acme.sh-master/dnsapi/dns_miab.sh
new file mode 100644
index 0000000..dad69bd
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_miab.sh
@@ -0,0 +1,211 @@
+#!/usr/bin/env sh
+
+# Name: dns_miab.sh
+#
+# Authors:
+# Darven Dissek 2018
+# William Gertz 2019
+#
+# Thanks to Neil Pang and other developers here for code reused from acme.sh from DNS-01
+# used to communicate with the MailinaBox Custom DNS API
+# Report Bugs here:
+# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh)
+# https://github.com/acmesh-official/acme.sh (for acme.sh)
+#
+######## Public functions #####################
+
+#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_miab_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using miab challange add"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ #retrieve MIAB environemt vars
+ if ! _retrieve_miab_env; then
+ return 1
+ fi
+
+ #check domain and seperate into doamin and host
+ if ! _get_root "$fulldomain"; then
+ _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
+ return 1
+ fi
+
+ _debug2 _sub_domain "$_sub_domain"
+ _debug2 _domain "$_domain"
+
+ #add the challenge record
+ _api_path="custom/${fulldomain}/txt"
+ _miab_rest "$txtvalue" "$_api_path" "POST"
+
+ #check if result was good
+ if _contains "$response" "updated DNS"; then
+ _info "Successfully created the txt record"
+ return 0
+ else
+ _err "Error encountered during record add"
+ _err "$response"
+ return 1
+ fi
+}
+
+#Usage: dns_miab_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_miab_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using miab challage delete"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ #retrieve MIAB environemt vars
+ if ! _retrieve_miab_env; then
+ return 1
+ fi
+
+ #check domain and seperate into doamin and host
+ if ! _get_root "$fulldomain"; then
+ _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}"
+ return 1
+ fi
+
+ _debug2 _sub_domain "$_sub_domain"
+ _debug2 _domain "$_domain"
+
+ #Remove the challenge record
+ _api_path="custom/${fulldomain}/txt"
+ _miab_rest "$txtvalue" "$_api_path" "DELETE"
+
+ #check if result was good
+ if _contains "$response" "updated DNS"; then
+ _info "Successfully removed the txt record"
+ return 0
+ else
+ _err "Error encountered during record remove"
+ _err "$response"
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+#
+#Usage: _get_root _acme-challenge.www.domain.com
+#Returns:
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ _passed_domain=$1
+ _debug _passed_domain "$_passed_domain"
+ _i=2
+ _p=1
+
+ #get the zones hosed on MIAB server, must be a json stream
+ _miab_rest "" "zones" "GET"
+
+ if ! _is_json "$response"; then
+ _err "ERROR fetching domain list"
+ _err "$response"
+ return 1
+ fi
+
+ #cycle through the passed domain seperating out a test domain discarding
+ # the subdomain by marching thorugh the dots
+ while true; do
+ _test_domain=$(printf "%s" "$_passed_domain" | cut -d . -f ${_i}-100)
+ _debug _test_domain "$_test_domain"
+
+ if [ -z "$_test_domain" ]; then
+ return 1
+ fi
+
+ #report found if the test domain is in the json response and
+ # report the subdomain
+ if _contains "$response" "\"$_test_domain\""; then
+ _sub_domain=$(printf "%s" "$_passed_domain" | cut -d . -f 1-${_p})
+ _domain=${_test_domain}
+ return 0
+ fi
+
+ #cycle to the next dot in the passed domain
+ _p=${_i}
+ _i=$(_math "$_i" + 1)
+ done
+
+ return 1
+}
+
+#Usage: _retrieve_miab_env
+#Returns (from store or environment variables):
+# MIAB_Username
+# MIAB_Password
+# MIAB_Server
+#retrieve MIAB environment variables, report errors and quit if problems
+_retrieve_miab_env() {
+ MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}"
+ MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}"
+ MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}"
+
+ #debug log the environmental variables
+ _debug MIAB_Username "$MIAB_Username"
+ _debug MIAB_Password "$MIAB_Password"
+ _debug MIAB_Server "$MIAB_Server"
+
+ #check if MIAB environemt vars set and quit if not
+ if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then
+ _err "You didn't specify one or more of MIAB_Username, MIAB_Password or MIAB_Server."
+ _err "Please check these environment variables and try again."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable MIAB_Username "$MIAB_Username"
+ _saveaccountconf_mutable MIAB_Password "$MIAB_Password"
+ _saveaccountconf_mutable MIAB_Server "$MIAB_Server"
+ return 0
+}
+
+#Useage: _miab_rest "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" "custom/_acme-challenge.www.domain.com/txt "POST"
+#Returns: "updated DNS: domain.com"
+#rest interface MIAB dns
+_miab_rest() {
+ _data="$1"
+ _api_path="$2"
+ _httpmethod="$3"
+
+ #encode username and password for basic authentication
+ _credentials="$(printf "%s" "$MIAB_Username:$MIAB_Password" | _base64)"
+ export _H1="Authorization: Basic $_credentials"
+ _url="https://${MIAB_Server}/admin/dns/${_api_path}"
+
+ _debug2 _data "$_data"
+ _debug _api_path "$_api_path"
+ _debug2 _url "$_url"
+ _debug2 _credentails "$_credentials"
+ _debug _httpmethod "$_httpmethod"
+
+ if [ "$_httpmethod" = "GET" ]; then
+ response="$(_get "$_url")"
+ else
+ response="$(_post "$_data" "$_url" "" "$_httpmethod")"
+ fi
+
+ _retcode="$?"
+
+ if [ "$_retcode" != "0" ]; then
+ _err "MIAB REST authentication failed on $_httpmethod"
+ return 1
+ fi
+
+ _debug response "$response"
+ return 0
+}
+
+#Usage: _is_json "\[\n "mydomain.com"\n]"
+#Reurns "\[\n "mydomain.com"\n]"
+#returns the string if it begins and ends with square braces
+_is_json() {
+ _str="$(echo "$1" | _normalizeJson)"
+ echo "$_str" | grep '^\[.*\]$' >/dev/null 2>&1
+}
diff --git a/acme.sh-master/dnsapi/dns_misaka.sh b/acme.sh-master/dnsapi/dns_misaka.sh
new file mode 100644
index 0000000..36ba5cf
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_misaka.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env sh
+
+# bug reports to support+acmesh@misaka.io
+# based on dns_nsone.sh by dev@1e.ca
+
+#
+#Misaka_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+Misaka_Api="https://dnsapi.misaka.io/dns"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_misaka_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$Misaka_Key" ]; then
+ Misaka_Key=""
+ _err "You didn't specify misaka.io dns api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf Misaka_Key "$Misaka_Key"
+
+ _debug "checking root zone [$fulldomain]"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _misaka_rest GET "zones/${_domain}/recordsets?search=${_sub_domain}"
+
+ if ! _contains "$response" "\"results\":"; then
+ _err "Error"
+ return 1
+ fi
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Adding record"
+
+ if _misaka_rest POST "zones/${_domain}/recordsets/${_sub_domain}/TXT" "{\"records\":[{\"value\":\"\\\"$txtvalue\\\"\"}],\"filters\":[],\"ttl\":1}"; then
+ _debug response "$response"
+ if _contains "$response" "$_sub_domain"; then
+ _info "Added"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ else
+ _info "Updating record"
+
+ _misaka_rest PUT "zones/${_domain}/recordsets/${_sub_domain}/TXT?append=true" "{\"records\": [{\"value\": \"\\\"$txtvalue\\\"\"}],\"ttl\":1}"
+ if [ "$?" = "0" ] && _contains "$response" "$_sub_domain"; then
+ _info "Updated!"
+ #todo: check if the record takes effect
+ return 0
+ fi
+ _err "Update error"
+ return 1
+ fi
+
+}
+
+#fulldomain
+dns_misaka_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _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"
+ _misaka_rest GET "zones/${_domain}/recordsets?search=${_sub_domain}"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ if ! _misaka_rest DELETE "zones/${_domain}/recordsets/${_sub_domain}/TXT"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" ""
+ 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=2
+ p=1
+ if ! _misaka_rest GET "zones?limit=1000"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_misaka_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Content-Type: application/json"
+ export _H2="User-Agent: acme.sh/$VER misaka-dns-acmesh/20191213"
+ export _H3="Authorization: Token $Misaka_Key"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$Misaka_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$Misaka_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_myapi.sh b/acme.sh-master/dnsapi/dns_myapi.sh
new file mode 100644
index 0000000..7f3c5a8
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_myapi.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env sh
+
+#Here is a sample custom api script.
+#This file name is "dns_myapi.sh"
+#So, here must be a method dns_myapi_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: Neilpang
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+#
+######## Public functions #####################
+
+# Please Read this guide first: https://github.com/acmesh-official/acme.sh/wiki/DNS-API-Dev-Guide
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_myapi_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using myapi"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _err "Not implemented!"
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_myapi_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using myapi"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+}
+
+#################### Private functions below ##################################
diff --git a/acme.sh-master/dnsapi/dns_mydevil.sh b/acme.sh-master/dnsapi/dns_mydevil.sh
new file mode 100644
index 0000000..953290a
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_mydevil.sh
@@ -0,0 +1,99 @@
+#!/usr/bin/env sh
+
+# MyDevil.net API (2019-02-03)
+#
+# MyDevil.net already supports automatic Let's Encrypt certificates,
+# except for wildcard domains.
+#
+# This script depends on `devil` command that MyDevil.net provides,
+# which means that it works only on server side.
+#
+# Author: Marcin Konicki
+#
+######## Public functions #####################
+
+#Usage: dns_mydevil_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_mydevil_add() {
+ fulldomain=$1
+ txtvalue=$2
+ domain=""
+
+ if ! _exists "devil"; then
+ _err "Could not find 'devil' command."
+ return 1
+ fi
+
+ _info "Using mydevil"
+
+ domain=$(mydevil_get_domain "$fulldomain")
+ if [ -z "$domain" ]; then
+ _err "Invalid domain name: could not find root domain of $fulldomain."
+ return 1
+ fi
+
+ # No need to check if record name exists, `devil` always adds new record.
+ # In worst case scenario, we end up with multiple identical records.
+
+ _info "Adding $fulldomain record for domain $domain"
+ if devil dns add "$domain" "$fulldomain" TXT "$txtvalue"; then
+ _info "Successfully added TXT record, ready for validation."
+ return 0
+ else
+ _err "Unable to add DNS record."
+ return 1
+ fi
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_mydevil_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ domain=""
+
+ if ! _exists "devil"; then
+ _err "Could not find 'devil' command."
+ return 1
+ fi
+
+ _info "Using mydevil"
+
+ domain=$(mydevil_get_domain "$fulldomain")
+ if [ -z "$domain" ]; then
+ _err "Invalid domain name: could not find root domain of $fulldomain."
+ return 1
+ fi
+
+ # catch one or more numbers
+ num='[0-9][0-9]*'
+ # catch one or more whitespace
+ w=$(printf '[\t ][\t ]*')
+ # catch anything, except newline
+ any='.*'
+ # filter to make sure we do not delete other records
+ validRecords="^${num}${w}${fulldomain}${w}TXT${w}${any}${txtvalue}$"
+ for id in $(devil dns list "$domain" | tail -n+2 | grep "${validRecords}" | cut -w -s -f 1); do
+ _info "Removing record $id from domain $domain"
+ echo "y" | devil dns del "$domain" "$id" || _err "Could not remove DNS record."
+ done
+}
+
+#################### Private functions below ##################################
+
+# Usage: domain=$(mydevil_get_domain "_acme-challenge.www.domain.com" || _err "Invalid domain name")
+# echo $domain
+mydevil_get_domain() {
+ fulldomain=$1
+ domain=""
+
+ for domain in $(devil dns list | cut -w -s -f 1 | tail -n+2); do
+ _debug "Checking domain: $domain"
+ if _endswith "$fulldomain" "$domain"; then
+ _debug "Fulldomain '$fulldomain' matches '$domain'"
+ printf -- "%s" "$domain"
+ return 0
+ fi
+ done
+
+ return 1
+}
diff --git a/acme.sh-master/dnsapi/dns_mydnsjp.sh b/acme.sh-master/dnsapi/dns_mydnsjp.sh
new file mode 100644
index 0000000..13866f7
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_mydnsjp.sh
@@ -0,0 +1,196 @@
+#!/usr/bin/env sh
+
+#Here is a api script for MyDNS.JP.
+#This file name is "dns_mydnsjp.sh"
+#So, here must be a method dns_mydnsjp_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: epgdatacapbon
+#Report Bugs here: https://github.com/epgdatacapbon/acme.sh
+#
+######## Public functions #####################
+
+# Export MyDNS.JP MasterID and Password in following variables...
+# MYDNSJP_MasterID=MasterID
+# MYDNSJP_Password=Password
+
+MYDNSJP_API="https://www.mydns.jp"
+
+#Usage: dns_mydnsjp_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_mydnsjp_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using mydnsjp"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ # Load the credentials from the account conf file
+ MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
+ MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
+ if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
+ MYDNSJP_MasterID=""
+ MYDNSJP_Password=""
+ _err "You don't specify mydnsjp api MasterID and Password yet."
+ _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again."
+ return 1
+ fi
+
+ # Save the credentials to the account conf file
+ _saveaccountconf_mutable MYDNSJP_MasterID "$MYDNSJP_MasterID"
+ _saveaccountconf_mutable MYDNSJP_Password "$MYDNSJP_Password"
+
+ _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"
+
+ if _mydnsjp_api "REGIST" "$_domain" "$txtvalue"; then
+ if printf -- "%s" "$response" | grep "OK." >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_mydnsjp_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Removing TXT record"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ # Load the credentials from the account conf file
+ MYDNSJP_MasterID="${MYDNSJP_MasterID:-$(_readaccountconf_mutable MYDNSJP_MasterID)}"
+ MYDNSJP_Password="${MYDNSJP_Password:-$(_readaccountconf_mutable MYDNSJP_Password)}"
+ if [ -z "$MYDNSJP_MasterID" ] || [ -z "$MYDNSJP_Password" ]; then
+ MYDNSJP_MasterID=""
+ MYDNSJP_Password=""
+ _err "You don't specify mydnsjp api MasterID and Password yet."
+ _err "Please export as MYDNSJP_MasterID / MYDNSJP_Password and try again."
+ 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"
+
+ if _mydnsjp_api "DELETE" "$_domain" "$txtvalue"; then
+ if printf -- "%s" "$response" | grep "OK." >/dev/null; then
+ _info "Deleted, OK"
+ return 0
+ else
+ _err "Delete txt record error."
+ return 1
+ fi
+ fi
+ _err "Delete txt record error."
+
+ return 1
+}
+
+#################### Private functions below ##################################
+# _acme-challenge.www.domain.com
+# returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=2
+ p=1
+
+ # Get the root domain
+ _mydnsjp_retrieve_domain
+ if [ "$?" != "0" ]; then
+ # not valid
+ return 1
+ fi
+
+ while true; do
+ _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+
+ if [ -z "$_domain" ]; then
+ # not valid
+ return 1
+ fi
+
+ if [ "$_domain" = "$_root_domain" ]; then
+ _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+# Retrieve the root domain
+# returns 0 success
+_mydnsjp_retrieve_domain() {
+ _debug "Login to MyDNS.JP"
+
+ response="$(_post "MENU=100&masterid=$MYDNSJP_MasterID&masterpwd=$MYDNSJP_Password" "$MYDNSJP_API/members/")"
+ cookie="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _head_n 1 | cut -d " " -f 2)"
+
+ # If cookies is not empty then logon successful
+ if [ -z "$cookie" ]; then
+ _err "Fail to get a cookie."
+ return 1
+ fi
+
+ _root_domain=$(echo "$response" | grep "DNSINFO\[domainname\]" | sed 's/^.*value="\([^"]*\)".*/\1/')
+
+ _debug _root_domain "$_root_domain"
+
+ if [ -z "$_root_domain" ]; then
+ _err "Fail to get the root domain."
+ return 1
+ fi
+
+ return 0
+}
+
+_mydnsjp_api() {
+ cmd=$1
+ domain=$2
+ txtvalue=$3
+
+ # Base64 encode the credentials
+ credentials=$(printf "%s:%s" "$MYDNSJP_MasterID" "$MYDNSJP_Password" | _base64)
+
+ # Construct the HTTP Authorization header
+ export _H1="Content-Type: application/x-www-form-urlencoded"
+ export _H2="Authorization: Basic ${credentials}"
+
+ response="$(_post "CERTBOT_DOMAIN=$domain&CERTBOT_VALIDATION=$txtvalue&EDIT_CMD=$cmd" "$MYDNSJP_API/directedit.html")"
+
+ if [ "$?" != "0" ]; then
+ _err "error $domain"
+ return 1
+ fi
+
+ _debug2 response "$response"
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_mythic_beasts.sh b/acme.sh-master/dnsapi/dns_mythic_beasts.sh
new file mode 100644
index 0000000..294ae84
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_mythic_beasts.sh
@@ -0,0 +1,261 @@
+#!/usr/bin/env sh
+# Mythic Beasts is a long-standing UK service provider using standards-based OAuth2 authentication
+# To test: ./acme.sh --dns dns_mythic_beasts --test --debug 1 --output-insecure --issue --domain domain.com
+# Cannot retest once cert is issued
+# OAuth2 tokens only valid for 300 seconds so we do not store
+# NOTE: This will remove all TXT records matching the fulldomain, not just the added ones (_acme-challenge.www.domain.com)
+
+# Test OAuth2 credentials
+#MB_AK="aaaaaaaaaaaaaaaa"
+#MB_AS="bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
+
+# URLs
+MB_API='https://api.mythic-beasts.com/dns/v2/zones'
+MB_AUTH='https://auth.mythic-beasts.com/login'
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_mythic_beasts_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "MYTHIC BEASTS Adding record $fulldomain = $txtvalue"
+ if ! _initAuth; then
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ # method path body_data
+ if _mb_rest POST "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then
+
+ if _contains "$response" "1 records added"; then
+ _info "Added, verifying..."
+ # Max 120 seconds to publish
+ for i in $(seq 1 6); do
+ # Retry on error
+ if ! _mb_rest GET "$_domain/records/$_sub_domain/TXT?verify"; then
+ _sleep 20
+ else
+ _info "Record published!"
+ return 0
+ fi
+ done
+
+ else
+ _err "\n$response"
+ fi
+
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_mythic_beasts_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "MYTHIC BEASTS Removing record $fulldomain = $txtvalue"
+ if ! _initAuth; then
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+
+ # method path body_data
+ if _mb_rest DELETE "$_domain/records/$_sub_domain/TXT" "$txtvalue"; then
+ _info "Record removed"
+ return 0
+ fi
+ _err "Remove txt record error."
+ return 1
+}
+
+#################### Private functions below ##################################
+
+#Possible formats:
+# _acme-challenge.www.example.com
+# _acme-challenge.example.com
+# _acme-challenge.example.co.uk
+# _acme-challenge.www.example.co.uk
+# _acme-challenge.sub1.sub2.www.example.co.uk
+# sub1.sub2.example.co.uk
+# example.com
+# example.co.uk
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ _debug "Detect the root zone"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ _err "Domain exhausted"
+ return 1
+ fi
+
+ # Use the status errors to find the domain, continue on 403 Access denied
+ # method path body_data
+ _mb_rest GET "$h/records"
+ ret="$?"
+ if [ "$ret" -eq 0 ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+ return 0
+ elif [ "$ret" -eq 1 ]; then
+ return 1
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+
+ if [ "$i" -gt 50 ]; then
+ break
+ fi
+ done
+ _err "Domain too long"
+ return 1
+}
+
+_initAuth() {
+ MB_AK="${MB_AK:-$(_readaccountconf_mutable MB_AK)}"
+ MB_AS="${MB_AS:-$(_readaccountconf_mutable MB_AS)}"
+
+ if [ -z "$MB_AK" ] || [ -z "$MB_AS" ]; then
+ MB_AK=""
+ MB_AS=""
+ _err "Please specify an OAuth2 Key & Secret"
+ return 1
+ fi
+
+ _saveaccountconf_mutable MB_AK "$MB_AK"
+ _saveaccountconf_mutable MB_AS "$MB_AS"
+
+ if ! _oauth2; then
+ return 1
+ fi
+
+ _info "Checking authentication"
+ _secure_debug access_token "$MB_TK"
+ _sleep 1
+
+ # GET a list of zones
+ # method path body_data
+ if ! _mb_rest GET ""; then
+ _err "The token is invalid"
+ return 1
+ fi
+ _info "Token OK"
+ return 0
+}
+
+# Github appears to use an outbound proxy for requests which means subsequent requests may not have the same
+# source IP. The standard Mythic Beasts OAuth2 tokens are tied to an IP, meaning github test requests fail
+# authentication. This is a work around using an undocumented MB API to obtain a token not tied to an
+# IP just for the github tests.
+_oauth2() {
+ if [ "$GITHUB_ACTIONS" = "true" ]; then
+ _oauth2_github
+ else
+ _oauth2_std
+ fi
+ return $?
+}
+
+_oauth2_std() {
+ # HTTP Basic Authentication
+ _H1="Authorization: Basic $(echo "$MB_AK:$MB_AS" | _base64)"
+ _H2="Accepts: application/json"
+ export _H1 _H2
+ body="grant_type=client_credentials"
+
+ _info "Getting OAuth2 token..."
+ # body url [needbase64] [POST|PUT|DELETE] [ContentType]
+ response="$(_post "$body" "$MB_AUTH" "" "POST" "application/x-www-form-urlencoded")"
+ if _contains "$response" "\"token_type\":\"bearer\""; then
+ MB_TK="$(echo "$response" | _egrep_o "access_token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
+ if [ -z "$MB_TK" ]; then
+ _err "Unable to get access_token"
+ _err "\n$response"
+ return 1
+ fi
+ else
+ _err "OAuth2 token_type not Bearer"
+ _err "\n$response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
+
+_oauth2_github() {
+ _H1="Accepts: application/json"
+ export _H1
+ body="{\"login\":{\"handle\":\"$MB_AK\",\"pass\":\"$MB_AS\",\"floating\":1}}"
+
+ _info "Getting Floating token..."
+ # body url [needbase64] [POST|PUT|DELETE] [ContentType]
+ response="$(_post "$body" "$MB_AUTH" "" "POST" "application/json")"
+ MB_TK="$(echo "$response" | _egrep_o "\"token\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
+ if [ -z "$MB_TK" ]; then
+ _err "Unable to get token"
+ _err "\n$response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
+
+# method path body_data
+_mb_rest() {
+ # URL encoded body for single API operations
+ m="$1"
+ ep="$2"
+ data="$3"
+
+ if [ -z "$ep" ]; then
+ _mb_url="$MB_API"
+ else
+ _mb_url="$MB_API/$ep"
+ fi
+
+ _H1="Authorization: Bearer $MB_TK"
+ _H2="Accepts: application/json"
+ export _H1 _H2
+ if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
+ # body url [needbase64] [POST|PUT|DELETE] [ContentType]
+ response="$(_post "data=$data" "$_mb_url" "" "$m" "application/x-www-form-urlencoded")"
+ else
+ response="$(_get "$_mb_url")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "Request error"
+ return 1
+ fi
+
+ header="$(cat "$HTTP_HEADER")"
+ status="$(echo "$header" | _egrep_o "^HTTP[^ ]* .*$" | cut -d " " -f 2-100 | tr -d "\f\n")"
+ code="$(echo "$status" | _egrep_o "^[0-9]*")"
+ if [ "$code" -ge 400 ] || _contains "$response" "\"error\"" || _contains "$response" "invalid_client"; then
+ _err "error $status"
+ _err "\n$response"
+ _debug "\n$header"
+ return 2
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_namecheap.sh b/acme.sh-master/dnsapi/dns_namecheap.sh
new file mode 100644
index 0000000..a5f667a
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_namecheap.sh
@@ -0,0 +1,411 @@
+#!/usr/bin/env sh
+
+# Namecheap API
+# https://www.namecheap.com/support/api/intro.aspx
+#
+# Requires Namecheap API key set in
+#NAMECHEAP_API_KEY,
+#NAMECHEAP_USERNAME,
+#NAMECHEAP_SOURCEIP
+# Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise.
+
+######## Public functions #####################
+
+NAMECHEAP_API="https://api.namecheap.com/xml.response"
+
+#Usage: dns_namecheap_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_namecheap_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _namecheap_check_config; then
+ _err "$error"
+ return 1
+ fi
+
+ if ! _namecheap_set_publicip; then
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _set_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_namecheap_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _namecheap_set_publicip; then
+ return 1
+ fi
+
+ if ! _namecheap_check_config; then
+ _err "$error"
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _del_namecheap_TXT "$_domain" "$_sub_domain" "$txtvalue"
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+
+ if ! _get_root_by_getList "$fulldomain"; then
+ _debug "Failed domain lookup via domains.getList api call. Trying domain lookup via domains.dns.getHosts api."
+ # The above "getList" api will only return hosts *owned* by the calling user. However, if the calling
+ # user is not the owner, but still has administrative rights, we must query the getHosts api directly.
+ # See this comment and the official namecheap response: https://disq.us/p/1q6v9x9
+ if ! _get_root_by_getHosts "$fulldomain"; then
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+_get_root_by_getList() {
+ domain=$1
+
+ if ! _namecheap_post "namecheap.domains.getList"; then
+ _err "$error"
+ return 1
+ fi
+
+ i=2
+ p=1
+
+ while true; do
+
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+ if ! _contains "$h" "\\."; then
+ #not valid
+ return 1
+ fi
+
+ if ! _contains "$response" "$h"; then
+ _debug "$h not found"
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_get_root_by_getHosts() {
+ i=100
+ p=99
+
+ while [ $p -ne 0 ]; do
+
+ h=$(printf "%s" "$1" | cut -d . -f $i-100)
+ if [ -n "$h" ]; then
+ if _contains "$h" "\\."; then
+ _debug h "$h"
+ if _namecheap_set_tld_sld "$h"; then
+ _sub_domain=$(printf "%s" "$1" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ else
+ _debug "$h not found"
+ fi
+ fi
+ fi
+ i="$p"
+ p=$(_math "$p" - 1)
+ done
+ return 1
+}
+
+_namecheap_set_publicip() {
+
+ if [ -z "$NAMECHEAP_SOURCEIP" ]; then
+ _err "No Source IP specified for Namecheap API."
+ _err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
+ return 1
+ else
+ _saveaccountconf NAMECHEAP_SOURCEIP "$NAMECHEAP_SOURCEIP"
+ _debug sourceip "$NAMECHEAP_SOURCEIP"
+
+ ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
+ addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https):\/\/.*')
+
+ _debug2 ip "$ip"
+ _debug2 addr "$addr"
+
+ if [ -n "$ip" ]; then
+ _publicip="$ip"
+ elif [ -n "$addr" ]; then
+ _publicip=$(_get "$addr")
+ else
+ _err "No Source IP specified for Namecheap API."
+ _err "Use your public ip address or an url to retrieve it (e.g. https://ifconfig.co/ip) and export it as NAMECHEAP_SOURCEIP"
+ return 1
+ fi
+ fi
+
+ _debug publicip "$_publicip"
+
+ return 0
+}
+
+_namecheap_post() {
+ command=$1
+ data="ApiUser=${NAMECHEAP_USERNAME}&ApiKey=${NAMECHEAP_API_KEY}&ClientIp=${_publicip}&UserName=${NAMECHEAP_USERNAME}&Command=${command}"
+ _debug2 "_namecheap_post data" "$data"
+ response="$(_post "$data" "$NAMECHEAP_API" "" "POST")"
+ _debug2 response "$response"
+
+ if _contains "$response" "Status=\"ERROR\"" >/dev/null; then
+ error=$(echo "$response" | _egrep_o ">.*<\\/Error>" | cut -d '<' -f 1 | tr -d '>')
+ _err "error $error"
+ return 1
+ fi
+
+ return 0
+}
+
+_namecheap_parse_host() {
+ _host=$1
+ _debug _host "$_host"
+
+ _hostid=$(echo "$_host" | _egrep_o ' HostId="[^"]*' | cut -d '"' -f 2)
+ _hostname=$(echo "$_host" | _egrep_o ' Name="[^"]*' | cut -d '"' -f 2)
+ _hosttype=$(echo "$_host" | _egrep_o ' Type="[^"]*' | cut -d '"' -f 2)
+ _hostaddress=$(echo "$_host" | _egrep_o ' Address="[^"]*' | cut -d '"' -f 2 | _xml_decode)
+ _hostmxpref=$(echo "$_host" | _egrep_o ' MXPref="[^"]*' | cut -d '"' -f 2)
+ _hostttl=$(echo "$_host" | _egrep_o ' TTL="[^"]*' | cut -d '"' -f 2)
+
+ _debug hostid "$_hostid"
+ _debug hostname "$_hostname"
+ _debug hosttype "$_hosttype"
+ _debug hostaddress "$_hostaddress"
+ _debug hostmxpref "$_hostmxpref"
+ _debug hostttl "$_hostttl"
+}
+
+_namecheap_check_config() {
+
+ if [ -z "$NAMECHEAP_API_KEY" ]; then
+ _err "No API key specified for Namecheap API."
+ _err "Create your key and export it as NAMECHEAP_API_KEY"
+ return 1
+ fi
+
+ if [ -z "$NAMECHEAP_USERNAME" ]; then
+ _err "No username key specified for Namecheap API."
+ _err "Create your key and export it as NAMECHEAP_USERNAME"
+ return 1
+ fi
+
+ _saveaccountconf NAMECHEAP_API_KEY "$NAMECHEAP_API_KEY"
+ _saveaccountconf NAMECHEAP_USERNAME "$NAMECHEAP_USERNAME"
+
+ return 0
+}
+
+_set_namecheap_TXT() {
+ subdomain=$2
+ txt=$3
+
+ if ! _namecheap_set_tld_sld "$1"; then
+ return 1
+ fi
+
+ request="namecheap.domains.dns.getHosts&SLD=${_sld}&TLD=${_tld}"
+
+ if ! _namecheap_post "$request"; then
+ _err "$error"
+ return 1
+ fi
+
+ hosts=$(echo "$response" | _egrep_o ']*')
+ _debug hosts "$hosts"
+
+ if [ -z "$hosts" ]; then
+ _err "Hosts not found"
+ return 1
+ fi
+
+ _namecheap_reset_hostList
+
+ while read -r host; do
+ if _contains "$host" "]*')
+ _debug hosts "$hosts"
+
+ if [ -z "$hosts" ]; then
+ _err "Hosts not found"
+ return 1
+ fi
+
+ _namecheap_reset_hostList
+
+ found=0
+
+ while read -r host; do
+ if _contains "$host" "300")
+ if [ "$retcode" ]; then
+ _info "Successfully added TXT record, ready for validation."
+ return 0
+ else
+ _err "Unable to add the DNS record."
+ return 1
+ fi
+ fi
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_namesilo_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _get_root "$fulldomain"; then
+ _err "Unable to find domain specified."
+ return 1
+ fi
+
+ # Get the record id.
+ if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "
300")
+ if [ "$retcode" ]; then
+ _record_id=$(echo "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1)
+ _debug _record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _info "Successfully retrieved the record id for ACME challenge."
+ else
+ _info "Empty record id, it seems no such record."
+ return 0
+ fi
+ else
+ _err "Unable to retrieve the record id."
+ return 1
+ fi
+ fi
+
+ # Remove the DNS record using record id.
+ if _namesilo_rest GET "dnsDeleteRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrid=$_record_id"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "300")
+ if [ "$retcode" ]; then
+ _info "Successfully removed the TXT record."
+ return 0
+ else
+ _err "Unable to remove the DNS record."
+ return 1
+ fi
+ fi
+}
+
+#################### Private functions below ##################################
+
+# _acme-challenge.www.domain.com
+# returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if ! _namesilo_rest GET "listDomains?version=1&type=xml&key=$Namesilo_Key"; then
+ return 1
+ fi
+
+ # Need to exclude the last field (tld)
+ numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
+ while [ $i -le "$numfields" ]; do
+ host=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug host "$host"
+ if [ -z "$host" ]; then
+ return 1
+ fi
+
+ if _contains "$response" ">$host"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$host"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_namesilo_rest() {
+ method=$1
+ param=$2
+ data=$3
+
+ if [ "$method" != "GET" ]; then
+ response="$(_post "$data" "$Namesilo_API/$param" "" "$method")"
+ else
+ response="$(_get "$Namesilo_API/$param")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $param"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_nanelo.sh b/acme.sh-master/dnsapi/dns_nanelo.sh
new file mode 100644
index 0000000..8ccc8c2
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nanelo.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env sh
+
+# Official DNS API for Nanelo.com
+
+# Provide the required API Key like this:
+# NANELO_TOKEN="FmD408PdqT1E269gUK57"
+
+NANELO_API="https://api.nanelo.com/v1/"
+
+######## Public functions #####################
+
+# Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nanelo_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
+ if [ -z "$NANELO_TOKEN" ]; then
+ NANELO_TOKEN=""
+ _err "You didn't configure a Nanelo API Key yet."
+ _err "Please set NANELO_TOKEN and try again."
+ _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
+ return 1
+ fi
+ _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
+
+ _info "Adding TXT record to ${fulldomain}"
+ response="$(_get "$NANELO_API$NANELO_TOKEN/dns/addrecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
+ if _contains "${response}" 'success'; then
+ return 0
+ fi
+ _err "Could not create resource record, please check the logs"
+ _err "${response}"
+ return 1
+}
+
+dns_nanelo_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NANELO_TOKEN="${NANELO_TOKEN:-$(_readaccountconf_mutable NANELO_TOKEN)}"
+ if [ -z "$NANELO_TOKEN" ]; then
+ NANELO_TOKEN=""
+ _err "You didn't configure a Nanelo API Key yet."
+ _err "Please set NANELO_TOKEN and try again."
+ _err "Login to Nanelo.com and go to Settings > API Keys to get a Key"
+ return 1
+ fi
+ _saveaccountconf_mutable NANELO_TOKEN "$NANELO_TOKEN"
+
+ _info "Deleting resource record $fulldomain"
+ response="$(_get "$NANELO_API$NANELO_TOKEN/dns/deleterecord?type=TXT&ttl=60&name=${fulldomain}&value=${txtvalue}")"
+ if _contains "${response}" 'success'; then
+ return 0
+ fi
+ _err "Could not delete resource record, please check the logs"
+ _err "${response}"
+ return 1
+}
diff --git a/acme.sh-master/dnsapi/dns_nederhost.sh b/acme.sh-master/dnsapi/dns_nederhost.sh
new file mode 100644
index 0000000..abaae42
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nederhost.sh
@@ -0,0 +1,127 @@
+#!/usr/bin/env sh
+
+#NederHost_Key="sdfgikogfdfghjklkjhgfcdcfghj"
+
+NederHost_Api="https://api.nederhost.nl/dns/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nederhost_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}"
+ if [ -z "$NederHost_Key" ]; then
+ NederHost_Key=""
+ _err "You didn't specify a NederHost api key."
+ _err "You can get yours from https://www.nederhost.nl/mijn_nederhost"
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable NederHost_Key "$NederHost_Key"
+
+ _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"
+
+ _info "Adding record"
+ if _nederhost_rest PATCH "zones/$_domain/records/$fulldomain/TXT" "[{\"content\":\"$txtvalue\",\"ttl\":60}]"; then
+ if _contains "$response" "$fulldomain"; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+
+}
+
+#fulldomain txtvalue
+dns_nederhost_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NederHost_Key="${NederHost_Key:-$(_readaccountconf_mutable NederHost_Key)}"
+ if [ -z "$NederHost_Key" ]; then
+ NederHost_Key=""
+ _err "You didn't specify a NederHost api key."
+ _err "You can get yours from https://www.nederhost.nl/mijn_nederhost"
+ 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 "Removing txt record"
+ _nederhost_rest DELETE "zones/${_domain}/records/$fulldomain/TXT?content=$txtvalue"
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _debug _domain "$_domain"
+ if [ -z "$_domain" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _nederhost_rest GET "zones/${_domain}"; then
+ if [ "${_code}" = "204" ]; then
+ return 0
+ fi
+ else
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_nederhost_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Authorization: Bearer $NederHost_Key"
+ export _H2="Content-Type: application/json"
+
+ _debug data "$data"
+ response="$(_post "$data" "$NederHost_Api/$ep" "" "$m")"
+
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_neodigit.sh b/acme.sh-master/dnsapi/dns_neodigit.sh
new file mode 100644
index 0000000..64ea878
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_neodigit.sh
@@ -0,0 +1,181 @@
+#!/usr/bin/env sh
+
+#
+# NEODIGIT_API_TOKEN="jasdfhklsjadhflnhsausdfas"
+
+# This is Neodigit.net api wrapper for acme.sh
+#
+# Author: Adrian Almenar
+# Report Bugs here: https://github.com/tecnocratica/acme.sh
+#
+NEODIGIT_API_URL="https://api.neodigit.net/v1"
+#
+######## Public functions #####################
+
+# Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_neodigit_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}"
+ if [ -z "$NEODIGIT_API_TOKEN" ]; then
+ NEODIGIT_API_TOKEN=""
+ _err "You haven't specified a Token api key."
+ _err "Please create the key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _debug "Getting txt records"
+ _neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain"
+
+ _debug _code "$_code"
+
+ if [ "$_code" != "200" ]; then
+ _err "error retrieving data!"
+ return 1
+ fi
+
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _info "Adding record"
+ if _neo_rest POST "dns/zones/$_domain_id/records" "{\"record\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":60}}"; then
+ if printf -- "%s" "$response" | grep "$_sub_domain" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_neodigit_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NEODIGIT_API_TOKEN="${NEODIGIT_API_TOKEN:-$(_readaccountconf_mutable NEODIGIT_API_TOKEN)}"
+ if [ -z "$NEODIGIT_API_TOKEN" ]; then
+ NEODIGIT_API_TOKEN=""
+ _err "You haven't specified a Token api key."
+ _err "Please create the key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable NEODIGIT_API_TOKEN "$NEODIGIT_API_TOKEN"
+
+ _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"
+ _neo_rest GET "dns/zones/${_domain_id}/records?type=TXT&name=$fulldomain&content=$txtvalue"
+
+ if [ "$_code" != "200" ]; then
+ _err "error retrieving data!"
+ return 1
+ fi
+
+ record_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _neo_rest DELETE "dns/zones/$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=dasfdsafsadg5ythd
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _neo_rest GET "dns/zones?name=$h"; then
+ return 1
+ fi
+
+ _debug p "$p"
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _domain_id=$(echo "$response" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_neo_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="X-TCPanel-Token: $NEODIGIT_API_TOKEN"
+ export _H2="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$NEODIGIT_API_URL/$ep" "" "$m")"
+ else
+ response="$(_get "$NEODIGIT_API_URL/$ep")"
+ fi
+
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_netcup.sh b/acme.sh-master/dnsapi/dns_netcup.sh
new file mode 100644
index 0000000..776fa02
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_netcup.sh
@@ -0,0 +1,134 @@
+#!/usr/bin/env sh
+#developed by linux-insideDE
+
+NC_Apikey="${NC_Apikey:-$(_readaccountconf_mutable NC_Apikey)}"
+NC_Apipw="${NC_Apipw:-$(_readaccountconf_mutable NC_Apipw)}"
+NC_CID="${NC_CID:-$(_readaccountconf_mutable NC_CID)}"
+end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
+client=""
+
+dns_netcup_add() {
+ _debug NC_Apikey "$NC_Apikey"
+ login
+ if [ "$NC_Apikey" = "" ] || [ "$NC_Apipw" = "" ] || [ "$NC_CID" = "" ]; then
+ _err "No Credentials given"
+ return 1
+ fi
+ _saveaccountconf_mutable NC_Apikey "$NC_Apikey"
+ _saveaccountconf_mutable NC_Apipw "$NC_Apipw"
+ _saveaccountconf_mutable NC_CID "$NC_CID"
+ fulldomain=$1
+ txtvalue=$2
+ domain=""
+ exit=$(echo "$fulldomain" | tr -dc '.' | wc -c)
+ exit=$(_math "$exit" + 1)
+ i=$exit
+
+ while
+ [ "$exit" -gt 0 ]
+ do
+ tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
+ if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
+ domain="$tmp"
+ else
+ domain="$tmp.$domain"
+ fi
+ if [ "$(_math "$i" - "$exit")" -ge 1 ]; then
+ msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"false\", \"state\": \"yes\"} ]}}}" "$end" "" "POST")
+ _debug "$msg"
+ if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then
+ if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
+ _err "$msg"
+ return 1
+ else
+ break
+ fi
+ fi
+ fi
+ exit=$(_math "$exit" - 1)
+ done
+ logout
+}
+
+dns_netcup_rm() {
+ login
+ fulldomain=$1
+ txtvalue=$2
+
+ domain=""
+ exit=$(echo "$fulldomain" | tr -dc '.' | wc -c)
+ exit=$(_math "$exit" + 1)
+ i=$exit
+ rec=""
+
+ while
+ [ "$exit" -gt 0 ]
+ do
+ tmp=$(echo "$fulldomain" | cut -d'.' -f"$exit")
+ if [ "$(_math "$i" - "$exit")" -eq 0 ]; then
+ domain="$tmp"
+ else
+ domain="$tmp.$domain"
+ fi
+ if [ "$(_math "$i" - "$exit")" -ge 1 ]; then
+ msg=$(_post "{\"action\": \"infoDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\", \"domainname\": \"$domain\"}}" "$end" "" "POST")
+ rec=$(echo "$msg" | sed 's/\[//g' | sed 's/\]//g' | sed 's/{\"serverrequestid\".*\"dnsrecords\"://g' | sed 's/},{/};{/g' | sed 's/{//g' | sed 's/}//g')
+ _debug "$msg"
+ if [ "$(_getfield "$msg" "5" | sed 's/"statuscode"://g')" != 5028 ]; then
+ if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
+ _err "$msg"
+ return 1
+ else
+ break
+ fi
+ fi
+ fi
+ exit=$(_math "$exit" - 1)
+ done
+
+ ida=0000
+ idv=0001
+ ids=0000000000
+ i=1
+ while
+ [ "$i" -ne 0 ]
+ do
+ specrec=$(_getfield "$rec" "$i" ";")
+ idv="$ida"
+ ida=$(_getfield "$specrec" "1" "," | sed 's/\"id\":\"//g' | sed 's/\"//g')
+ txtv=$(_getfield "$specrec" "5" "," | sed 's/\"destination\":\"//g' | sed 's/\"//g')
+ i=$(_math "$i" + 1)
+ if [ "$txtvalue" = "$txtv" ]; then
+ i=0
+ ids="$ida"
+ fi
+ if [ "$ida" = "$idv" ]; then
+ i=0
+ fi
+ done
+ msg=$(_post "{\"action\": \"updateDnsRecords\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\",\"clientrequestid\": \"$client\" , \"domainname\": \"$domain\", \"dnsrecordset\": { \"dnsrecords\": [ {\"id\": \"$ids\", \"hostname\": \"$fulldomain.\", \"type\": \"TXT\", \"priority\": \"\", \"destination\": \"$txtvalue\", \"deleterecord\": \"TRUE\", \"state\": \"yes\"} ]}}}" "$end" "" "POST")
+ _debug "$msg"
+ if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
+ _err "$msg"
+ return 1
+ fi
+ logout
+}
+
+login() {
+ tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
+ sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4)
+ _debug "$tmp"
+ if [ "$(_getfield "$tmp" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
+ _err "$tmp"
+ return 1
+ fi
+}
+logout() {
+ tmp=$(_post "{\"action\": \"logout\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apisessionid\": \"$sid\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST")
+ _debug "$tmp"
+ if [ "$(_getfield "$tmp" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then
+ _err "$tmp"
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_netlify.sh b/acme.sh-master/dnsapi/dns_netlify.sh
new file mode 100644
index 0000000..0e5dc32
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_netlify.sh
@@ -0,0 +1,162 @@
+#!/usr/bin/env sh
+
+#NETLIFY_ACCESS_TOKEN="xxxx"
+
+NETLIFY_HOST="api.netlify.com/api/v1/"
+NETLIFY_URL="https://$NETLIFY_HOST"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_netlify_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
+
+ if [ -z "$NETLIFY_ACCESS_TOKEN" ]; then
+ NETLIFY_ACCESS_TOKEN=""
+ _err "Please specify your Netlify Access Token and try again."
+ return 1
+ else
+ _saveaccountconf_mutable NETLIFY_ACCESS_TOKEN "$NETLIFY_ACCESS_TOKEN"
+ fi
+
+ _info "Using Netlify"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ dnsRecordURI="dns_zones/$_domain_id/dns_records"
+
+ body="{\"type\":\"TXT\", \"hostname\":\"$_sub_domain\", \"value\":\"$txtvalue\", \"ttl\":\"10\"}"
+
+ _netlify_rest POST "$dnsRecordURI" "$body" "$NETLIFY_ACCESS_TOKEN"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ if [ "$_code" = "200" ] || [ "$_code" = '201' ]; then
+ _info "validation value added"
+ return 0
+ else
+ _err "error adding validation value ($_code)"
+ return 1
+ fi
+
+ _err "Not fully implemented!"
+ return 1
+}
+
+#Usage: dns_myapi_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+#Remove the txt record after validation.
+dns_netlify_rm() {
+ _info "Using Netlify"
+ txtdomain="$1"
+ txt="$2"
+ _debug txtdomain "$txtdomain"
+ _debug txt "$txt"
+
+ NETLIFY_ACCESS_TOKEN="${NETLIFY_ACCESS_TOKEN:-$(_readaccountconf_mutable NETLIFY_ACCESS_TOKEN)}"
+
+ if ! _get_root "$txtdomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ dnsRecordURI="dns_zones/$_domain_id/dns_records"
+
+ _netlify_rest GET "$dnsRecordURI" "" "$NETLIFY_ACCESS_TOKEN"
+
+ _record_id=$(echo "$response" | _egrep_o "\"type\":\"TXT\",[^\}]*\"value\":\"$txt\"" | head -n 1 | _egrep_o "\"id\":\"[^\"\}]*\"" | cut -d : -f 2 | tr -d \")
+ _debug _record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _netlify_rest DELETE "$dnsRecordURI/$_record_id" "" "$NETLIFY_ACCESS_TOKEN"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ if [ "$_code" = "200" ] || [ "$_code" = '204' ]; then
+ _info "validation value removed"
+ return 0
+ else
+ _err "error removing validation value ($_code)"
+ return 1
+ fi
+ return 0
+ fi
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_get_root() {
+ domain=$1
+ accesstoken=$2
+ i=1
+ p=1
+
+ _netlify_rest GET "dns_zones" "" "$accesstoken"
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug2 "Checking domain: $h"
+ if [ -z "$h" ]; then
+ #not valid
+ _err "Invalid domain"
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _domain_id=$(echo "$response" | _egrep_o "\"[^\"]*\",\"name\":\"$h\"" | cut -d , -f 1 | tr -d \")
+ if [ "$_domain_id" ]; then
+ if [ "$i" = 1 ]; then
+ #create the record at the domain apex (@) if only the domain name was provided as --domain-alias
+ _sub_domain="@"
+ else
+ _sub_domain=$(echo "$domain" | cut -d . -f 1-$p)
+ fi
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_netlify_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ token_trimmed=$(echo "$NETLIFY_ACCESS_TOKEN" | tr -d '"')
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: Bearer $token_trimmed"
+
+ : >"$HTTP_HEADER"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$NETLIFY_URL$ep" "" "$m")"
+ else
+ response="$(_get "$NETLIFY_URL$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_nic.sh b/acme.sh-master/dnsapi/dns_nic.sh
new file mode 100644
index 0000000..56170f8
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nic.sh
@@ -0,0 +1,205 @@
+#!/usr/bin/env sh
+
+#
+#NIC_ClientID='0dc0xxxxxxxxxxxxxxxxxxxxxxxxce88'
+#NIC_ClientSecret='3LTtxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnuW8'
+#NIC_Username="000000/NIC-D"
+#NIC_Password="xxxxxxx"
+
+NIC_Api="https://api.nic.ru"
+
+dns_nic_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _nic_get_authtoken save; then
+ _err "get NIC auth token failed"
+ 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 _service "$_service"
+
+ _info "Adding record"
+ if ! _nic_rest PUT "services/$_service/zones/$_domain/records" "$_sub_domainTXT$txtvalue"; then
+ _err "Add TXT record error"
+ return 1
+ fi
+
+ if ! _nic_rest POST "services/$_service/zones/$_domain/commit" ""; then
+ return 1
+ fi
+ _info "Added, OK"
+}
+
+dns_nic_rm() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _nic_get_authtoken; then
+ _err "get NIC auth token failed"
+ return 1
+ fi
+
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+ _debug _service "$_service"
+
+ if ! _nic_rest GET "services/$_service/zones/$_domain/records"; then
+ _err "Get records error"
+ return 1
+ fi
+
+ _domain_id=$(printf "%s" "$response" | grep "$_sub_domain" | grep -- "$txtvalue" | sed -r "s/.*"; then
+ error=$(printf "%s" "$response" | grep "error code" | sed -r "s/.*(.*)<\/error>/\1/g")
+ _err "Error: $error"
+ return 1
+ fi
+
+ if ! _contains "$response" "success"; then
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_njalla.sh b/acme.sh-master/dnsapi/dns_njalla.sh
new file mode 100644
index 0000000..e924328
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_njalla.sh
@@ -0,0 +1,168 @@
+#!/usr/bin/env sh
+
+#
+#NJALLA_Token="sdfsdfsdfljlbjkljlkjsdfoiwje"
+
+NJALLA_Api="https://njal.la/api/1/"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_njalla_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NJALLA_Token="${NJALLA_Token:-$(_readaccountconf_mutable NJALLA_Token)}"
+
+ if [ "$NJALLA_Token" ]; then
+ _saveaccountconf_mutable NJALLA_Token "$NJALLA_Token"
+ else
+ NJALLA_Token=""
+ _err "You didn't specify a Njalla api token yet."
+ 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"
+
+ # 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 _njalla_rest "{\"method\":\"add-record\",\"params\":{\"domain\":\"$_domain\",\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}}"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+
+}
+
+#fulldomain txtvalue
+dns_njalla_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ NJALLA_Token="${NJALLA_Token:-$(_readaccountconf_mutable NJALLA_Token)}"
+
+ if [ "$NJALLA_Token" ]; then
+ _saveaccountconf_mutable NJALLA_Token "$NJALLA_Token"
+ else
+ NJALLA_Token=""
+ _err "You didn't specify a Njalla api token yet."
+ 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 records for domain"
+ if ! _njalla_rest "{\"method\":\"list-records\",\"params\":{\"domain\":\"${_domain}\"}}"; then
+ return 1
+ fi
+
+ if ! echo "$response" | tr -d " " | grep "\"id\":" >/dev/null; then
+ _err "Error: $response"
+ return 1
+ fi
+
+ records=$(echo "$response" | _egrep_o "\"records\":\s?\[(.*)\]\}" | _egrep_o "\[.*\]" | _egrep_o "\{[^\{\}]*\"id\":[^\{\}]*\}")
+ count=$(echo "$records" | wc -l)
+ _debug count "$count"
+
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ echo "$records" | while read -r record; do
+ record_name=$(echo "$record" | _egrep_o "\"name\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
+ record_content=$(echo "$record" | _egrep_o "\"content\":\s?\"[^\"]*\"" | cut -d : -f 2 | tr -d " " | tr -d \")
+ record_id=$(echo "$record" | _egrep_o "\"id\":\s?[0-9]+" | cut -d : -f 2 | tr -d " " | tr -d \")
+ if [ "$_sub_domain" = "$record_name" ]; then
+ if [ "$txtvalue" = "$record_content" ]; then
+ _debug "record_id" "$record_id"
+ if ! _njalla_rest "{\"method\":\"remove-record\",\"params\":{\"domain\":\"${_domain}\",\"id\":${record_id}}}"; then
+ _err "Delete record error."
+ return 1
+ fi
+ echo "$response" | tr -d " " | grep "\"result\"" >/dev/null
+ fi
+ fi
+ done
+ 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
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _njalla_rest "{\"method\":\"get-domain\",\"params\":{\"domain\":\"${h}\"}}"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"$h\""; then
+ _domain_returned=$(echo "$response" | _egrep_o "\{\"name\": *\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
+ if [ "$_domain_returned" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_njalla_rest() {
+ data="$1"
+
+ token_trimmed=$(echo "$NJALLA_Token" | tr -d '"')
+
+ export _H1="Content-Type: application/json"
+ export _H2="Accept: application/json"
+ export _H3="Authorization: Njalla $token_trimmed"
+
+ _debug data "$data"
+ response="$(_post "$data" "$NJALLA_Api" "" "POST")"
+
+ if [ "$?" != "0" ]; then
+ _err "error $data"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_nm.sh b/acme.sh-master/dnsapi/dns_nm.sh
new file mode 100644
index 0000000..4dfcc77
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nm.sh
@@ -0,0 +1,88 @@
+#!/usr/bin/env sh
+
+########################################################################
+# https://namemaster.de hook script for acme.sh
+#
+# Environment variables:
+#
+# - $NM_user (your namemaster.de API username)
+# - $NM_sha256 (your namemaster.de API password_as_sha256hash)
+#
+# Author: Thilo Gass
+# Git repo: https://github.com/ThiloGa/acme.sh
+
+#-- dns_nm_add() - Add TXT record --------------------------------------
+# Usage: dns_nm_add _acme-challenge.subdomain.domain.com "XyZ123..."
+
+namemaster_api="https://namemaster.de/api/api.php"
+
+dns_nm_add() {
+ fulldomain=$1
+ txt_value=$2
+ _info "Using DNS-01 namemaster hook"
+
+ NM_user="${NM_user:-$(_readaccountconf_mutable NM_user)}"
+ NM_sha256="${NM_sha256:-$(_readaccountconf_mutable NM_sha256)}"
+ if [ -z "$NM_user" ] || [ -z "$NM_sha256" ]; then
+ NM_user=""
+ NM_sha256=""
+ _err "No auth details provided. Please set user credentials using the \$NM_user and \$NM_sha256 environment variables."
+ return 1
+ fi
+ #save the api user and sha256 password to the account conf file.
+ _debug "Save user and hash"
+ _saveaccountconf_mutable NM_user "$NM_user"
+ _saveaccountconf_mutable NM_sha256 "$NM_sha256"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain" "$fulldomain"
+ return 1
+ fi
+
+ _info "die Zone lautet:" "$zone"
+
+ get="$namemaster_api?User=$NM_user&Password=$NM_sha256&Antwort=csv&Typ=ACME&zone=$zone&hostname=$fulldomain&TXT=$txt_value&Action=Auto&Lifetime=3600"
+
+ if ! erg="$(_get "$get")"; then
+ _err "error Adding $fulldomain TXT: $txt_value"
+ return 1
+ fi
+
+ if _contains "$erg" "Success"; then
+ _info "Success, TXT Added, OK"
+ else
+ _err "error Adding $fulldomain TXT: $txt_value erg: $erg"
+ return 1
+ fi
+
+ _debug "ok Auto $fulldomain TXT: $txt_value erg: $erg"
+ return 0
+}
+
+dns_nm_rm() {
+
+ fulldomain=$1
+ txtvalue=$2
+ _info "TXT enrty in $fulldomain is deleted automatically"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+}
+
+_get_root() {
+
+ domain=$1
+
+ get="$namemaster_api?User=$NM_user&Password=$NM_sha256&Typ=acme&hostname=$domain&Action=getzone&antwort=csv"
+
+ if ! zone="$(_get "$get")"; then
+ _err "error getting Zone"
+ return 1
+ else
+ if _contains "$zone" "hostname not found"; then
+ return 1
+ fi
+ fi
+
+}
diff --git a/acme.sh-master/dnsapi/dns_nsd.sh b/acme.sh-master/dnsapi/dns_nsd.sh
new file mode 100644
index 0000000..0d29a48
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nsd.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env sh
+
+#Nsd_ZoneFile="/etc/nsd/zones/example.com.zone"
+#Nsd_Command="sudo nsd-control reload"
+
+# args: fulldomain txtvalue
+dns_nsd_add() {
+ fulldomain=$1
+ txtvalue=$2
+ ttlvalue=300
+
+ Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}"
+ Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}"
+
+ # Arg checks
+ if [ -z "$Nsd_ZoneFile" ] || [ -z "$Nsd_Command" ]; then
+ Nsd_ZoneFile=""
+ Nsd_Command=""
+ _err "Specify ENV vars Nsd_ZoneFile and Nsd_Command"
+ return 1
+ fi
+
+ if [ ! -f "$Nsd_ZoneFile" ]; then
+ Nsd_ZoneFile=""
+ Nsd_Command=""
+ _err "No such file: $Nsd_ZoneFile"
+ return 1
+ fi
+
+ _savedomainconf Nsd_ZoneFile "$Nsd_ZoneFile"
+ _savedomainconf Nsd_Command "$Nsd_Command"
+
+ echo "$fulldomain. $ttlvalue IN TXT \"$txtvalue\"" >>"$Nsd_ZoneFile"
+ _info "Added TXT record for $fulldomain"
+ _debug "Running $Nsd_Command"
+ if eval "$Nsd_Command"; then
+ _info "Successfully updated the zone"
+ return 0
+ else
+ _err "Problem updating the zone"
+ return 1
+ fi
+}
+
+# args: fulldomain txtvalue
+dns_nsd_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ ttlvalue=300
+
+ Nsd_ZoneFile="${Nsd_ZoneFile:-$(_readdomainconf Nsd_ZoneFile)}"
+ Nsd_Command="${Nsd_Command:-$(_readdomainconf Nsd_Command)}"
+
+ _sed_i "/$fulldomain. $ttlvalue IN TXT \"$txtvalue\"/d" "$Nsd_ZoneFile"
+ _info "Removed TXT record for $fulldomain"
+ _debug "Running $Nsd_Command"
+ if eval "$Nsd_Command"; then
+ _info "Successfully reloaded NSD "
+ return 0
+ else
+ _err "Problem reloading NSD"
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_nsone.sh b/acme.sh-master/dnsapi/dns_nsone.sh
new file mode 100644
index 0000000..9a99834
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nsone.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+#
+#NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+NS1_Api="https://api.nsone.net/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nsone_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$NS1_Key" ]; then
+ NS1_Key=""
+ _err "You didn't specify nsone dns api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf NS1_Key "$NS1_Key"
+
+ _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"
+ _nsone_rest GET "zones/${_domain}"
+
+ if ! _contains "$response" "\"records\":"; then
+ _err "Error"
+ return 1
+ fi
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Adding record"
+
+ if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\",\"ttl\":0}"; then
+ if _contains "$response" "$fulldomain"; then
+ _info "Added"
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ else
+ _info "Updating record"
+ prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1)
+ _debug "prev_txt" "$prev_txt"
+
+ _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\",\"ttl\":0}"
+ if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then
+ _info "Updated!"
+ #todo: check if the record takes effect
+ return 0
+ fi
+ _err "Update error"
+ return 1
+ fi
+
+}
+
+#fulldomain
+dns_nsone_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _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"
+ _nsone_rest GET "zones/${_domain}/$fulldomain/TXT"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",.*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ if ! _nsone_rest DELETE "zones/${_domain}/$fulldomain/TXT"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" ""
+ 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=2
+ p=1
+ if ! _nsone_rest GET "zones"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"zone\":\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_nsone_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="X-NSONE-Key: $NS1_Key"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$NS1_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$NS1_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_nsupdate.sh b/acme.sh-master/dnsapi/dns_nsupdate.sh
new file mode 100644
index 0000000..cd4b714
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_nsupdate.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env sh
+
+######## Public functions #####################
+
+#Usage: dns_nsupdate_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nsupdate_add() {
+ fulldomain=$1
+ txtvalue=$2
+ NSUPDATE_SERVER="${NSUPDATE_SERVER:-$(_readaccountconf_mutable NSUPDATE_SERVER)}"
+ NSUPDATE_SERVER_PORT="${NSUPDATE_SERVER_PORT:-$(_readaccountconf_mutable NSUPDATE_SERVER_PORT)}"
+ NSUPDATE_KEY="${NSUPDATE_KEY:-$(_readaccountconf_mutable NSUPDATE_KEY)}"
+ NSUPDATE_ZONE="${NSUPDATE_ZONE:-$(_readaccountconf_mutable NSUPDATE_ZONE)}"
+
+ _checkKeyFile || return 1
+
+ # save the dns server and key to the account conf file.
+ _saveaccountconf_mutable NSUPDATE_SERVER "${NSUPDATE_SERVER}"
+ _saveaccountconf_mutable NSUPDATE_SERVER_PORT "${NSUPDATE_SERVER_PORT}"
+ _saveaccountconf_mutable NSUPDATE_KEY "${NSUPDATE_KEY}"
+ _saveaccountconf_mutable NSUPDATE_ZONE "${NSUPDATE_ZONE}"
+
+ [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
+ [ -n "${NSUPDATE_SERVER_PORT}" ] || NSUPDATE_SERVER_PORT=53
+
+ _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
+ [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_1" ] && nsdebug="-d"
+ [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D"
+ if [ -z "${NSUPDATE_ZONE}" ]; then
+ nsupdate -k "${NSUPDATE_KEY}" $nsdebug <
+
+NW_API_VERSION="0"
+
+# dns_nw_add() - Add TXT record
+# Usage: dns_nw_add _acme-challenge.subdomain.domain.com "XyZ123..."
+dns_nw_add() {
+ host="${1}"
+ txtvalue="${2}"
+
+ _debug host "${host}"
+ _debug txtvalue "${txtvalue}"
+
+ if ! _check_nw_api_creds; then
+ return 1
+ fi
+
+ _info "Using NocWorx (${NW_API_ENDPOINT})"
+ _debug "Calling: dns_nw_add() '${host}' '${txtvalue}'"
+
+ _debug "Detecting root zone"
+ if ! _get_root "${host}"; then
+ _err "Zone for domain does not exist."
+ return 1
+ fi
+ _debug _zone_id "${_zone_id}"
+ _debug _sub_domain "${_sub_domain}"
+ _debug _domain "${_domain}"
+
+ _post_data="{\"zone_id\": \"${_zone_id}\", \"type\": \"TXT\", \"host\": \"${host}\", \"target\": \"${txtvalue}\", \"ttl\": \"300\"}"
+
+ if _rest POST "dns-record" "${_post_data}" && [ -n "${response}" ]; then
+ _record_id=$(printf "%s\n" "${response}" | _egrep_o "\"record_id\": *[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _record_id "${_record_id}"
+
+ if [ -z "$_record_id" ]; then
+ _err "Error adding the TXT record."
+ return 1
+ fi
+
+ _info "TXT record successfully added."
+ return 0
+ fi
+
+ return 1
+}
+
+# dns_nw_rm() - Remove TXT record
+# Usage: dns_nw_rm _acme-challenge.subdomain.domain.com "XyZ123..."
+dns_nw_rm() {
+ host="${1}"
+ txtvalue="${2}"
+
+ _debug host "${host}"
+ _debug txtvalue "${txtvalue}"
+
+ if ! _check_nw_api_creds; then
+ return 1
+ fi
+
+ _info "Using NocWorx (${NW_API_ENDPOINT})"
+ _debug "Calling: dns_nw_rm() '${host}'"
+
+ _debug "Detecting root zone"
+ if ! _get_root "${host}"; then
+ _err "Zone for domain does not exist."
+ return 1
+ fi
+ _debug _zone_id "${_zone_id}"
+ _debug _sub_domain "${_sub_domain}"
+ _debug _domain "${_domain}"
+
+ _parameters="?zone_id=${_zone_id}"
+
+ if _rest GET "dns-record" "${_parameters}" && [ -n "${response}" ]; then
+ response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"record_id":/|"record_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
+ _debug response "${response}"
+
+ record="$(echo "${response}" | _egrep_o "{.*\"host\": *\"${_sub_domain}\", *\"target\": *\"${txtvalue}\".*}")"
+ _debug record "${record}"
+
+ if [ "${record}" ]; then
+ _record_id=$(printf "%s\n" "${record}" | _egrep_o "\"record_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "${_record_id}" ]; then
+ _debug _record_id "${_record_id}"
+
+ _rest DELETE "dns-record/${_record_id}"
+
+ _info "TXT record successfully deleted."
+ return 0
+ fi
+
+ return 1
+ fi
+
+ return 0
+ fi
+
+ return 1
+}
+
+_check_nw_api_creds() {
+ NW_API_TOKEN="${NW_API_TOKEN:-$(_readaccountconf_mutable NW_API_TOKEN)}"
+ NW_API_ENDPOINT="${NW_API_ENDPOINT:-$(_readaccountconf_mutable NW_API_ENDPOINT)}"
+
+ if [ -z "${NW_API_ENDPOINT}" ]; then
+ NW_API_ENDPOINT="https://portal.nexcess.net"
+ fi
+
+ if [ -z "${NW_API_TOKEN}" ]; then
+ _err "You have not defined your NW_API_TOKEN."
+ _err "Please create your token and try again."
+ _err "If you need to generate a new token, please visit one of the following URLs:"
+ _err " - https://portal.nexcess.net/api-token"
+ _err " - https://core.thermo.io/api-token"
+ _err " - https://my.futurehosting.com/api-token"
+
+ return 1
+ fi
+
+ _saveaccountconf_mutable NW_API_TOKEN "${NW_API_TOKEN}"
+ _saveaccountconf_mutable NW_API_ENDPOINT "${NW_API_ENDPOINT}"
+}
+
+_get_root() {
+ domain="${1}"
+ i=2
+ p=1
+
+ if _rest GET "dns-zone"; then
+ response="$(echo "${response}" | tr -d "\n" | sed 's/^\[\(.*\)\]$/\1/' | sed -e 's/{"zone_id":/|"zone_id":/g' | sed 's/|/&{/g' | tr "|" "\n")"
+
+ _debug response "${response}"
+ while true; do
+ h=$(printf "%s" "${domain}" | cut -d . -f $i-100)
+ _debug h "${h}"
+ if [ -z "${h}" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "${response}" | _egrep_o "{.*\"domain\": *\"${h}\".*}")"
+ if [ "${hostedzone}" ]; then
+ _zone_id=$(printf "%s\n" "${hostedzone}" | _egrep_o "\"zone_id\": *[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "${_zone_id}" ]; then
+ _sub_domain=$(printf "%s" "${domain}" | cut -d . -f 1-${p})
+ _domain="${h}"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "${i}" + 1)
+ done
+ fi
+ return 1
+}
+
+_rest() {
+ method="${1}"
+ ep="/${2}"
+ data="${3}"
+
+ _debug method "${method}"
+ _debug ep "${ep}"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+ export _H3="Api-Version: ${NW_API_VERSION}"
+ export _H4="User-Agent: NW-ACME-CLIENT"
+ export _H5="Authorization: Bearer ${NW_API_TOKEN}"
+
+ if [ "${method}" != "GET" ]; then
+ _debug data "${data}"
+ response="$(_post "${data}" "${NW_API_ENDPOINT}${ep}" "" "${method}")"
+ else
+ response="$(_get "${NW_API_ENDPOINT}${ep}${data}")"
+ fi
+
+ if [ "${?}" != "0" ]; then
+ _err "error ${ep}"
+ return 1
+ fi
+ _debug2 response "${response}"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_oci.sh b/acme.sh-master/dnsapi/dns_oci.sh
new file mode 100644
index 0000000..3b81143
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_oci.sh
@@ -0,0 +1,325 @@
+#!/usr/bin/env sh
+#
+# Acme.sh DNS API plugin for Oracle Cloud Infrastructure
+# Copyright (c) 2021, Oracle and/or its affiliates
+#
+# The plugin will automatically use the default profile from an OCI SDK and CLI
+# configuration file, if it exists.
+#
+# Alternatively, set the following environment variables:
+# - OCI_CLI_TENANCY : OCID of tenancy that contains the target DNS zone
+# - OCI_CLI_USER : OCID of user with permission to add/remove records from zones
+# - OCI_CLI_REGION : Should point to the tenancy home region
+#
+# One of the following two variables is required:
+# - OCI_CLI_KEY_FILE: Path to private API signing key file in PEM format; or
+# - OCI_CLI_KEY : The private API signing key in PEM format
+#
+# NOTE: using an encrypted private key that needs a passphrase is not supported.
+#
+
+dns_oci_add() {
+ _fqdn="$1"
+ _rdata="$2"
+
+ if _get_oci_zone; then
+
+ _add_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"ttl\": 30,\"operation\":\"ADD\"}]}"
+ response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_add_record_body")
+ if [ "$response" ]; then
+ _info "Success: added TXT record for ${_sub_domain}.${_domain}."
+ else
+ _err "Error: failed to add TXT record for ${_sub_domain}.${_domain}."
+ _err "Check that the user has permission to add records to this zone."
+ return 1
+ fi
+
+ else
+ return 1
+ fi
+
+}
+
+dns_oci_rm() {
+ _fqdn="$1"
+ _rdata="$2"
+
+ if _get_oci_zone; then
+
+ _remove_record_body="{\"items\":[{\"domain\":\"${_sub_domain}.${_domain}\",\"rdata\":\"$_rdata\",\"rtype\":\"TXT\",\"operation\":\"REMOVE\"}]}"
+ response=$(_signed_request "PATCH" "/20180115/zones/${_domain}/records" "$_remove_record_body")
+ if [ "$response" ]; then
+ _info "Success: removed TXT record for ${_sub_domain}.${_domain}."
+ else
+ _err "Error: failed to remove TXT record for ${_sub_domain}.${_domain}."
+ _err "Check that the user has permission to remove records from this zone."
+ return 1
+ fi
+
+ else
+ return 1
+ fi
+
+}
+
+#################### Private functions below ##################################
+_get_oci_zone() {
+
+ if ! _oci_config; then
+ return 1
+ fi
+
+ if ! _get_zone "$_fqdn"; then
+ _err "Error: DNS Zone not found for $_fqdn in $OCI_CLI_TENANCY"
+ return 1
+ fi
+
+ return 0
+
+}
+
+_oci_config() {
+
+ _DEFAULT_OCI_CLI_CONFIG_FILE="$HOME/.oci/config"
+ OCI_CLI_CONFIG_FILE="${OCI_CLI_CONFIG_FILE:-$(_readaccountconf_mutable OCI_CLI_CONFIG_FILE)}"
+
+ if [ -z "$OCI_CLI_CONFIG_FILE" ]; then
+ OCI_CLI_CONFIG_FILE="$_DEFAULT_OCI_CLI_CONFIG_FILE"
+ fi
+
+ if [ "$_DEFAULT_OCI_CLI_CONFIG_FILE" != "$OCI_CLI_CONFIG_FILE" ]; then
+ _saveaccountconf_mutable OCI_CLI_CONFIG_FILE "$OCI_CLI_CONFIG_FILE"
+ else
+ _clearaccountconf_mutable OCI_CLI_CONFIG_FILE
+ fi
+
+ _DEFAULT_OCI_CLI_PROFILE="DEFAULT"
+ OCI_CLI_PROFILE="${OCI_CLI_PROFILE:-$(_readaccountconf_mutable OCI_CLI_PROFILE)}"
+ if [ "$_DEFAULT_OCI_CLI_PROFILE" != "$OCI_CLI_PROFILE" ]; then
+ _saveaccountconf_mutable OCI_CLI_PROFILE "$OCI_CLI_PROFILE"
+ else
+ OCI_CLI_PROFILE="$_DEFAULT_OCI_CLI_PROFILE"
+ _clearaccountconf_mutable OCI_CLI_PROFILE
+ fi
+
+ OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readaccountconf_mutable OCI_CLI_TENANCY)}"
+ if [ "$OCI_CLI_TENANCY" ]; then
+ _saveaccountconf_mutable OCI_CLI_TENANCY "$OCI_CLI_TENANCY"
+ elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
+ _debug "Reading OCI_CLI_TENANCY value from: $OCI_CLI_CONFIG_FILE"
+ OCI_CLI_TENANCY="${OCI_CLI_TENANCY:-$(_readini "$OCI_CLI_CONFIG_FILE" tenancy "$OCI_CLI_PROFILE")}"
+ fi
+
+ if [ -z "$OCI_CLI_TENANCY" ]; then
+ _err "Error: unable to read OCI_CLI_TENANCY from config file or environment variable."
+ return 1
+ fi
+
+ OCI_CLI_USER="${OCI_CLI_USER:-$(_readaccountconf_mutable OCI_CLI_USER)}"
+ if [ "$OCI_CLI_USER" ]; then
+ _saveaccountconf_mutable OCI_CLI_USER "$OCI_CLI_USER"
+ elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
+ _debug "Reading OCI_CLI_USER value from: $OCI_CLI_CONFIG_FILE"
+ OCI_CLI_USER="${OCI_CLI_USER:-$(_readini "$OCI_CLI_CONFIG_FILE" user "$OCI_CLI_PROFILE")}"
+ fi
+ if [ -z "$OCI_CLI_USER" ]; then
+ _err "Error: unable to read OCI_CLI_USER from config file or environment variable."
+ return 1
+ fi
+
+ OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readaccountconf_mutable OCI_CLI_REGION)}"
+ if [ "$OCI_CLI_REGION" ]; then
+ _saveaccountconf_mutable OCI_CLI_REGION "$OCI_CLI_REGION"
+ elif [ -f "$OCI_CLI_CONFIG_FILE" ]; then
+ _debug "Reading OCI_CLI_REGION value from: $OCI_CLI_CONFIG_FILE"
+ OCI_CLI_REGION="${OCI_CLI_REGION:-$(_readini "$OCI_CLI_CONFIG_FILE" region "$OCI_CLI_PROFILE")}"
+ fi
+ if [ -z "$OCI_CLI_REGION" ]; then
+ _err "Error: unable to read OCI_CLI_REGION from config file or environment variable."
+ return 1
+ fi
+
+ OCI_CLI_KEY="${OCI_CLI_KEY:-$(_readaccountconf_mutable OCI_CLI_KEY)}"
+ if [ -z "$OCI_CLI_KEY" ]; then
+ _clearaccountconf_mutable OCI_CLI_KEY
+ OCI_CLI_KEY_FILE="${OCI_CLI_KEY_FILE:-$(_readini "$OCI_CLI_CONFIG_FILE" key_file "$OCI_CLI_PROFILE")}"
+ if [ "$OCI_CLI_KEY_FILE" ] && [ -f "$OCI_CLI_KEY_FILE" ]; then
+ _debug "Reading OCI_CLI_KEY value from: $OCI_CLI_KEY_FILE"
+ OCI_CLI_KEY=$(_base64 <"$OCI_CLI_KEY_FILE")
+ _saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
+ fi
+ else
+ _saveaccountconf_mutable OCI_CLI_KEY "$OCI_CLI_KEY"
+ fi
+
+ if [ -z "$OCI_CLI_KEY_FILE" ] && [ -z "$OCI_CLI_KEY" ]; then
+ _err "Error: unable to find key file path in OCI config file or OCI_CLI_KEY_FILE."
+ _err "Error: unable to load private API signing key from OCI_CLI_KEY."
+ return 1
+ fi
+
+ if [ "$(printf "%s\n" "$OCI_CLI_KEY" | wc -l)" -eq 1 ]; then
+ OCI_CLI_KEY=$(printf "%s" "$OCI_CLI_KEY" | _dbase64)
+ fi
+
+ return 0
+
+}
+
+# _get_zone(): retrieves the Zone name and OCID
+#
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_ociid=ocid1.dns-zone.oc1..
+_get_zone() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ # not valid
+ return 1
+ fi
+
+ _domain_id=$(_signed_request "GET" "/20180115/zones/$h" "" "id")
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+
+}
+
+#Usage: privatekey
+#Output MD5 fingerprint
+_fingerprint() {
+
+ pkey="$1"
+ if [ -z "$pkey" ]; then
+ _usage "Usage: _fingerprint privkey"
+ return 1
+ fi
+
+ printf "%s" "$pkey" | ${ACME_OPENSSL_BIN:-openssl} rsa -pubout -outform DER 2>/dev/null | ${ACME_OPENSSL_BIN:-openssl} md5 -c | cut -d = -f 2 | tr -d ' '
+
+}
+
+_signed_request() {
+
+ _sig_method="$1"
+ _sig_target="$2"
+ _sig_body="$3"
+ _return_field="$4"
+
+ _key_fingerprint=$(_fingerprint "$OCI_CLI_KEY")
+ _sig_host="dns.$OCI_CLI_REGION.oraclecloud.com"
+ _sig_keyId="$OCI_CLI_TENANCY/$OCI_CLI_USER/$_key_fingerprint"
+ _sig_alg="rsa-sha256"
+ _sig_version="1"
+ _sig_now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")"
+
+ _request_method=$(printf %s "$_sig_method" | _lower_case)
+ _curl_method=$(printf %s "$_sig_method" | _upper_case)
+
+ _request_target="(request-target): $_request_method $_sig_target"
+ _date_header="date: $_sig_now"
+ _host_header="host: $_sig_host"
+
+ _string_to_sign="$_request_target\n$_date_header\n$_host_header"
+ _sig_headers="(request-target) date host"
+
+ if [ "$_sig_body" ]; then
+ _secure_debug3 _sig_body "$_sig_body"
+ _sig_body_sha256="x-content-sha256: $(printf %s "$_sig_body" | _digest sha256)"
+ _sig_body_type="content-type: application/json"
+ _sig_body_length="content-length: ${#_sig_body}"
+ _string_to_sign="$_string_to_sign\n$_sig_body_sha256\n$_sig_body_type\n$_sig_body_length"
+ _sig_headers="$_sig_headers x-content-sha256 content-type content-length"
+ fi
+
+ _tmp_file=$(_mktemp)
+ if [ -f "$_tmp_file" ]; then
+ printf '%s' "$OCI_CLI_KEY" >"$_tmp_file"
+ _signature=$(printf '%b' "$_string_to_sign" | _sign "$_tmp_file" sha256 | tr -d '\r\n')
+ rm -f "$_tmp_file"
+ fi
+
+ _signed_header="Authorization: Signature version=\"$_sig_version\",keyId=\"$_sig_keyId\",algorithm=\"$_sig_alg\",headers=\"$_sig_headers\",signature=\"$_signature\""
+ _secure_debug3 _signed_header "$_signed_header"
+
+ if [ "$_curl_method" = "GET" ]; then
+ export _H1="$_date_header"
+ export _H2="$_signed_header"
+ _response="$(_get "https://${_sig_host}${_sig_target}")"
+ elif [ "$_curl_method" = "PATCH" ]; then
+ export _H1="$_date_header"
+ # shellcheck disable=SC2090
+ export _H2="$_sig_body_sha256"
+ export _H3="$_sig_body_type"
+ export _H4="$_sig_body_length"
+ export _H5="$_signed_header"
+ _response="$(_post "$_sig_body" "https://${_sig_host}${_sig_target}" "" "PATCH")"
+ else
+ _err "Unable to process method: $_curl_method."
+ fi
+
+ _ret="$?"
+ if [ "$_return_field" ]; then
+ _response="$(echo "$_response" | sed 's/\\\"//g'))"
+ _return=$(echo "${_response}" | _egrep_o "\"$_return_field\"\\s*:\\s*\"[^\"]*\"" | _head_n 1 | cut -d : -f 2 | tr -d "\"")
+ else
+ _return="$_response"
+ fi
+
+ printf "%s" "$_return"
+ return $_ret
+
+}
+
+# file key [section]
+_readini() {
+ _file="$1"
+ _key="$2"
+ _section="${3:-DEFAULT}"
+
+ _start_n=$(grep -n '\['"$_section"']' "$_file" | cut -d : -f 1)
+ _debug3 _start_n "$_start_n"
+ if [ -z "$_start_n" ]; then
+ _err "Can not find section: $_section"
+ return 1
+ fi
+
+ _start_nn=$(_math "$_start_n" + 1)
+ _debug3 "_start_nn" "$_start_nn"
+
+ _left="$(sed -n "${_start_nn},99999p" "$_file")"
+ _debug3 _left "$_left"
+ _end="$(echo "$_left" | grep -n "^\[" | _head_n 1)"
+ _debug3 "_end" "$_end"
+ if [ "$_end" ]; then
+ _end_n=$(echo "$_end" | cut -d : -f 1)
+ _debug3 "_end_n" "$_end_n"
+ _seg_n=$(echo "$_left" | sed -n "1,${_end_n}p")
+ else
+ _seg_n="$_left"
+ fi
+
+ _debug3 "_seg_n" "$_seg_n"
+ _lineini="$(echo "$_seg_n" | grep "^ *$_key *= *")"
+ _inivalue="$(printf "%b" "$(eval "echo $_lineini | sed \"s/^ *${_key} *= *//g\"")")"
+ _debug2 _inivalue "$_inivalue"
+ echo "$_inivalue"
+
+}
diff --git a/acme.sh-master/dnsapi/dns_one.sh b/acme.sh-master/dnsapi/dns_one.sh
new file mode 100644
index 0000000..1565b76
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_one.sh
@@ -0,0 +1,227 @@
+#!/usr/bin/env sh
+# one.com ui wrapper for acme.sh
+
+#
+# export ONECOM_User="username"
+# export ONECOM_Password="password"
+
+dns_one_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _dns_one_login; then
+ _err "login failed"
+ return 1
+ fi
+
+ _debug "detect the root domain"
+ if ! _get_root "$fulldomain"; then
+ _err "root domain not found"
+ return 1
+ fi
+
+ subdomain="${_sub_domain}"
+ maindomain=${_domain}
+
+ _debug subdomain "$subdomain"
+ _debug maindomain "$maindomain"
+
+ #Check if the TXT exists
+ _dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
+ if [ -n "$id" ]; then
+ _info "$(__green "Txt record with the same value found. Skip adding.")"
+ return 0
+ fi
+
+ _dns_one_addrecord "TXT" "$subdomain" "$txtvalue"
+ if [ -z "$id" ]; then
+ _err "Add TXT record error."
+ return 1
+ else
+ _info "$(__green "Added, OK ($id)")"
+ return 0
+ fi
+}
+
+dns_one_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _dns_one_login; then
+ _err "login failed"
+ return 1
+ fi
+
+ _debug "detect the root domain"
+ if ! _get_root "$fulldomain"; then
+ _err "root domain not found"
+ return 1
+ fi
+
+ subdomain="${_sub_domain}"
+ maindomain=${_domain}
+
+ _debug subdomain "$subdomain"
+ _debug maindomain "$maindomain"
+
+ #Check if the TXT exists
+ _dns_one_getrecord "TXT" "$subdomain" "$txtvalue"
+ if [ -z "$id" ]; then
+ _err "Txt record not found."
+ return 1
+ fi
+
+ # delete entry
+ if _dns_one_delrecord "$id"; then
+ _info "$(__green Removed, OK)"
+ return 0
+ else
+ _err "Removing txt record error."
+ return 1
+ fi
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ response="$(_get "https://www.one.com/admin/api/domains/$h/dns/custom_records")"
+
+ if ! _contains "$response" "CRMRST_000302"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Unable to parse this domain"
+ return 1
+}
+
+_dns_one_login() {
+
+ # get credentials
+ ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}"
+ ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}"
+ if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then
+ ONECOM_User=""
+ ONECOM_Password=""
+ _err "You didn't specify a one.com username and password yet."
+ _err "Please create the key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable ONECOM_User "$ONECOM_User"
+ _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password"
+
+ # Login with user and password
+ postdata="loginDomain=true"
+ postdata="$postdata&displayUsername=$ONECOM_User"
+ postdata="$postdata&username=$ONECOM_User"
+ postdata="$postdata&targetDomain="
+ postdata="$postdata&password1=$ONECOM_Password"
+ postdata="$postdata&loginTarget="
+ #_debug postdata "$postdata"
+
+ response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")"
+ #_debug response "$response"
+
+ # Get SessionID
+ JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _head_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')"
+ _debug jsessionid "$JSESSIONID"
+
+ if [ -z "$JSESSIONID" ]; then
+ _err "error sessionid cookie not found"
+ return 1
+ fi
+
+ export _H1="Cookie: ${JSESSIONID}"
+
+ return 0
+}
+
+_dns_one_getrecord() {
+ type="$1"
+ name="$2"
+ value="$3"
+ if [ -z "$type" ]; then
+ type="TXT"
+ fi
+ if [ -z "$name" ]; then
+ _err "Record name is empty."
+ return 1
+ fi
+
+ response="$(_get "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ if [ -z "${value}" ]; then
+ id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"[^\"]*\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ response=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"[^\"]*\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"\([^\"]*\)\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ else
+ id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"${name}\",\"type\":\"${type}\",\"content\":\"${value}\",\"priority\":0,\"ttl\":600}.*/\1/p")
+ fi
+ if [ -z "$id" ]; then
+ return 1
+ fi
+ return 0
+}
+
+_dns_one_addrecord() {
+ type="$1"
+ name="$2"
+ value="$3"
+ if [ -z "$type" ]; then
+ type="TXT"
+ fi
+ if [ -z "$name" ]; then
+ _err "Record name is empty."
+ return 1
+ fi
+
+ postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"${type}\",\"prefix\":\"${name}\",\"content\":\"${value}\"}}"
+ _debug postdata "$postdata"
+ response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records" "" "POST" "application/json")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$subdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p")
+
+ if [ -z "$id" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+_dns_one_delrecord() {
+ id="$1"
+ if [ -z "$id" ]; then
+ return 1
+ fi
+
+ response="$(_post "" "https://www.one.com/admin/api/domains/$maindomain/dns/custom_records/$id" "" "DELETE" "application/json")"
+ response="$(echo "$response" | _normalizeJson)"
+ _debug response "$response"
+
+ if [ "$response" = '{"result":null,"metadata":null}' ]; then
+ return 0
+ else
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_online.sh b/acme.sh-master/dnsapi/dns_online.sh
new file mode 100644
index 0000000..9158c26
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_online.sh
@@ -0,0 +1,217 @@
+#!/usr/bin/env sh
+
+# Online API
+# https://console.online.net/en/api/
+#
+# Requires Online API key set in ONLINE_API_KEY
+
+######## Public functions #####################
+
+ONLINE_API="https://api.online.net/api/v1"
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_online_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _online_check_config; 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 _real_dns_version "$_real_dns_version"
+
+ _info "Creating temporary zone version"
+ _online_create_temporary_zone_version
+ _info "Enabling temporary zone version"
+ _online_enable_zone "$_temporary_dns_version"
+
+ _info "Adding record"
+ _online_create_TXT_record "$_real_dns_version" "$_sub_domain" "$txtvalue"
+ _info "Disabling temporary version"
+ _online_enable_zone "$_real_dns_version"
+ _info "Destroying temporary version"
+ _online_destroy_zone "$_temporary_dns_version"
+
+ _info "Record added."
+ return 0
+}
+
+#fulldomain
+dns_online_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _online_check_config; 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 _real_dns_version "$_real_dns_version"
+
+ _debug "Getting txt records"
+ if ! _online_rest GET "domain/$_domain/version/active"; then
+ return 1
+ fi
+
+ rid=$(echo "$response" | _egrep_o "\"id\":[0-9]+,\"name\":\"$_sub_domain\",\"data\":\"\\\u0022$txtvalue\\\u0022\"" | cut -d ':' -f 2 | cut -d ',' -f 1)
+ _debug rid "$rid"
+ if [ -z "$rid" ]; then
+ return 1
+ fi
+
+ _info "Creating temporary zone version"
+ _online_create_temporary_zone_version
+ _info "Enabling temporary zone version"
+ _online_enable_zone "$_temporary_dns_version"
+
+ _info "Removing DNS record"
+ _online_rest DELETE "domain/$_domain/version/$_real_dns_version/zone/$rid"
+ _info "Disabling temporary version"
+ _online_enable_zone "$_real_dns_version"
+ _info "Destroying temporary version"
+ _online_destroy_zone "$_temporary_dns_version"
+
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_online_check_config() {
+ ONLINE_API_KEY="${ONLINE_API_KEY:-$(_readaccountconf_mutable ONLINE_API_KEY)}"
+ if [ -z "$ONLINE_API_KEY" ]; then
+ _err "No API key specified for Online API."
+ _err "Create your key and export it as ONLINE_API_KEY"
+ return 1
+ fi
+ if ! _online_rest GET "domain/"; then
+ _err "Invalid API key specified for Online API."
+ return 1
+ fi
+
+ _saveaccountconf_mutable ONLINE_API_KEY "$ONLINE_API_KEY"
+
+ return 0
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _online_rest GET "domain/$h/version/active"
+
+ if ! _contains "$response" "Domain not found" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ _real_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Unable to retrive DNS zone matching this domain"
+ return 1
+}
+
+# this function create a temporary zone version
+# as online.net does not allow updating an active version
+_online_create_temporary_zone_version() {
+
+ _online_rest POST "domain/$_domain/version" "name=acme.sh"
+ if [ "$?" != "0" ]; then
+ return 1
+ fi
+
+ _temporary_dns_version=$(echo "$response" | _egrep_o '"uuid_ref":.*' | cut -d ':' -f 2 | cut -d '"' -f 2)
+
+ # Creating a dummy record in this temporary version, because online.net doesn't accept enabling an empty version
+ _online_create_TXT_record "$_temporary_dns_version" "dummy.acme.sh" "dummy"
+
+ return 0
+}
+
+_online_destroy_zone() {
+ version_id=$1
+ _online_rest DELETE "domain/$_domain/version/$version_id"
+
+ if [ "$?" != "0" ]; then
+ return 1
+ fi
+ return 0
+}
+
+_online_enable_zone() {
+ version_id=$1
+ _online_rest PATCH "domain/$_domain/version/$version_id/enable"
+
+ if [ "$?" != "0" ]; then
+ return 1
+ fi
+ return 0
+}
+
+_online_create_TXT_record() {
+ version=$1
+ txt_name=$2
+ txt_value=$3
+
+ _online_rest POST "domain/$_domain/version/$version/zone" "type=TXT&name=$txt_name&data=%22$txt_value%22&ttl=60&priority=0"
+
+ # Note : the normal, expected response SHOULD be "Unknown method".
+ # this happens because the API HTTP response contains a Location: header, that redirect
+ # to an unknown online.net endpoint.
+ if [ "$?" != "0" ] || _contains "$response" "Unknown method" || _contains "$response" "\$ref"; then
+ return 0
+ else
+ _err "error $response"
+ return 1
+ fi
+}
+
+_online_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+ _online_url="$ONLINE_API/$ep"
+ _debug2 _online_url "$_online_url"
+ export _H1="Authorization: Bearer $ONLINE_API_KEY"
+ export _H2="X-Pretty-JSON: 1"
+ if [ "$data" ] || [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$_online_url" "" "$m")"
+ else
+ response="$(_get "$_online_url")"
+ fi
+ if [ "$?" != "0" ] || _contains "$response" "invalid_grant" || _contains "$response" "Method not allowed"; then
+ _err "error $response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_openprovider.sh b/acme.sh-master/dnsapi/dns_openprovider.sh
new file mode 100644
index 0000000..0a9e5ad
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_openprovider.sh
@@ -0,0 +1,247 @@
+#!/usr/bin/env sh
+
+# This is the OpenProvider API wrapper for acme.sh
+#
+# Author: Sylvia van Os
+# Report Bugs here: https://github.com/acmesh-official/acme.sh/issues/2104
+#
+# export OPENPROVIDER_USER="username"
+# export OPENPROVIDER_PASSWORDHASH="hashed_password"
+#
+# Usage:
+# acme.sh --issue --dns dns_openprovider -d example.com
+
+OPENPROVIDER_API="https://api.openprovider.eu/"
+#OPENPROVIDER_API="https://api.cte.openprovider.eu/" # Test API
+
+######## Public functions #####################
+
+#Usage: dns_openprovider_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_openprovider_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}"
+ OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}"
+
+ if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then
+ _err "You didn't specify the openprovider user and/or password hash."
+ return 1
+ fi
+
+ # save the username and password to the account conf file.
+ _saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER"
+ _saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_name "$_domain_name"
+ _debug _domain_extension "$_domain_extension"
+
+ _debug "Getting current records"
+ existing_items=""
+ results_retrieved=0
+ while true; do
+ _openprovider_request "$(printf '%s.%s%s' "$_domain_name" "$_domain_extension" "$results_retrieved")"
+
+ items="$response"
+ while true; do
+ item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(- .*<\/item>\).*/\1/p')"
+ _debug existing_items "$existing_items"
+ _debug results_retrieved "$results_retrieved"
+ _debug item "$item"
+
+ if [ -z "$item" ]; then
+ break
+ fi
+
+ tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
+ items="$(echo "$items" | sed "s|${tmpitem}||")"
+
+ results_retrieved="$(_math "$results_retrieved" + 1)"
+ new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
+ if [ -z "$new_item" ]; then
+ # Domain apex
+ new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
+ fi
+
+ if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
+ _debug "not an allowed record type, skipping" "$new_item"
+ continue
+ fi
+
+ existing_items="$existing_items$new_item"
+ done
+
+ total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')"
+
+ _debug total "$total"
+ if [ "$results_retrieved" -eq "$total" ]; then
+ break
+ fi
+ done
+
+ _debug "Creating acme record"
+ acme_record="$(echo "$fulldomain" | sed -e "s/.$_domain_name.$_domain_extension$//")"
+ _openprovider_request "$(printf '%s%smaster%s
- %sTXT%s600
' "$_domain_name" "$_domain_extension" "$existing_items" "$acme_record" "$txtvalue")"
+
+ return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_openprovider_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ OPENPROVIDER_USER="${OPENPROVIDER_USER:-$(_readaccountconf_mutable OPENPROVIDER_USER)}"
+ OPENPROVIDER_PASSWORDHASH="${OPENPROVIDER_PASSWORDHASH:-$(_readaccountconf_mutable OPENPROVIDER_PASSWORDHASH)}"
+
+ if [ -z "$OPENPROVIDER_USER" ] || [ -z "$OPENPROVIDER_PASSWORDHASH" ]; then
+ _err "You didn't specify the openprovider user and/or password hash."
+ return 1
+ fi
+
+ # save the username and password to the account conf file.
+ _saveaccountconf_mutable OPENPROVIDER_USER "$OPENPROVIDER_USER"
+ _saveaccountconf_mutable OPENPROVIDER_PASSWORDHASH "$OPENPROVIDER_PASSWORDHASH"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain_name "$_domain_name"
+ _debug _domain_extension "$_domain_extension"
+
+ _debug "Getting current records"
+ existing_items=""
+ results_retrieved=0
+ while true; do
+ _openprovider_request "$(printf '%s.%s%s' "$_domain_name" "$_domain_extension" "$results_retrieved")"
+
+ # Remove acme records from items
+ items="$response"
+ while true; do
+ item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(- .*<\/item>\).*/\1/p')"
+ _debug existing_items "$existing_items"
+ _debug results_retrieved "$results_retrieved"
+ _debug item "$item"
+
+ if [ -z "$item" ]; then
+ break
+ fi
+
+ tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
+ items="$(echo "$items" | sed "s|${tmpitem}||")"
+
+ results_retrieved="$(_math "$results_retrieved" + 1)"
+ if ! echo "$item" | grep -v "$fulldomain"; then
+ _debug "acme record, skipping" "$item"
+ continue
+ fi
+
+ new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)\.'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
+
+ if [ -z "$new_item" ]; then
+ # domain apex
+ new_item="$(echo "$item" | sed -n 's/.*
- .*\(\(.*\)'"$_domain_name"'\.'"$_domain_extension"'<\/name>.*\(.*<\/type>\).*\(.*<\/value>\).*\(.*<\/prio>\).*\(.*<\/ttl>\)\).*<\/item>.*/
- \2<\/name>\3\4\5\6<\/item>/p')"
+ fi
+
+ if [ -z "$(echo "$new_item" | _egrep_o ".*(A|AAAA|CNAME|MX|SPF|SRV|TXT|TLSA|SSHFP|CAA|NS)<\/type>.*")" ]; then
+ _debug "not an allowed record type, skipping" "$new_item"
+ continue
+ fi
+
+ existing_items="$existing_items$new_item"
+ done
+
+ total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')"
+
+ _debug total "$total"
+
+ if [ "$results_retrieved" -eq "$total" ]; then
+ break
+ fi
+ done
+
+ _debug "Removing acme record"
+ _openprovider_request "$(printf '%s%smaster%s' "$_domain_name" "$_domain_extension" "$existing_items")"
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain_name=domain
+# _domain_extension=com
+_get_root() {
+ domain=$1
+ i=2
+
+ results_retrieved=0
+ while true; do
+ h=$(echo "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _openprovider_request "$(printf '%s%s' "$(echo "$h" | cut -d . -f 1)" "$results_retrieved")"
+
+ items="$response"
+ while true; do
+ item="$(echo "$items" | _egrep_o '.*<\/openXML>' | sed -n 's/.*\(.*<\/domain>\).*/\1/p')"
+ _debug existing_items "$existing_items"
+ _debug results_retrieved "$results_retrieved"
+ _debug item "$item"
+
+ if [ -z "$item" ]; then
+ break
+ fi
+
+ tmpitem="$(echo "$item" | sed 's/\*/\\*/g')"
+ items="$(echo "$items" | sed "s|${tmpitem}||")"
+
+ results_retrieved="$(_math "$results_retrieved" + 1)"
+
+ _domain_name="$(echo "$item" | sed -n 's/.*.*\(.*\)<\/name>.*<\/domain>.*/\1/p')"
+ _domain_extension="$(echo "$item" | sed -n 's/.*.*\(.*\)<\/extension>.*<\/domain>.*/\1/p')"
+ _debug _domain_name "$_domain_name"
+ _debug _domain_extension "$_domain_extension"
+ if [ "$_domain_name.$_domain_extension" = "$h" ]; then
+ return 0
+ fi
+ done
+
+ total="$(echo "$response" | _egrep_o '.*?<\/total>' | sed -n 's/.*\(.*\)<\/total>.*/\1/p')"
+
+ _debug total "$total"
+
+ if [ "$results_retrieved" -eq "$total" ]; then
+ results_retrieved=0
+ i="$(_math "$i" + 1)"
+ fi
+ done
+ return 1
+}
+
+_openprovider_request() {
+ request_xml=$1
+
+ xml_prefix=''
+ xml_content=$(printf '%s%s%s' "$OPENPROVIDER_USER" "$OPENPROVIDER_PASSWORDHASH" "$request_xml")
+ response="$(_post "$(echo "$xml_prefix$xml_content" | tr -d '\n')" "$OPENPROVIDER_API" "" "POST" "application/xml")"
+ _debug response "$response"
+ if ! _contains "$response" "
0.*"; then
+ _err "API request failed."
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_openstack.sh b/acme.sh-master/dnsapi/dns_openstack.sh
new file mode 100644
index 0000000..fcc1dc2
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_openstack.sh
@@ -0,0 +1,348 @@
+#!/usr/bin/env sh
+
+# OpenStack Designate API plugin
+#
+# This requires you to have OpenStackClient and python-desginateclient
+# installed.
+#
+# You will require Keystone V3 credentials loaded into your environment, which
+# could be either password or v3applicationcredential type.
+#
+# Author: Andy Botting
+
+######## Public functions #####################
+
+# Usage: dns_openstack_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_openstack_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _dns_openstack_credentials || return $?
+ _dns_openstack_check_setup || return $?
+ _dns_openstack_find_zone || return $?
+ _dns_openstack_get_recordset || return $?
+ _debug _recordset_id "$_recordset_id"
+ if [ -n "$_recordset_id" ]; then
+ _dns_openstack_get_records || return $?
+ _debug _records "$_records"
+ fi
+ _dns_openstack_create_recordset || return $?
+}
+
+# Usage: dns_openstack_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Remove the txt record after validation.
+dns_openstack_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ _dns_openstack_credentials || return $?
+ _dns_openstack_check_setup || return $?
+ _dns_openstack_find_zone || return $?
+ _dns_openstack_get_recordset || return $?
+ _debug _recordset_id "$_recordset_id"
+ if [ -n "$_recordset_id" ]; then
+ _dns_openstack_get_records || return $?
+ _debug _records "$_records"
+ fi
+ _dns_openstack_delete_recordset || return $?
+}
+
+#################### Private functions below ##################################
+
+_dns_openstack_create_recordset() {
+
+ if [ -z "$_recordset_id" ]; then
+ _info "Creating a new recordset"
+ if ! _recordset_id=$(openstack recordset create -c id -f value --type TXT --record="$txtvalue" "$_zone_id" "$fulldomain."); then
+ _err "No recordset ID found after create"
+ return 1
+ fi
+ else
+ _info "Updating existing recordset"
+ # Build new list of --record= args for update
+ _record_args="--record=$txtvalue"
+ for _rec in $_records; do
+ _record_args="$_record_args --record=$_rec"
+ done
+ # shellcheck disable=SC2086
+ if ! _recordset_id=$(openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain."); then
+ _err "Recordset update failed"
+ return 1
+ fi
+ fi
+
+ _max_retries=60
+ _sleep_sec=5
+ _retry_times=0
+ while [ "$_retry_times" -lt "$_max_retries" ]; do
+ _retry_times=$(_math "$_retry_times" + 1)
+ _debug3 _retry_times "$_retry_times"
+
+ _record_status=$(openstack recordset show -c status -f value "$_zone_id" "$_recordset_id")
+ _info "Recordset status is $_record_status"
+ if [ "$_record_status" = "ACTIVE" ]; then
+ return 0
+ elif [ "$_record_status" = "ERROR" ]; then
+ return 1
+ else
+ _sleep $_sleep_sec
+ fi
+ done
+
+ _err "Recordset failed to become ACTIVE"
+ return 1
+}
+
+_dns_openstack_delete_recordset() {
+
+ if [ "$_records" = "$txtvalue" ]; then
+ _info "Only one record found, deleting recordset"
+ if ! openstack recordset delete "$_zone_id" "$fulldomain." >/dev/null; then
+ _err "Failed to delete recordset"
+ return 1
+ fi
+ else
+ _info "Found existing records, updating recordset"
+ # Build new list of --record= args for update
+ _record_args=""
+ for _rec in $_records; do
+ if [ "$_rec" = "$txtvalue" ]; then
+ continue
+ fi
+ _record_args="$_record_args --record=$_rec"
+ done
+ # shellcheck disable=SC2086
+ if ! openstack recordset set -c id -f value $_record_args "$_zone_id" "$fulldomain." >/dev/null; then
+ _err "Recordset update failed"
+ return 1
+ fi
+ fi
+}
+
+_dns_openstack_get_root() {
+ # Take the full fqdn and strip away pieces until we get an exact zone name
+ # match. For example, _acme-challenge.something.domain.com might need to go
+ # into something.domain.com or domain.com
+ _zone_name=$1
+ _zone_list=$2
+ while [ "$_zone_name" != "" ]; do
+ _zone_name="$(echo "$_zone_name" | sed 's/[^.]*\.*//')"
+ echo "$_zone_list" | while read -r id name; do
+ if _startswith "$_zone_name." "$name"; then
+ echo "$id"
+ fi
+ done
+ done | _head_n 1
+}
+
+_dns_openstack_find_zone() {
+ if ! _zone_list="$(openstack zone list -c id -c name -f value)"; then
+ _err "Can't list zones. Check your OpenStack credentials"
+ return 1
+ fi
+ _debug _zone_list "$_zone_list"
+
+ if ! _zone_id="$(_dns_openstack_get_root "$fulldomain" "$_zone_list")"; then
+ _err "Can't find a matching zone. Check your OpenStack credentials"
+ return 1
+ fi
+ _debug _zone_id "$_zone_id"
+}
+
+_dns_openstack_get_records() {
+ if ! _records=$(openstack recordset show -c records -f value "$_zone_id" "$fulldomain."); then
+ _err "Failed to get records"
+ return 1
+ fi
+ return 0
+}
+
+_dns_openstack_get_recordset() {
+ if ! _recordset_id=$(openstack recordset list -c id -f value --name "$fulldomain." "$_zone_id"); then
+ _err "Failed to get recordset"
+ return 1
+ fi
+ return 0
+}
+
+_dns_openstack_check_setup() {
+ if ! _exists openstack; then
+ _err "OpenStack client not found"
+ return 1
+ fi
+}
+
+_dns_openstack_credentials() {
+ _debug "Check OpenStack credentials"
+
+ # If we have OS_AUTH_URL already set in the environment, then assume we want
+ # to use those, otherwise use stored credentials
+ if [ -n "$OS_AUTH_URL" ]; then
+ _debug "OS_AUTH_URL env var found, using environment"
+ else
+ _debug "OS_AUTH_URL not found, loading stored credentials"
+ OS_AUTH_URL="${OS_AUTH_URL:-$(_readaccountconf_mutable OS_AUTH_URL)}"
+ OS_IDENTITY_API_VERSION="${OS_IDENTITY_API_VERSION:-$(_readaccountconf_mutable OS_IDENTITY_API_VERSION)}"
+ OS_AUTH_TYPE="${OS_AUTH_TYPE:-$(_readaccountconf_mutable OS_AUTH_TYPE)}"
+ OS_APPLICATION_CREDENTIAL_ID="${OS_APPLICATION_CREDENTIAL_ID:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID)}"
+ OS_APPLICATION_CREDENTIAL_SECRET="${OS_APPLICATION_CREDENTIAL_SECRET:-$(_readaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET)}"
+ OS_USERNAME="${OS_USERNAME:-$(_readaccountconf_mutable OS_USERNAME)}"
+ OS_PASSWORD="${OS_PASSWORD:-$(_readaccountconf_mutable OS_PASSWORD)}"
+ OS_PROJECT_NAME="${OS_PROJECT_NAME:-$(_readaccountconf_mutable OS_PROJECT_NAME)}"
+ OS_PROJECT_ID="${OS_PROJECT_ID:-$(_readaccountconf_mutable OS_PROJECT_ID)}"
+ OS_USER_DOMAIN_NAME="${OS_USER_DOMAIN_NAME:-$(_readaccountconf_mutable OS_USER_DOMAIN_NAME)}"
+ OS_USER_DOMAIN_ID="${OS_USER_DOMAIN_ID:-$(_readaccountconf_mutable OS_USER_DOMAIN_ID)}"
+ OS_PROJECT_DOMAIN_NAME="${OS_PROJECT_DOMAIN_NAME:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_NAME)}"
+ OS_PROJECT_DOMAIN_ID="${OS_PROJECT_DOMAIN_ID:-$(_readaccountconf_mutable OS_PROJECT_DOMAIN_ID)}"
+ fi
+
+ # Check each var and either save or clear it depending on whether its set.
+ # The helps us clear out old vars in the case where a user may want
+ # to switch between password and app creds
+ _debug "OS_AUTH_URL" "$OS_AUTH_URL"
+ if [ -n "$OS_AUTH_URL" ]; then
+ export OS_AUTH_URL
+ _saveaccountconf_mutable OS_AUTH_URL "$OS_AUTH_URL"
+ else
+ unset OS_AUTH_URL
+ _clearaccountconf SAVED_OS_AUTH_URL
+ fi
+
+ _debug "OS_IDENTITY_API_VERSION" "$OS_IDENTITY_API_VERSION"
+ if [ -n "$OS_IDENTITY_API_VERSION" ]; then
+ export OS_IDENTITY_API_VERSION
+ _saveaccountconf_mutable OS_IDENTITY_API_VERSION "$OS_IDENTITY_API_VERSION"
+ else
+ unset OS_IDENTITY_API_VERSION
+ _clearaccountconf SAVED_OS_IDENTITY_API_VERSION
+ fi
+
+ _debug "OS_AUTH_TYPE" "$OS_AUTH_TYPE"
+ if [ -n "$OS_AUTH_TYPE" ]; then
+ export OS_AUTH_TYPE
+ _saveaccountconf_mutable OS_AUTH_TYPE "$OS_AUTH_TYPE"
+ else
+ unset OS_AUTH_TYPE
+ _clearaccountconf SAVED_OS_AUTH_TYPE
+ fi
+
+ _debug "OS_APPLICATION_CREDENTIAL_ID" "$OS_APPLICATION_CREDENTIAL_ID"
+ if [ -n "$OS_APPLICATION_CREDENTIAL_ID" ]; then
+ export OS_APPLICATION_CREDENTIAL_ID
+ _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_ID "$OS_APPLICATION_CREDENTIAL_ID"
+ else
+ unset OS_APPLICATION_CREDENTIAL_ID
+ _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_ID
+ fi
+
+ _secure_debug "OS_APPLICATION_CREDENTIAL_SECRET" "$OS_APPLICATION_CREDENTIAL_SECRET"
+ if [ -n "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
+ export OS_APPLICATION_CREDENTIAL_SECRET
+ _saveaccountconf_mutable OS_APPLICATION_CREDENTIAL_SECRET "$OS_APPLICATION_CREDENTIAL_SECRET"
+ else
+ unset OS_APPLICATION_CREDENTIAL_SECRET
+ _clearaccountconf SAVED_OS_APPLICATION_CREDENTIAL_SECRET
+ fi
+
+ _debug "OS_USERNAME" "$OS_USERNAME"
+ if [ -n "$OS_USERNAME" ]; then
+ export OS_USERNAME
+ _saveaccountconf_mutable OS_USERNAME "$OS_USERNAME"
+ else
+ unset OS_USERNAME
+ _clearaccountconf SAVED_OS_USERNAME
+ fi
+
+ _secure_debug "OS_PASSWORD" "$OS_PASSWORD"
+ if [ -n "$OS_PASSWORD" ]; then
+ export OS_PASSWORD
+ _saveaccountconf_mutable OS_PASSWORD "$OS_PASSWORD"
+ else
+ unset OS_PASSWORD
+ _clearaccountconf SAVED_OS_PASSWORD
+ fi
+
+ _debug "OS_PROJECT_NAME" "$OS_PROJECT_NAME"
+ if [ -n "$OS_PROJECT_NAME" ]; then
+ export OS_PROJECT_NAME
+ _saveaccountconf_mutable OS_PROJECT_NAME "$OS_PROJECT_NAME"
+ else
+ unset OS_PROJECT_NAME
+ _clearaccountconf SAVED_OS_PROJECT_NAME
+ fi
+
+ _debug "OS_PROJECT_ID" "$OS_PROJECT_ID"
+ if [ -n "$OS_PROJECT_ID" ]; then
+ export OS_PROJECT_ID
+ _saveaccountconf_mutable OS_PROJECT_ID "$OS_PROJECT_ID"
+ else
+ unset OS_PROJECT_ID
+ _clearaccountconf SAVED_OS_PROJECT_ID
+ fi
+
+ _debug "OS_USER_DOMAIN_NAME" "$OS_USER_DOMAIN_NAME"
+ if [ -n "$OS_USER_DOMAIN_NAME" ]; then
+ export OS_USER_DOMAIN_NAME
+ _saveaccountconf_mutable OS_USER_DOMAIN_NAME "$OS_USER_DOMAIN_NAME"
+ else
+ unset OS_USER_DOMAIN_NAME
+ _clearaccountconf SAVED_OS_USER_DOMAIN_NAME
+ fi
+
+ _debug "OS_USER_DOMAIN_ID" "$OS_USER_DOMAIN_ID"
+ if [ -n "$OS_USER_DOMAIN_ID" ]; then
+ export OS_USER_DOMAIN_ID
+ _saveaccountconf_mutable OS_USER_DOMAIN_ID "$OS_USER_DOMAIN_ID"
+ else
+ unset OS_USER_DOMAIN_ID
+ _clearaccountconf SAVED_OS_USER_DOMAIN_ID
+ fi
+
+ _debug "OS_PROJECT_DOMAIN_NAME" "$OS_PROJECT_DOMAIN_NAME"
+ if [ -n "$OS_PROJECT_DOMAIN_NAME" ]; then
+ export OS_PROJECT_DOMAIN_NAME
+ _saveaccountconf_mutable OS_PROJECT_DOMAIN_NAME "$OS_PROJECT_DOMAIN_NAME"
+ else
+ unset OS_PROJECT_DOMAIN_NAME
+ _clearaccountconf SAVED_OS_PROJECT_DOMAIN_NAME
+ fi
+
+ _debug "OS_PROJECT_DOMAIN_ID" "$OS_PROJECT_DOMAIN_ID"
+ if [ -n "$OS_PROJECT_DOMAIN_ID" ]; then
+ export OS_PROJECT_DOMAIN_ID
+ _saveaccountconf_mutable OS_PROJECT_DOMAIN_ID "$OS_PROJECT_DOMAIN_ID"
+ else
+ unset OS_PROJECT_DOMAIN_ID
+ _clearaccountconf SAVED_OS_PROJECT_DOMAIN_ID
+ fi
+
+ if [ "$OS_AUTH_TYPE" = "v3applicationcredential" ]; then
+ # Application Credential auth
+ if [ -z "$OS_APPLICATION_CREDENTIAL_ID" ] || [ -z "$OS_APPLICATION_CREDENTIAL_SECRET" ]; then
+ _err "When using OpenStack application credentials, OS_APPLICATION_CREDENTIAL_ID"
+ _err "and OS_APPLICATION_CREDENTIAL_SECRET must be set."
+ _err "Please check your credentials and try again."
+ return 1
+ fi
+ else
+ # Password auth
+ if [ -z "$OS_USERNAME" ] || [ -z "$OS_PASSWORD" ]; then
+ _err "OpenStack username or password not found."
+ _err "Please check your credentials and try again."
+ return 1
+ fi
+
+ if [ -z "$OS_PROJECT_NAME" ] && [ -z "$OS_PROJECT_ID" ]; then
+ _err "When using password authentication, OS_PROJECT_NAME or"
+ _err "OS_PROJECT_ID must be set."
+ _err "Please check your credentials and try again."
+ return 1
+ fi
+ fi
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_opnsense.sh b/acme.sh-master/dnsapi/dns_opnsense.sh
new file mode 100644
index 0000000..c2806a1
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_opnsense.sh
@@ -0,0 +1,272 @@
+#!/usr/bin/env sh
+
+#OPNsense Bind API
+#https://docs.opnsense.org/development/api.html
+#
+#OPNs_Host="opnsense.example.com"
+#OPNs_Port="443"
+# optional, defaults to 443 if unset
+#OPNs_Key="qocfU9RSbt8vTIBcnW8bPqCrpfAHMDvj5OzadE7Str+rbjyCyk7u6yMrSCHtBXabgDDXx/dY0POUp7ZA"
+#OPNs_Token="pZEQ+3ce8dDlfBBdg3N8EpqpF5I1MhFqdxX06le6Gl8YzyQvYCfCzNaFX9O9+IOSyAs7X71fwdRiZ+Lv"
+#OPNs_Api_Insecure=0
+# optional, defaults to 0 if unset
+# Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
+#fulldomain
+#txtvalue
+OPNs_DefaultPort=443
+OPNs_DefaultApi_Insecure=0
+
+dns_opnsense_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _opns_check_auth || return 1
+
+ if ! set_record "$fulldomain" "$txtvalue"; then
+ return 1
+ fi
+
+ return 0
+}
+
+#fulldomain
+dns_opnsense_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _opns_check_auth || return 1
+
+ if ! rm_record "$fulldomain" "$txtvalue"; then
+ return 1
+ fi
+
+ return 0
+}
+
+set_record() {
+ fulldomain=$1
+ new_challenge=$2
+ _info "Adding record $fulldomain with challenge: $new_challenge"
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain "$_domain"
+ _debug _host "$_host"
+ _debug _domainid "$_domainid"
+ _return_str=""
+ _record_string=""
+ _build_record_string "$_domainid" "$_host" "$new_challenge"
+ _uuid=""
+ if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
+ # Update
+ if _opns_rest "POST" "/record/setRecord/${_uuid}" "$_record_string"; then
+ _return_str="$response"
+ else
+ return 1
+ fi
+
+ else
+ #create
+ if _opns_rest "POST" "/record/addRecord" "$_record_string"; then
+ _return_str="$response"
+ else
+ return 1
+ fi
+ fi
+
+ if echo "$_return_str" | _egrep_o "\"result\":\"saved\"" >/dev/null; then
+ _opns_rest "POST" "/service/reconfigure" "{}"
+ _debug "Record created"
+ else
+ _err "Error creating record $_record_string"
+ return 1
+ fi
+
+ return 0
+}
+
+rm_record() {
+ fulldomain=$1
+ new_challenge="$2"
+ _info "Remove record $fulldomain with challenge: $new_challenge"
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain "$_domain"
+ _debug _host "$_host"
+ _debug _domainid "$_domainid"
+ _uuid=""
+ if _existingchallenge "$_domain" "$_host" "$new_challenge"; then
+ # Delete
+ if _opns_rest "POST" "/record/delRecord/${_uuid}" "\{\}"; then
+ if echo "$_return_str" | _egrep_o "\"result\":\"deleted\"" >/dev/null; then
+ _opns_rest "POST" "/service/reconfigure" "{}"
+ _debug "Record deleted"
+ else
+ _err "Error deleting record $_host from domain $fulldomain"
+ return 1
+ fi
+ else
+ _err "Error deleting record $_host from domain $fulldomain"
+ return 1
+ fi
+ else
+ _info "Record not found, nothing to remove"
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domainid=domid
+#_domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ if _opns_rest "GET" "/domain/searchMasterDomain"; then
+ _domain_response="$response"
+ else
+ return 1
+ fi
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+ _debug h "$h"
+ id=$(echo "$_domain_response" | _egrep_o "\"uuid\":\"[a-z0-9\-]*\",\"enabled\":\"1\",\"type\":\"master\",\"domainname\":\"${h}\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ if [ -n "$id" ]; then
+ _debug id "$id"
+ _host=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="${h}"
+ _domainid="${id}"
+ return 0
+ fi
+ p=$i
+ i=$(_math $i + 1)
+ done
+ _debug "$domain not found"
+
+ return 1
+}
+
+_opns_rest() {
+ method=$1
+ ep=$2
+ data=$3
+ #Percent encode user and token
+ key=$(echo "$OPNs_Key" | tr -d "\n\r" | _url_encode)
+ token=$(echo "$OPNs_Token" | tr -d "\n\r" | _url_encode)
+
+ opnsense_url="https://${key}:${token}@${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
+ export _H1="Content-Type: application/json"
+ _debug2 "Try to call api: https://${OPNs_Host}:${OPNs_Port:-$OPNs_DefaultPort}/api/bind${ep}"
+ if [ ! "$method" = "GET" ]; then
+ _debug data "$data"
+ export _H1="Content-Type: application/json"
+ response="$(_post "$data" "$opnsense_url" "" "$method")"
+ else
+ export _H1=""
+ response="$(_get "$opnsense_url")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+
+ return 0
+}
+
+_build_record_string() {
+ _record_string="{\"record\":{\"enabled\":\"1\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"}}"
+}
+
+_existingchallenge() {
+ if _opns_rest "GET" "/record/searchRecord"; then
+ _record_response="$response"
+ else
+ return 1
+ fi
+ _uuid=""
+ _uuid=$(echo "$_record_response" | _egrep_o "\"uuid\":\"[^\"]*\",\"enabled\":\"[01]\",\"domain\":\"$1\",\"name\":\"$2\",\"type\":\"TXT\",\"value\":\"$3\"" | cut -d ':' -f 2 | cut -d '"' -f 2)
+
+ if [ -n "$_uuid" ]; then
+ _debug uuid "$_uuid"
+ return 0
+ fi
+ _debug "${2}.$1{1} record not found"
+
+ return 1
+}
+
+_opns_check_auth() {
+ OPNs_Host="${OPNs_Host:-$(_readaccountconf_mutable OPNs_Host)}"
+ OPNs_Port="${OPNs_Port:-$(_readaccountconf_mutable OPNs_Port)}"
+ OPNs_Key="${OPNs_Key:-$(_readaccountconf_mutable OPNs_Key)}"
+ OPNs_Token="${OPNs_Token:-$(_readaccountconf_mutable OPNs_Token)}"
+ OPNs_Api_Insecure="${OPNs_Api_Insecure:-$(_readaccountconf_mutable OPNs_Api_Insecure)}"
+
+ if [ -z "$OPNs_Host" ]; then
+ _err "You don't specify OPNsense address."
+ return 1
+ else
+ _saveaccountconf_mutable OPNs_Host "$OPNs_Host"
+ fi
+
+ if ! printf '%s' "$OPNs_Port" | grep '^[0-9]*$' >/dev/null; then
+ _err 'OPNs_Port specified but not numeric value'
+ return 1
+ elif [ -z "$OPNs_Port" ]; then
+ _info "OPNSense port not specified. Defaulting to using port $OPNs_DefaultPort"
+ else
+ _saveaccountconf_mutable OPNs_Port "$OPNs_Port"
+ fi
+
+ if ! printf '%s' "$OPNs_Api_Insecure" | grep '^[01]$' >/dev/null; then
+ _err 'OPNs_Api_Insecure specified but not 0/1 value'
+ return 1
+ elif [ -n "$OPNs_Api_Insecure" ]; then
+ _saveaccountconf_mutable OPNs_Api_Insecure "$OPNs_Api_Insecure"
+ fi
+ export HTTPS_INSECURE="${OPNs_Api_Insecure:-$OPNs_DefaultApi_Insecure}"
+
+ if [ -z "$OPNs_Key" ]; then
+ _err "you have not specified your OPNsense api key id."
+ _err "Please set OPNs_Key and try again."
+ return 1
+ else
+ _saveaccountconf_mutable OPNs_Key "$OPNs_Key"
+ fi
+
+ if [ -z "$OPNs_Token" ]; then
+ _err "you have not specified your OPNsense token."
+ _err "Please create OPNs_Token and try again."
+ return 1
+ else
+ _saveaccountconf_mutable OPNs_Token "$OPNs_Token"
+ fi
+
+ if ! _opns_rest "GET" "/general/get"; then
+ _err "Call to OPNsense API interface failed. Unable to access OPNsense API."
+ return 1
+ fi
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_ovh.sh b/acme.sh-master/dnsapi/dns_ovh.sh
new file mode 100644
index 0000000..5e35011
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_ovh.sh
@@ -0,0 +1,322 @@
+#!/usr/bin/env sh
+
+#Application Key
+#OVH_AK="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#Application Secret
+#OVH_AS="sdfsafsdfsdfdsfsdfsa"
+#
+#Consumer Key
+#OVH_CK="sdfsdfsdfsdfsdfdsf"
+
+#OVH_END_POINT=ovh-eu
+
+#'ovh-eu'
+OVH_EU='https://eu.api.ovh.com/1.0'
+
+#'ovh-ca':
+OVH_CA='https://ca.api.ovh.com/1.0'
+
+#'kimsufi-eu'
+KSF_EU='https://eu.api.kimsufi.com/1.0'
+
+#'kimsufi-ca'
+KSF_CA='https://ca.api.kimsufi.com/1.0'
+
+#'soyoustart-eu'
+SYS_EU='https://eu.api.soyoustart.com/1.0'
+
+#'soyoustart-ca'
+SYS_CA='https://ca.api.soyoustart.com/1.0'
+
+#'runabove-ca'
+RAV_CA='https://api.runabove.com/1.0'
+
+wiki="https://github.com/acmesh-official/acme.sh/wiki/How-to-use-OVH-domain-api"
+
+ovh_success="https://github.com/acmesh-official/acme.sh/wiki/OVH-Success"
+
+_ovh_get_api() {
+ _ogaep="$1"
+
+ case "${_ogaep}" in
+
+ ovh-eu | ovheu)
+ printf "%s" $OVH_EU
+ return
+ ;;
+ ovh-ca | ovhca)
+ printf "%s" $OVH_CA
+ return
+ ;;
+ kimsufi-eu | kimsufieu)
+ printf "%s" $KSF_EU
+ return
+ ;;
+ kimsufi-ca | kimsufica)
+ printf "%s" $KSF_CA
+ return
+ ;;
+ soyoustart-eu | soyoustarteu)
+ printf "%s" $SYS_EU
+ return
+ ;;
+ soyoustart-ca | soyoustartca)
+ printf "%s" $SYS_CA
+ return
+ ;;
+ runabove-ca | runaboveca)
+ printf "%s" $RAV_CA
+ return
+ ;;
+
+ *)
+
+ _err "Unknown parameter : $1"
+ return 1
+ ;;
+ esac
+}
+
+_initAuth() {
+ OVH_AK="${OVH_AK:-$(_readaccountconf_mutable OVH_AK)}"
+ OVH_AS="${OVH_AS:-$(_readaccountconf_mutable OVH_AS)}"
+
+ if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
+ OVH_AK=""
+ OVH_AS=""
+ _err "You don't specify OVH application key and application secret yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then
+ _info "It seems that your ovh key is changed, let's clear consumer key first."
+ _clearaccountconf_mutable OVH_CK
+ fi
+ _saveaccountconf_mutable OVH_AK "$OVH_AK"
+ _saveaccountconf_mutable OVH_AS "$OVH_AS"
+
+ OVH_END_POINT="${OVH_END_POINT:-$(_readaccountconf_mutable OVH_END_POINT)}"
+ if [ -z "$OVH_END_POINT" ]; then
+ OVH_END_POINT="ovh-eu"
+ fi
+ _info "Using OVH endpoint: $OVH_END_POINT"
+ if [ "$OVH_END_POINT" != "ovh-eu" ]; then
+ _saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT"
+ fi
+
+ OVH_API="$(_ovh_get_api $OVH_END_POINT)"
+ _debug OVH_API "$OVH_API"
+
+ OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}"
+ if [ -z "$OVH_CK" ]; then
+ _info "OVH consumer key is empty, Let's get one:"
+ if ! _ovh_authentication; then
+ _err "Can not get consumer key."
+ fi
+ #return and wait for retry.
+ return 1
+ fi
+ _saveaccountconf_mutable OVH_CK "$OVH_CK"
+
+ _info "Checking authentication"
+
+ if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL" || _contains "$response" "NOT_CREDENTIAL"; then
+ _err "The consumer key is invalid: $OVH_CK"
+ _err "Please retry to create a new one."
+ _clearaccountconf_mutable OVH_CK
+ return 1
+ fi
+ _info "Consumer key is ok."
+ return 0
+}
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ovh_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _initAuth; 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"
+
+ _info "Adding record"
+ if _ovh_rest POST "domain/zone/$_domain/record" "{\"fieldType\":\"TXT\",\"subDomain\":\"$_sub_domain\",\"target\":\"$txtvalue\",\"ttl\":60}"; then
+ if _contains "$response" "$txtvalue"; then
+ _ovh_rest POST "domain/zone/$_domain/refresh"
+ _debug "Refresh:$response"
+ _info "Added, sleep 10 seconds."
+ _sleep 10
+ return 0
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+
+}
+
+#fulldomain
+dns_ovh_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _initAuth; 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"
+ if ! _ovh_rest GET "domain/zone/$_domain/record?fieldType=TXT&subDomain=$_sub_domain"; then
+ return 1
+ fi
+
+ for rid in $(echo "$response" | tr '][,' ' '); do
+ _debug rid "$rid"
+ if ! _ovh_rest GET "domain/zone/$_domain/record/$rid"; then
+ return 1
+ fi
+ if _contains "$response" "\"target\":\"$txtvalue\""; then
+ _debug "Found txt id:$rid"
+ if ! _ovh_rest DELETE "domain/zone/$_domain/record/$rid"; then
+ return 1
+ fi
+ _ovh_rest POST "domain/zone/$_domain/refresh"
+ _debug "Refresh:$response"
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_ovh_authentication() {
+
+ _H1="X-Ovh-Application: $OVH_AK"
+ _H2="Content-type: application/json"
+ _H3=""
+ _H4=""
+
+ _ovhdata='{"accessRules": [{"method": "GET","path": "/auth/time"},{"method": "GET","path": "/domain"},{"method": "GET","path": "/domain/zone/*"},{"method": "GET","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/record"},{"method": "POST","path": "/domain/zone/*/refresh"},{"method": "PUT","path": "/domain/zone/*/record/*"},{"method": "DELETE","path": "/domain/zone/*/record/*"}],"redirection":"'$ovh_success'"}'
+
+ response="$(_post "$_ovhdata" "$OVH_API/auth/credential")"
+ _debug3 response "$response"
+ validationUrl="$(echo "$response" | _egrep_o "validationUrl\":\"[^\"]*\"" | _egrep_o "http.*\"" | tr -d '"')"
+ if [ -z "$validationUrl" ]; then
+ _err "Unable to get validationUrl"
+ return 1
+ fi
+ _debug validationUrl "$validationUrl"
+
+ consumerKey="$(echo "$response" | _egrep_o "consumerKey\":\"[^\"]*\"" | cut -d : -f 2 | tr -d '"')"
+ if [ -z "$consumerKey" ]; then
+ _err "Unable to get consumerKey"
+ return 1
+ fi
+ _secure_debug consumerKey "$consumerKey"
+
+ OVH_CK="$consumerKey"
+ _saveaccountconf_mutable OVH_CK "$OVH_CK"
+ _info "Please open this link to do authentication: $(__green "$validationUrl")"
+
+ _info "Here is a guide for you: $(__green "$wiki")"
+ _info "Please retry after the authentication is done."
+
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _ovh_rest GET "domain/zone/$h"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "This service does not exist" >/dev/null &&
+ ! _contains "$response" "This call has not been granted" >/dev/null &&
+ ! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_ovh_timestamp() {
+ _H1=""
+ _H2=""
+ _H3=""
+ _H4=""
+ _H5=""
+ _get "$OVH_API/auth/time" "" 30
+}
+
+_ovh_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ _ovh_url="$OVH_API/$ep"
+ _debug2 _ovh_url "$_ovh_url"
+ _ovh_t="$(_ovh_timestamp)"
+ _debug2 _ovh_t "$_ovh_t"
+ _ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
+ _secure_debug _ovh_p "$_ovh_p"
+ _ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
+ _debug2 _ovh_hex "$_ovh_hex"
+
+ export _H1="X-Ovh-Application: $OVH_AK"
+ export _H2="X-Ovh-Signature: \$1\$$_ovh_hex"
+ _debug2 _H2 "$_H2"
+ export _H3="X-Ovh-Timestamp: $_ovh_t"
+ export _H4="X-Ovh-Consumer: $OVH_CK"
+ export _H5="Content-Type: application/json;charset=utf-8"
+ if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$_ovh_url" "" "$m")"
+ else
+ response="$(_get "$_ovh_url")"
+ fi
+
+ if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then
+ _err "error $response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_pdns.sh b/acme.sh-master/dnsapi/dns_pdns.sh
new file mode 100644
index 0000000..6aa2e95
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_pdns.sh
@@ -0,0 +1,233 @@
+#!/usr/bin/env sh
+
+#PowerDNS Embedded API
+#https://doc.powerdns.com/md/httpapi/api_spec/
+#
+#PDNS_Url="http://ns.example.com:8081"
+#PDNS_ServerId="localhost"
+#PDNS_Token="0123456789ABCDEF"
+#PDNS_Ttl=60
+
+DEFAULT_PDNS_TTL=60
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
+#fulldomain
+#txtvalue
+dns_pdns_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$PDNS_Url" ]; then
+ PDNS_Url=""
+ _err "You don't specify PowerDNS address."
+ _err "Please set PDNS_Url and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_ServerId" ]; then
+ PDNS_ServerId=""
+ _err "You don't specify PowerDNS server id."
+ _err "Please set you PDNS_ServerId and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_Token" ]; then
+ PDNS_Token=""
+ _err "You don't specify PowerDNS token."
+ _err "Please create you PDNS_Token and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_Ttl" ]; then
+ PDNS_Ttl="$DEFAULT_PDNS_TTL"
+ fi
+
+ #save the api addr and key to the account conf file.
+ _saveaccountconf PDNS_Url "$PDNS_Url"
+ _saveaccountconf PDNS_ServerId "$PDNS_ServerId"
+ _saveaccountconf PDNS_Token "$PDNS_Token"
+
+ if [ "$PDNS_Ttl" != "$DEFAULT_PDNS_TTL" ]; then
+ _saveaccountconf PDNS_Ttl "$PDNS_Ttl"
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ if ! set_record "$_domain" "$fulldomain" "$txtvalue"; then
+ return 1
+ fi
+
+ return 0
+}
+
+#fulldomain
+dns_pdns_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$PDNS_Ttl" ]; then
+ PDNS_Ttl="$DEFAULT_PDNS_TTL"
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _domain "$_domain"
+
+ if ! rm_record "$_domain" "$fulldomain" "$txtvalue"; then
+ return 1
+ fi
+
+ return 0
+}
+
+set_record() {
+ _info "Adding record"
+ root=$1
+ full=$2
+ new_challenge=$3
+
+ _record_string=""
+ _build_record_string "$new_challenge"
+ _list_existingchallenges
+ for oldchallenge in $_existing_challenges; do
+ _build_record_string "$oldchallenge"
+ done
+
+ if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then
+ _err "Set txt record error."
+ return 1
+ fi
+
+ if ! notify_slaves "$root"; then
+ return 1
+ fi
+
+ return 0
+}
+
+rm_record() {
+ _info "Remove record"
+ root=$1
+ full=$2
+ txtvalue=$3
+
+ #Enumerate existing acme challenges
+ _list_existingchallenges
+
+ if _contains "$_existing_challenges" "$txtvalue"; then
+ #Delete all challenges (PowerDNS API does not allow to delete content)
+ if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}" "application/json"; then
+ _err "Delete txt record error."
+ return 1
+ fi
+ _record_string=""
+ #If the only existing challenge was the challenge to delete: nothing to do
+ if ! [ "$_existing_challenges" = "$txtvalue" ]; then
+ for oldchallenge in $_existing_challenges; do
+ #Build up the challenges to re-add, ommitting the one what should be deleted
+ if ! [ "$oldchallenge" = "$txtvalue" ]; then
+ _build_record_string "$oldchallenge"
+ fi
+ done
+ #Recreate the existing challenges
+ if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root" "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [$_record_string]}]}" "application/json"; then
+ _err "Set txt record error."
+ return 1
+ fi
+ fi
+ if ! notify_slaves "$root"; then
+ return 1
+ fi
+ else
+ _info "Record not found, nothing to remove"
+ fi
+
+ return 0
+}
+
+notify_slaves() {
+ root=$1
+
+ if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root/notify"; then
+ _err "Notify slaves error."
+ return 1
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+
+ if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then
+ _zones_response=$(echo "$response" | _normalizeJson)
+ fi
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+
+ if _contains "$_zones_response" "\"name\":\"$h.\""; then
+ _domain="$h."
+ if [ -z "$h" ]; then
+ _domain="=2E"
+ fi
+ return 0
+ fi
+
+ if [ -z "$h" ]; then
+ return 1
+ fi
+ i=$(_math $i + 1)
+ done
+ _debug "$domain not found"
+
+ return 1
+}
+
+_pdns_rest() {
+ method=$1
+ ep=$2
+ data=$3
+ ct=$4
+
+ export _H1="X-API-Key: $PDNS_Token"
+
+ if [ ! "$method" = "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$PDNS_Url$ep" "" "$method" "$ct")"
+ else
+ response="$(_get "$PDNS_Url$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+
+ return 0
+}
+
+_build_record_string() {
+ _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}"
+}
+
+_list_existingchallenges() {
+ _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones/$root"
+ _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p')
+}
diff --git a/acme.sh-master/dnsapi/dns_pleskxml.sh b/acme.sh-master/dnsapi/dns_pleskxml.sh
new file mode 100644
index 0000000..f598682
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_pleskxml.sh
@@ -0,0 +1,417 @@
+#!/usr/bin/env sh
+
+## Name: dns_pleskxml.sh
+## Created by Stilez.
+## Also uses some code from PR#1832 by @romanlum (https://github.com/acmesh-official/acme.sh/pull/1832/files)
+
+## This DNS-01 method uses the Plesk XML API described at:
+## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709
+## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784
+
+## Note: a DNS ID with host = empty string is OK for this API, see
+## https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798
+## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action.
+## So this API module can handle such a request, if needed.
+
+## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records.
+
+## The user credentials (username+password) and URL/URI for the Plesk XML API must be set by the user
+## before this module is called (case sensitive):
+##
+## ```
+## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php"
+## (or probably something similar)
+## export pleskxml_user="my plesk username"
+## export pleskxml_pass="my plesk password"
+## ```
+
+## Ok, let's issue a cert now:
+## ```
+## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com
+## ```
+##
+## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed.
+
+#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ##################################
+
+pleskxml_init_checks_done=0
+
+# Variable containing bare newline - not a style issue
+# shellcheck disable=SC1004
+NEWLINE='\
+'
+
+pleskxml_tplt_get_domains=""
+# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh
+# Also used to test credentials and URI.
+# No params.
+
+pleskxml_tplt_get_dns_records="%s"
+# Get all DNS records for a Plesk domain ID.
+# PARAM = Plesk domain id to query
+
+pleskxml_tplt_add_txt_record="%sTXT%s%s"
+# Add a TXT record to a domain.
+# PARAMS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value
+
+pleskxml_tplt_rmv_dns_record="%s"
+# Delete a specific TXT record from a domain.
+# PARAM = the Plesk internal ID for the DNS record to be deleted
+
+#################### Public functions ##################################
+
+#Usage: dns_pleskxml_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_pleskxml_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Entering dns_pleskxml_add() to add TXT record '$txtvalue' to domain '$fulldomain'..."
+
+ # Get credentials if not already checked, and confirm we can log in to Plesk XML API
+ if ! _credential_check; then
+ return 1
+ fi
+
+ # Get root and subdomain details, and Plesk domain ID
+ if ! _pleskxml_get_root_domain "$fulldomain"; then
+ return 1
+ fi
+
+ _debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record'
+
+ # printf using template in a variable - not a style issue
+ # shellcheck disable=SC2059
+ request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")"
+ if ! _call_api "$request"; then
+ return 1
+ fi
+
+ # OK, we should have added a TXT record. Let's check and return success if so.
+ # All that should be left in the result, is one section, containing okNEW_DNS_RECORD_ID
+
+ results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')"
+
+ if ! _value "$results" | grep 'ok' | grep '[0-9]\{1,\}' >/dev/null; then
+ # Error - doesn't contain expected string. Something's wrong.
+ _err 'Error when calling Plesk XML API.'
+ _err 'The result did not contain the expected XXXXX section, or contained other values as well.'
+ _err 'This is unexpected: something has gone wrong.'
+ _err 'The full response was:'
+ _err "$pleskxml_prettyprint_result"
+ return 1
+ fi
+
+ recid="$(_value "$results" | grep '[0-9]\{1,\}' | sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/')"
+
+ _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()."
+
+ return 0
+}
+
+#Usage: dns_pleskxml_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_pleskxml_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Entering dns_pleskxml_rm() to remove TXT record '$txtvalue' from domain '$fulldomain'..."
+
+ # Get credentials if not already checked, and confirm we can log in to Plesk XML API
+ if ! _credential_check; then
+ return 1
+ fi
+
+ # Get root and subdomain details, and Plesk domain ID
+ if ! _pleskxml_get_root_domain "$fulldomain"; then
+ return 1
+ fi
+
+ _debug 'Credentials OK, and domain identified. Calling Plesk XML API to get list of TXT records and their IDs'
+
+ # printf using template in a variable - not a style issue
+ # shellcheck disable=SC2059
+ request="$(printf "$pleskxml_tplt_get_dns_records" "$root_domain_id")"
+ if ! _call_api "$request"; then
+ return 1
+ fi
+
+ # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have)
+ # Also strip out spaces between tags, redundant and group tags and any tags
+ reclist="$(
+ _api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' |
+ sed 's# \{1,\}<\([a-zA-Z]\)#<\1#g;s#\{0,1\}data>##g;s#<[a-z][^/<>]*/>##g' |
+ grep "${root_domain_id}" |
+ grep '[0-9]\{1,\}' |
+ grep 'TXT'
+ )"
+
+ if [ -z "$reclist" ]; then
+ _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting."
+ return 1
+ fi
+
+ _debug "Got list of DNS TXT records for root domain '$root_domain_name':"
+ _debug "$reclist"
+
+ recid="$(
+ _value "$reclist" |
+ grep "${fulldomain}." |
+ grep "${txtvalue}" |
+ sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/'
+ )"
+
+ if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then
+ _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'"
+ _err "Cannot delete TXT record. Exiting."
+ return 1
+ fi
+
+ _debug "Found Plesk record ID for target text string '${txtvalue}': ID=${recid}"
+ _debug 'Calling Plesk XML API to remove TXT record'
+
+ # printf using template in a variable - not a style issue
+ # shellcheck disable=SC2059
+ request="$(printf "$pleskxml_tplt_rmv_dns_record" "$recid")"
+ if ! _call_api "$request"; then
+ return 1
+ fi
+
+ # OK, we should have removed a TXT record. Let's check and return success if so.
+ # All that should be left in the result, is one section, containing okPLESK_DELETED_DNS_RECORD_ID
+
+ results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')"
+
+ if ! _value "$results" | grep 'ok' | grep '[0-9]\{1,\}' >/dev/null; then
+ # Error - doesn't contain expected string. Something's wrong.
+ _err 'Error when calling Plesk XML API.'
+ _err 'The result did not contain the expected XXXXX section, or contained other values as well.'
+ _err 'This is unexpected: something has gone wrong.'
+ _err 'The full response was:'
+ _err "$pleskxml_prettyprint_result"
+ return 1
+ fi
+
+ _info "Success. TXT record appears to be correctly removed. Exiting dns_pleskxml_rm()."
+ return 0
+}
+
+#################### Private functions below (utility functions) ##################################
+
+# Outputs value of a variable without additional newlines etc
+_value() {
+ printf '%s' "$1"
+}
+
+# Outputs value of a variable (FQDN) and cuts it at 2 specified '.' delimiters, returning the text in between
+# $1, $2 = where to cut
+# $3 = FQDN
+_valuecut() {
+ printf '%s' "$3" | cut -d . -f "${1}-${2}"
+}
+
+# Counts '.' present in a domain name or other string
+# $1 = domain name
+_countdots() {
+ _value "$1" | tr -dc '.' | wc -c | sed 's/ //g'
+}
+
+# Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines
+# $1 - result string from API
+# $2 - plain text tag to resplit on (usually "result" or "domain"). NOT REGEX
+# $3 - basic regex to recognise useful return lines
+# note: $3 matches via basic NOT extended regex (BRE), as extended regex capabilities not needed at the moment.
+# Last line could change to instead, with suitable escaping of ['"/$],
+# if future Plesk XML API changes ever require extended regex
+_api_response_split() {
+ printf '%s' "$1" |
+ sed 's/^ +//;s/ +$//' |
+ tr -d '\n\r' |
+ sed "s/<\/\{0,1\}$2>/${NEWLINE}/g" |
+ grep "$3"
+}
+
+#################### Private functions below (DNS functions) ##################################
+
+# Calls Plesk XML API, and checks results for obvious issues
+_call_api() {
+ request="$1"
+ errtext=''
+
+ _debug 'Entered _call_api(). Calling Plesk XML API with request:'
+ _debug "'$request'"
+
+ export _H1="HTTP_AUTH_LOGIN: $pleskxml_user"
+ export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass"
+ export _H3="content-Type: text/xml"
+ export _H4="HTTP_PRETTY_PRINT: true"
+ pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")"
+ pleskxml_retcode="$?"
+ _debug 'The responses from the Plesk XML server were:'
+ _debug "retcode=$pleskxml_retcode. Literal response:"
+ _debug "'$pleskxml_prettyprint_result'"
+
+ # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly.
+ # Also detect if there simply aren't any status lines (null result?) and report that, as well.
+
+ statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *[^<]* *$')"
+ statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *ok *$')"
+
+ if [ -z "$statuslines_count_total" ]; then
+
+ # We have no status lines at all. Results are empty
+ errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.'
+
+ elif [ "$statuslines_count_okay" -ne "$statuslines_count_total" ]; then
+
+ # We have some status lines that aren't "ok". Any available details are in API response fields "status" "errcode" and "errtext"
+ # Workaround for basic regex:
+ # - filter output to keep only lines like this: "SPACEStextSPACES" (shouldn't be necessary with prettyprint but guarantees subsequent code is ok)
+ # - then edit the 3 "useful" error tokens individually and remove closing tags on all lines
+ # - then filter again to remove all lines not edited (which will be the lines not starting A-Z)
+ errtext="$(
+ _value "$pleskxml_prettyprint_result" |
+ grep '^ *<[a-z]\{1,\}>[^<]*<\/[a-z]\{1,\}> *$' |
+ sed 's/^ */Status: /;s/^ */Error code: /;s/^ */Error text: /;s/<\/.*$//' |
+ grep '^[A-Z]'
+ )"
+
+ fi
+
+ if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then
+ # Call failed, for reasons either in the retcode or the response text...
+
+ if [ "$pleskxml_retcode" -eq 0 ]; then
+ _err "The POST request was successfully sent to the Plesk server."
+ else
+ _err "The return code for the POST request was $pleskxml_retcode (non-zero = failure in submitting request to server)."
+ fi
+
+ if [ "$errtext" != "" ]; then
+ _err 'The error responses received from the Plesk server were:'
+ _err "$errtext"
+ else
+ _err "No additional error messages were received back from the Plesk server"
+ fi
+
+ _err "The Plesk XML API call failed."
+ return 1
+
+ fi
+
+ _debug "Leaving _call_api(). Successful call."
+
+ return 0
+}
+
+# Startup checks (credentials, URI)
+_credential_check() {
+ _debug "Checking Plesk XML API login credentials and URI..."
+
+ if [ "$pleskxml_init_checks_done" -eq 1 ]; then
+ _debug "Initial checks already done, no need to repeat. Skipped."
+ return 0
+ fi
+
+ pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}"
+ pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}"
+ pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}"
+
+ if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then
+ pleskxml_user=""
+ pleskxml_pass=""
+ pleskxml_uri=""
+ _err "You didn't specify one or more of the Plesk XML API username, password, or URI."
+ _err "Please create these and try again."
+ _err "Instructions are in the 'dns_pleskxml' plugin source code or in the acme.sh documentation."
+ return 1
+ fi
+
+ # Test the API is usable, by trying to read the list of managed domains...
+ _call_api "$pleskxml_tplt_get_domains"
+ if [ "$pleskxml_retcode" -ne 0 ]; then
+ _err 'Failed to access Plesk XML API.'
+ _err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable pleskxml_uri "$pleskxml_uri"
+ _saveaccountconf_mutable pleskxml_user "$pleskxml_user"
+ _saveaccountconf_mutable pleskxml_pass "$pleskxml_pass"
+
+ _debug "Test login to Plesk XML API successful. Login credentials and URI successfully saved to the acme.sh configuration file for future use."
+
+ pleskxml_init_checks_done=1
+
+ return 0
+}
+
+# For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any.
+
+# IMPORTANT NOTE: a result with host = empty string is OK for this API, see
+# https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798
+# See notes at top of this file
+
+_pleskxml_get_root_domain() {
+ original_full_domain_name="$1"
+
+ _debug "Identifying DNS root domain for '$original_full_domain_name' that is managed by the Plesk account."
+
+ # test if the domain as provided is valid for splitting.
+
+ if [ "$(_countdots "$original_full_domain_name")" -eq 0 ]; then
+ _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record."
+ return 1
+ fi
+
+ _debug "Querying Plesk server for list of managed domains..."
+
+ _call_api "$pleskxml_tplt_get_domains"
+ if [ "$pleskxml_retcode" -ne 0 ]; then
+ return 1
+ fi
+
+ # Generate a crude list of domains known to this Plesk account.
+ # We convert tags to so it'll flag on a hit with either or fields,
+ # for non-Western character sets.
+ # Output will be one line per known domain, containing 2 tages and a single tag
+ # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
+
+ output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')"
+
+ _debug 'Domains managed by Plesk server are (ignore the hacked output):'
+ _debug "$output"
+
+ # loop and test if domain, or any parent domain, is managed by Plesk
+ # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain
+
+ root_domain_name="$original_full_domain_name"
+
+ while true; do
+
+ _debug "Checking if '$root_domain_name' is managed by the Plesk server..."
+
+ root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/')"
+
+ if [ -n "$root_domain_id" ]; then
+ # Found a match
+ # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT.
+ # SO WE HANDLE IT AND DON'T PREVENT IT
+ sub_domain_name="$(_value "$original_full_domain_name" | sed "s/\.\{0,1\}${root_domain_name}"'$//')"
+ _info "Success. Matched host '$original_full_domain_name' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning."
+ return 0
+ fi
+
+ # No match, try next parent up (if any)...
+
+ root_domain_name="$(_valuecut 2 1000 "$root_domain_name")"
+
+ if [ "$(_countdots "$root_domain_name")" -eq 0 ]; then
+ _debug "No match, and next parent would be a TLD..."
+ _err "Cannot find '$original_full_domain_name' or any parent domain of it, in Plesk."
+ _err "Are you sure that this domain is managed by this Plesk server?"
+ return 1
+ fi
+
+ _debug "No match, trying next parent up..."
+
+ done
+}
diff --git a/acme.sh-master/dnsapi/dns_pointhq.sh b/acme.sh-master/dnsapi/dns_pointhq.sh
new file mode 100644
index 0000000..6231310
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_pointhq.sh
@@ -0,0 +1,164 @@
+#!/usr/bin/env sh
+
+#
+#PointHQ_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#PointHQ_Email="xxxx@sss.com"
+
+PointHQ_Api="https://api.pointhq.com"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_pointhq_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
+ PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
+ if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
+ PointHQ_Key=""
+ PointHQ_Email=""
+ _err "You didn't specify a PointHQ API key and email yet."
+ _err "Please create the key and try again."
+ return 1
+ fi
+
+ if ! _contains "$PointHQ_Email" "@"; then
+ _err "It seems that the PointHQ_Email=$PointHQ_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.
+ _saveaccountconf_mutable PointHQ_Key "$PointHQ_Key"
+ _saveaccountconf_mutable PointHQ_Email "$PointHQ_Email"
+
+ _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"
+
+ _info "Adding record"
+ if _pointhq_rest POST "zones/$_domain/records" "{\"zone_record\": {\"name\":\"$_sub_domain\",\"record_type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":3600}}"; then
+ if printf -- "%s" "$response" | grep "$fulldomain" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_pointhq_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ PointHQ_Key="${PointHQ_Key:-$(_readaccountconf_mutable PointHQ_Key)}"
+ PointHQ_Email="${PointHQ_Email:-$(_readaccountconf_mutable PointHQ_Email)}"
+ if [ -z "$PointHQ_Key" ] || [ -z "$PointHQ_Email" ]; then
+ PointHQ_Key=""
+ PointHQ_Email=""
+ _err "You didn't specify a PointHQ API key and email yet."
+ _err "Please create the key and try again."
+ 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"
+ _pointhq_rest GET "zones/${_domain}/records?record_type=TXT&name=$_sub_domain"
+
+ if ! printf "%s" "$response" | grep "^\[" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+
+ if [ "$response" = "[]" ]; then
+ _info "No records to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | cut -d : -f 2 | tr -d \" | head -n 1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _pointhq_rest DELETE "zones/$_domain/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" '"status":"OK"'
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _pointhq_rest GET "zones"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_pointhq_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ _pointhq_auth=$(printf "%s:%s" "$PointHQ_Email" "$PointHQ_Key" | _base64)
+
+ export _H1="Authorization: Basic $_pointhq_auth"
+ export _H2="Content-Type: application/json"
+ export _H3="Accept: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$PointHQ_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$PointHQ_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_porkbun.sh b/acme.sh-master/dnsapi/dns_porkbun.sh
new file mode 100644
index 0000000..ad4455b
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_porkbun.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+#
+#PORKBUN_API_KEY="pk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+#PORKBUN_SECRET_API_KEY="sk1_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+
+PORKBUN_Api="https://porkbun.com/api/json/v3"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_porkbun_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
+ PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
+
+ if [ -z "$PORKBUN_API_KEY" ] || [ -z "$PORKBUN_SECRET_API_KEY" ]; then
+ PORKBUN_API_KEY=''
+ PORKBUN_SECRET_API_KEY=''
+ _err "You didn't specify a Porkbun api key and secret api key yet."
+ _err "You can get yours from here https://porkbun.com/account/api."
+ return 1
+ fi
+
+ #save the credentials to the account conf file.
+ _saveaccountconf_mutable PORKBUN_API_KEY "$PORKBUN_API_KEY"
+ _saveaccountconf_mutable PORKBUN_SECRET_API_KEY "$PORKBUN_SECRET_API_KEY"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ # 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 _porkbun_rest POST "dns/create/$_domain" "{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+ if _contains "$response" '\"status\":"SUCCESS"'; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" "The 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."
+ return 1
+
+}
+
+#fulldomain txtvalue
+dns_porkbun_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ PORKBUN_API_KEY="${PORKBUN_API_KEY:-$(_readaccountconf_mutable PORKBUN_API_KEY)}"
+ PORKBUN_SECRET_API_KEY="${PORKBUN_SECRET_API_KEY:-$(_readaccountconf_mutable PORKBUN_SECRET_API_KEY)}"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ count=$(echo "$response" | _egrep_o "\"count\": *[^,]*" | cut -d : -f 2 | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(echo "$response" | tr '{' '\n' | grep -- "$txtvalue" | cut -d, -f1 | cut -d: -f2 | tr -d \")
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _porkbun_rest POST "dns/delete/$_domain/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ echo "$response" | tr -d " " | grep '\"status\":"SUCCESS"' >/dev/null
+ fi
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ if _porkbun_rest POST "dns/retrieve/$h"; then
+ if _contains "$response" "\"status\":\"SUCCESS\""; then
+ _domain=$h
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+ return 0
+ else
+ _debug "Go to next level of $_domain"
+ fi
+ else
+ _debug "Go to next level of $_domain"
+ fi
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_porkbun_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ api_key_trimmed=$(echo "$PORKBUN_API_KEY" | tr -d '"')
+ secret_api_key_trimmed=$(echo "$PORKBUN_SECRET_API_KEY" | tr -d '"')
+
+ test -z "$data" && data="{" || data="$(echo $data | cut -d'}' -f1),"
+ data="$data\"apikey\":\"$api_key_trimmed\",\"secretapikey\":\"$secret_api_key_trimmed\"}"
+
+ export _H1="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$PORKBUN_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$PORKBUN_Api/$ep")"
+ fi
+
+ _sleep 3 # prevent rate limit
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_rackcorp.sh b/acme.sh-master/dnsapi/dns_rackcorp.sh
new file mode 100644
index 0000000..6aabfdd
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_rackcorp.sh
@@ -0,0 +1,156 @@
+#!/usr/bin/env sh
+
+# Provider: RackCorp (www.rackcorp.com)
+# Author: Stephen Dendtler (sdendtler@rackcorp.com)
+# Report Bugs here: https://github.com/senjoo/acme.sh
+# Alternate email contact: support@rackcorp.com
+#
+# You'll need an API key (Portal: ADMINISTRATION -> API)
+# Set the environment variables as below:
+#
+# export RACKCORP_APIUUID="UUIDHERE"
+# export RACKCORP_APISECRET="SECRETHERE"
+#
+
+RACKCORP_API_ENDPOINT="https://api.rackcorp.net/api/rest/v2.4/json.php"
+
+######## Public functions #####################
+
+dns_rackcorp_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ _debug fulldomain="$fulldomain"
+ _debug txtvalue="$txtvalue"
+
+ if ! _rackcorp_validate; then
+ return 1
+ fi
+
+ _debug "Searching for root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _lookup "$_lookup"
+ _debug _domain "$_domain"
+
+ _info "Creating TXT record."
+
+ if ! _rackcorp_api dns.record.create "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\",\"ttl\":300"; then
+ return 1
+ fi
+
+ return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_rackcorp_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _debug fulldomain="$fulldomain"
+ _debug txtvalue="$txtvalue"
+
+ if ! _rackcorp_validate; then
+ return 1
+ fi
+
+ _debug "Searching for root zone"
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _lookup "$_lookup"
+ _debug _domain "$_domain"
+
+ _info "Creating TXT record."
+
+ if ! _rackcorp_api dns.record.delete "\"name\":\"$_domain\",\"type\":\"TXT\",\"lookup\":\"$_lookup\",\"data\":\"$txtvalue\""; then
+ return 1
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.domain.com
+#returns
+# _lookup=_acme-challenge
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+ if ! _rackcorp_api dns.domain.getall "\"name\":\"$domain\""; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug searchhost "$h"
+ if [ -z "$h" ]; then
+ _err "Could not find domain for record $domain in RackCorp using the provided credentials"
+ #not valid
+ return 1
+ fi
+
+ _rackcorp_api dns.domain.getall "\"exactName\":\"$h\""
+
+ if _contains "$response" "\"matches\":1"; then
+ if _contains "$response" "\"name\":\"$h\""; then
+ _lookup=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_rackcorp_validate() {
+ RACKCORP_APIUUID="${RACKCORP_APIUUID:-$(_readaccountconf_mutable RACKCORP_APIUUID)}"
+ if [ -z "$RACKCORP_APIUUID" ]; then
+ RACKCORP_APIUUID=""
+ _err "You require a RackCorp API UUID (export RACKCORP_APIUUID=\"\")"
+ _err "Please login to the portal and create an API key and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable RACKCORP_APIUUID "$RACKCORP_APIUUID"
+
+ RACKCORP_APISECRET="${RACKCORP_APISECRET:-$(_readaccountconf_mutable RACKCORP_APISECRET)}"
+ if [ -z "$RACKCORP_APISECRET" ]; then
+ RACKCORP_APISECRET=""
+ _err "You require a RackCorp API secret (export RACKCORP_APISECRET=\"\")"
+ _err "Please login to the portal and create an API key and try again."
+ return 1
+ fi
+
+ _saveaccountconf_mutable RACKCORP_APISECRET "$RACKCORP_APISECRET"
+
+ return 0
+}
+_rackcorp_api() {
+ _rackcorpcmd=$1
+ _rackcorpinputdata=$2
+ _debug cmd "$_rackcorpcmd $_rackcorpinputdata"
+
+ export _H1="Accept: application/json"
+ response="$(_post "{\"APIUUID\":\"$RACKCORP_APIUUID\",\"APISECRET\":\"$RACKCORP_APISECRET\",\"cmd\":\"$_rackcorpcmd\",$_rackcorpinputdata}" "$RACKCORP_API_ENDPOINT" "" "POST")"
+
+ if [ "$?" != "0" ]; then
+ _err "error $response"
+ return 1
+ fi
+ _debug2 response "$response"
+ if _contains "$response" "\"code\":\"OK\""; then
+ _debug code "OK"
+ else
+ _debug code "FAILED"
+ response=""
+ return 1
+ fi
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_rackspace.sh b/acme.sh-master/dnsapi/dns_rackspace.sh
new file mode 100644
index 0000000..b50d916
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_rackspace.sh
@@ -0,0 +1,208 @@
+#!/usr/bin/env sh
+#
+#
+#RACKSPACE_Username=""
+#
+#RACKSPACE_Apikey=""
+
+RACKSPACE_Endpoint="https://dns.api.rackspacecloud.com/v1.0"
+
+# 20210923 - RS changed the fields in the API response; fix sed
+# 20190213 - The name & id fields swapped in the API response; fix sed
+# 20190101 - Duplicating file for new pull request to dev branch
+# Original - tcocca:rackspace_dnsapi https://github.com/acmesh-official/acme.sh/pull/1297
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_rackspace_add() {
+ fulldomain="$1"
+ _debug fulldomain="$fulldomain"
+ txtvalue="$2"
+ _debug txtvalue="$txtvalue"
+ _rackspace_check_auth || return 1
+ _rackspace_check_rootzone || return 1
+ _info "Creating TXT record."
+ if ! _rackspace_rest POST "$RACKSPACE_Tenant/domains/$_domain_id/records" "{\"records\":[{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":300}]}"; then
+ return 1
+ fi
+ _debug2 response "$response"
+ if ! _contains "$response" "$txtvalue" >/dev/null; then
+ _err "Could not add TXT record."
+ return 1
+ fi
+ return 0
+}
+
+#fulldomain txtvalue
+dns_rackspace_rm() {
+ fulldomain=$1
+ _debug fulldomain="$fulldomain"
+ txtvalue=$2
+ _debug txtvalue="$txtvalue"
+ _rackspace_check_auth || return 1
+ _rackspace_check_rootzone || return 1
+ _info "Checking for TXT record."
+ if ! _get_recordid "$_domain_id" "$fulldomain" "$txtvalue"; then
+ _err "Could not get TXT record id."
+ return 1
+ fi
+ if [ "$_dns_record_id" = "" ]; then
+ _err "TXT record not found."
+ return 1
+ fi
+ _info "Removing TXT record."
+ if ! _delete_txt_record "$_domain_id" "$_dns_record_id"; then
+ _err "Could not remove TXT record $_dns_record_id."
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root_zone() {
+ domain="$1"
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+ if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/search?name=$h"; then
+ return 1
+ fi
+ _debug2 response "$response"
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ # Response looks like:
+ # {"id":"12345","accountId":"1111111","name": "example.com","ttl":3600,"emailAddress": ...
+ _domain_id=$(echo "$response" | sed -n "s/^.*\"id\":\"\([^,]*\)\",\"accountId\":\"[0-9]*\",\"name\":\"$h\",.*/\1/p")
+ _debug2 domain_id "$_domain_id"
+ if [ -n "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_get_recordid() {
+ domainid="$1"
+ fulldomain="$2"
+ txtvalue="$3"
+ if ! _rackspace_rest GET "$RACKSPACE_Tenant/domains/$domainid/records?name=$fulldomain&type=TXT"; then
+ return 1
+ fi
+ _debug response "$response"
+ if ! _contains "$response" "$txtvalue"; then
+ _dns_record_id=0
+ return 0
+ fi
+ _dns_record_id=$(echo "$response" | tr '{' "\n" | grep "\"data\":\"$txtvalue\"" | sed -n 's/^.*"id":"\([^"]*\)".*/\1/p')
+ _debug _dns_record_id "$_dns_record_id"
+ return 0
+}
+
+_delete_txt_record() {
+ domainid="$1"
+ _dns_record_id="$2"
+ if ! _rackspace_rest DELETE "$RACKSPACE_Tenant/domains/$domainid/records?id=$_dns_record_id"; then
+ return 1
+ fi
+ _debug response "$response"
+ if ! _contains "$response" "RUNNING"; then
+ return 1
+ fi
+ return 0
+}
+
+_rackspace_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ _debug ep "$ep"
+ export _H1="Accept: application/json"
+ export _H2="X-Auth-Token: $RACKSPACE_Token"
+ export _H3="X-Project-Id: $RACKSPACE_Tenant"
+ export _H4="Content-Type: application/json"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$RACKSPACE_Endpoint/$ep" "" "$m")"
+ retcode=$?
+ else
+ _info "Getting $RACKSPACE_Endpoint/$ep"
+ response="$(_get "$RACKSPACE_Endpoint/$ep")"
+ retcode=$?
+ fi
+
+ if [ "$retcode" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
+
+_rackspace_authorization() {
+ export _H1="Content-Type: application/json"
+ data="{\"auth\":{\"RAX-KSKEY:apiKeyCredentials\":{\"username\":\"$RACKSPACE_Username\",\"apiKey\":\"$RACKSPACE_Apikey\"}}}"
+ _debug data "$data"
+ response="$(_post "$data" "https://identity.api.rackspacecloud.com/v2.0/tokens" "" "POST")"
+ retcode=$?
+ _debug2 response "$response"
+ if [ "$retcode" != "0" ]; then
+ _err "Authentication failed."
+ return 1
+ fi
+ if _contains "$response" "token"; then
+ RACKSPACE_Token="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)",".*/\1/p')"
+ RACKSPACE_Tenant="$(echo "$response" | _normalizeJson | sed -n 's/^.*"token":{.*,"id":"\([^"]*\)"}.*/\1/p')"
+ _debug RACKSPACE_Token "$RACKSPACE_Token"
+ _debug RACKSPACE_Tenant "$RACKSPACE_Tenant"
+ fi
+ return 0
+}
+
+_rackspace_check_auth() {
+ # retrieve the rackspace creds
+ RACKSPACE_Username="${RACKSPACE_Username:-$(_readaccountconf_mutable RACKSPACE_Username)}"
+ RACKSPACE_Apikey="${RACKSPACE_Apikey:-$(_readaccountconf_mutable RACKSPACE_Apikey)}"
+ # check their vals for null
+ if [ -z "$RACKSPACE_Username" ] || [ -z "$RACKSPACE_Apikey" ]; then
+ RACKSPACE_Username=""
+ RACKSPACE_Apikey=""
+ _err "You didn't specify a Rackspace username and api key."
+ _err "Please set those values and try again."
+ return 1
+ fi
+ # save the username and api key to the account conf file.
+ _saveaccountconf_mutable RACKSPACE_Username "$RACKSPACE_Username"
+ _saveaccountconf_mutable RACKSPACE_Apikey "$RACKSPACE_Apikey"
+ if [ -z "$RACKSPACE_Token" ]; then
+ _info "Getting authorization token."
+ if ! _rackspace_authorization; then
+ _err "Can not get token."
+ fi
+ fi
+}
+
+_rackspace_check_rootzone() {
+ _debug "First detect the root zone"
+ if ! _get_root_zone "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+}
diff --git a/acme.sh-master/dnsapi/dns_rage4.sh b/acme.sh-master/dnsapi/dns_rage4.sh
new file mode 100644
index 0000000..4af4541
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_rage4.sh
@@ -0,0 +1,115 @@
+#!/usr/bin/env sh
+
+#
+#RAGE4_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#RAGE4_USERNAME="xxxx@sss.com"
+
+RAGE4_Api="https://rage4.com/rapi/"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_rage4_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ unquotedtxtvalue=$(echo "$txtvalue" | tr -d \")
+
+ RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
+ RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
+
+ if [ -z "$RAGE4_USERNAME" ] || [ -z "$RAGE4_TOKEN" ]; then
+ RAGE4_USERNAME=""
+ RAGE4_TOKEN=""
+ _err "You didn't specify a Rage4 api token and username yet."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable RAGE4_USERNAME "$RAGE4_USERNAME"
+ _saveaccountconf_mutable RAGE4_TOKEN "$RAGE4_TOKEN"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+
+ _rage4_rest "createrecord/?id=$_domain_id&name=$fulldomain&content=$unquotedtxtvalue&type=TXT&active=true&ttl=1"
+ return 0
+}
+
+#fulldomain txtvalue
+dns_rage4_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ RAGE4_USERNAME="${RAGE4_USERNAME:-$(_readaccountconf_mutable RAGE4_USERNAME)}"
+ RAGE4_TOKEN="${RAGE4_TOKEN:-$(_readaccountconf_mutable RAGE4_TOKEN)}"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+
+ _debug "Getting txt records"
+ _rage4_rest "getrecords/?id=${_domain_id}"
+
+ _record_id=$(echo "$response" | sed -rn 's/.*"id":([[:digit:]]+)[^\}]*'"$txtvalue"'.*/\1/p')
+ _rage4_rest "deleterecord/?id=${_record_id}"
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+
+ if ! _rage4_rest "getdomains"; then
+ return 1
+ fi
+ _debug _get_root_domain "$domain"
+
+ for line in $(echo "$response" | tr '}' '\n'); do
+ __domain=$(echo "$line" | sed -rn 's/.*"name":"([^"]*)",.*/\1/p')
+ __domain_id=$(echo "$line" | sed -rn 's/.*"id":([^,]*),.*/\1/p')
+ if [ "$domain" != "${domain%"$__domain"*}" ]; then
+ _domain_id="$__domain_id"
+ break
+ fi
+ done
+
+ if [ -z "$_domain_id" ]; then
+ return 1
+ fi
+
+ return 0
+}
+
+_rage4_rest() {
+ ep="$1"
+ _debug "$ep"
+
+ username_trimmed=$(echo "$RAGE4_USERNAME" | tr -d '"')
+ token_trimmed=$(echo "$RAGE4_TOKEN" | tr -d '"')
+ auth=$(printf '%s:%s' "$username_trimmed" "$token_trimmed" | _base64)
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: Basic $auth"
+
+ response="$(_get "$RAGE4_Api$ep")"
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_rcode0.sh b/acme.sh-master/dnsapi/dns_rcode0.sh
new file mode 100644
index 0000000..d3f7f21
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_rcode0.sh
@@ -0,0 +1,224 @@
+#!/usr/bin/env sh
+
+#Rcode0 API Integration
+#https://my.rcodezero.at/api-doc
+#
+# log into https://my.rcodezero.at/enableapi and get your ACME API Token (the ACME API token has limited
+# access to the REST calls needed for acme.sh only)
+#
+#RCODE0_URL="https://my.rcodezero.at"
+#RCODE0_API_TOKEN="0123456789ABCDEF"
+#RCODE0_TTL=60
+
+DEFAULT_RCODE0_URL="https://my.rcodezero.at"
+DEFAULT_RCODE0_TTL=60
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
+#fulldomain
+#txtvalue
+dns_rcode0_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
+ RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
+ RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
+
+ if [ -z "$RCODE0_URL" ]; then
+ RCODE0_URL="$DEFAULT_RCODE0_URL"
+ fi
+
+ if [ -z "$RCODE0_API_TOKEN" ]; then
+ RCODE0_API_TOKEN=""
+ _err "Missing Rcode0 ACME API Token."
+ _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
+ return 1
+ fi
+
+ if [ -z "$RCODE0_TTL" ]; then
+ RCODE0_TTL="$DEFAULT_RCODE0_TTL"
+ fi
+
+ #save the token to the account conf file.
+ _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
+
+ if [ "$RCODE0_URL" != "$DEFAULT_RCODE0_URL" ]; then
+ _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
+ fi
+
+ if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
+ _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "No 'MASTER' zone for $fulldomain found at RcodeZero Anycast."
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ _debug "Adding record"
+
+ _record_string=""
+ _build_record_string "$txtvalue"
+ _list_existingchallenges
+ for oldchallenge in $_existing_challenges; do
+ _build_record_string "$oldchallenge"
+ done
+
+ _debug "Challenges: $_existing_challenges"
+
+ if [ -z "$_existing_challenges" ]; then
+ if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"add\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
+ _err "Add txt record error."
+ return 1
+ fi
+ else
+ # try update in case a records exists (need for wildcard certs)
+ if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
+ _err "Set txt record error."
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
+#fulldomain txtvalue
+dns_rcode0_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}"
+ RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}"
+ RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}"
+
+ if [ -z "$RCODE0_URL" ]; then
+ RCODE0_URL="$DEFAULT_RCODE0_URL"
+ fi
+
+ if [ -z "$RCODE0_API_TOKEN" ]; then
+ RCODE0_API_TOKEN=""
+ _err "Missing Rcode0 API Token."
+ _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again."
+ return 1
+ fi
+
+ #save the api addr and key to the account conf file.
+ _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL"
+ _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN"
+
+ if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then
+ _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL"
+ fi
+
+ if [ -z "$RCODE0_TTL" ]; then
+ RCODE0_TTL="$DEFAULT_RCODE0_TTL"
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug "Remove record"
+
+ #Enumerate existing acme challenges
+ _list_existingchallenges
+
+ if _contains "$_existing_challenges" "$txtvalue"; then
+ #Delete all challenges (PowerDNS API does not allow to delete content)
+ if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"delete\", \"name\": \"$fulldomain.\", \"type\": \"TXT\"}]"; then
+ _err "Delete txt record error."
+ return 1
+ fi
+ _record_string=""
+ #If the only existing challenge was the challenge to delete: nothing to do
+ if ! [ "$_existing_challenges" = "$txtvalue" ]; then
+ for oldchallenge in $_existing_challenges; do
+ #Build up the challenges to re-add, ommitting the one what should be deleted
+ if ! [ "$oldchallenge" = "$txtvalue" ]; then
+ _build_record_string "$oldchallenge"
+ fi
+ done
+ #Recreate the existing challenges
+ if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then
+ _err "Set txt record error."
+ return 1
+ fi
+ fi
+ else
+ _info "Record not found, nothing to remove"
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+
+ _debug "try to find: $h"
+ if _rcode0_rest "GET" "/api/v1/acme/zones/$h"; then
+ if [ "$response" = "[\"found\"]" ]; then
+ _domain="$h"
+ if [ -z "$h" ]; then
+ _domain="=2E"
+ fi
+ return 0
+ elif [ "$response" = "[\"not a master domain\"]" ]; then
+ return 1
+ fi
+ fi
+
+ if [ -z "$h" ]; then
+ return 1
+ fi
+ i=$(_math $i + 1)
+ done
+ _debug "no matching domain for $domain found"
+
+ return 1
+}
+
+_rcode0_rest() {
+ method=$1
+ ep=$2
+ data=$3
+
+ export _H1="Authorization: Bearer $RCODE0_API_TOKEN"
+
+ if [ ! "$method" = "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$RCODE0_URL$ep" "" "$method")"
+ else
+ response="$(_get "$RCODE0_URL$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+
+ return 0
+}
+
+_build_record_string() {
+ _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}"
+}
+
+_list_existingchallenges() {
+ _rcode0_rest "GET" "/api/v1/acme/zones/$_domain/rrsets"
+ _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p')
+ _debug2 "$_existing_challenges"
+}
diff --git a/acme.sh-master/dnsapi/dns_regru.sh b/acme.sh-master/dnsapi/dns_regru.sh
new file mode 100644
index 0000000..8ff380f
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_regru.sh
@@ -0,0 +1,127 @@
+#!/usr/bin/env sh
+
+#
+# REGRU_API_Username="test"
+#
+# REGRU_API_Password="test"
+#
+
+REGRU_API_URL="https://api.reg.ru/api/regru2"
+
+######## Public functions #####################
+
+dns_regru_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}"
+ REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}"
+ if [ -z "$REGRU_API_Username" ] || [ -z "$REGRU_API_Password" ]; then
+ REGRU_API_Username=""
+ REGRU_API_Password=""
+ _err "You don't specify regru password or username."
+ return 1
+ fi
+
+ _saveaccountconf_mutable REGRU_API_Username "$REGRU_API_Username"
+ _saveaccountconf_mutable REGRU_API_Password "$REGRU_API_Password"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ _subdomain=$(echo "$fulldomain" | sed -r "s/.$_domain//")
+ _debug _subdomain "$_subdomain"
+
+ _info "Adding TXT record to ${fulldomain}"
+ _regru_rest POST "zone/add_txt" "input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22${_subdomain}%22,%22text%22:%22${txtvalue}%22,%22output_content_type%22:%22plain%22}&input_format=json"
+
+ if ! _contains "${response}" 'error'; then
+ return 0
+ fi
+ _err "Could not create resource record, check logs"
+ _err "${response}"
+ return 1
+}
+
+dns_regru_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}"
+ REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}"
+ if [ -z "$REGRU_API_Username" ] || [ -z "$REGRU_API_Password" ]; then
+ REGRU_API_Username=""
+ REGRU_API_Password=""
+ _err "You don't specify regru password or username."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ _subdomain=$(echo "$fulldomain" | sed -r "s/.$_domain//")
+ _debug _subdomain "$_subdomain"
+
+ _info "Deleting resource record $fulldomain"
+ _regru_rest POST "zone/remove_record" "input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22${_subdomain}%22,%22content%22:%22${txtvalue}%22,%22record_type%22:%22TXT%22,%22output_content_type%22:%22plain%22}&input_format=json"
+
+ if ! _contains "${response}" 'error'; then
+ return 0
+ fi
+ _err "Could not delete resource record, check logs"
+ _err "${response}"
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain=domain.com
+_get_root() {
+ domain=$1
+
+ _regru_rest POST "service/get_list" "username=${REGRU_API_Username}&password=${REGRU_API_Password}&output_format=xml&servtype=domain"
+ domains_list=$(echo "${response}" | grep dname | sed -r "s/.*dname=\"([^\"]+)\".*/\\1/g")
+
+ for ITEM in ${domains_list}; do
+ IDN_ITEM=${ITEM}
+ case "${domain}" in
+ *${IDN_ITEM}*)
+ _domain="$(_idn "${ITEM}")"
+ _debug _domain "${_domain}"
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+#returns
+# response
+_regru_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Content-Type: application/x-www-form-urlencoded"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$REGRU_API_URL/$ep" "" "$m")"
+ else
+ response="$(_get "$REGRU_API_URL/$ep?$data")"
+ fi
+
+ _debug response "${response}"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_scaleway.sh b/acme.sh-master/dnsapi/dns_scaleway.sh
new file mode 100644
index 0000000..a0a0f31
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_scaleway.sh
@@ -0,0 +1,176 @@
+#!/usr/bin/env sh
+
+# Scaleway API
+# https://developers.scaleway.com/en/products/domain/dns/api/
+#
+# Requires Scaleway API token set in SCALEWAY_API_TOKEN
+
+######## Public functions #####################
+
+SCALEWAY_API="https://api.scaleway.com/domain/v2beta1"
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_scaleway_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _scaleway_check_config; 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"
+
+ _info "Adding record"
+ _scaleway_create_TXT_record "$_domain" "$_sub_domain" "$txtvalue"
+ if _contains "$response" "records"; then
+ return 0
+ else
+ _err error "$response"
+ return 1
+ fi
+ _info "Record added."
+
+ return 0
+}
+
+dns_scaleway_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _scaleway_check_config; 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"
+
+ _info "Deleting record"
+ _scaleway_delete_TXT_record "$_domain" "$_sub_domain" "$txtvalue"
+ if _contains "$response" "records"; then
+ return 0
+ else
+ _err error "$response"
+ return 1
+ fi
+ _info "Record deleted."
+
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_scaleway_check_config() {
+ SCALEWAY_API_TOKEN="${SCALEWAY_API_TOKEN:-$(_readaccountconf_mutable SCALEWAY_API_TOKEN)}"
+ if [ -z "$SCALEWAY_API_TOKEN" ]; then
+ _err "No API key specified for Scaleway API."
+ _err "Create your key and export it as SCALEWAY_API_TOKEN"
+ return 1
+ fi
+ if ! _scaleway_rest GET "dns-zones"; then
+ _err "Invalid API key specified for Scaleway API."
+ return 1
+ fi
+
+ _saveaccountconf_mutable SCALEWAY_API_TOKEN "$SCALEWAY_API_TOKEN"
+
+ return 0
+}
+
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _scaleway_rest GET "dns-zones/$h/records"
+
+ if ! _contains "$response" "subdomain not found" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Unable to retrive DNS zone matching this domain"
+ return 1
+}
+
+# this function add a TXT record
+_scaleway_create_TXT_record() {
+ txt_zone=$1
+ txt_name=$2
+ txt_value=$3
+
+ _scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"add\":{\"records\":[{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\",\"ttl\":60}]}}]}"
+
+ if _contains "$response" "records"; then
+ return 0
+ else
+ _err "error1 $response"
+ return 1
+ fi
+}
+
+# this function delete a TXT record based on name and content
+_scaleway_delete_TXT_record() {
+ txt_zone=$1
+ txt_name=$2
+ txt_value=$3
+
+ _scaleway_rest PATCH "dns-zones/$txt_zone/records" "{\"return_all_records\":false,\"changes\":[{\"delete\":{\"id_fields\":{\"name\":\"$txt_name\",\"data\":\"$txt_value\",\"type\":\"TXT\"}}}]}"
+
+ if _contains "$response" "records"; then
+ return 0
+ else
+ _err "error2 $response"
+ return 1
+ fi
+}
+
+_scaleway_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+ _scaleway_url="$SCALEWAY_API/$ep"
+ _debug2 _scaleway_url "$_scaleway_url"
+ export _H1="x-auth-token: $SCALEWAY_API_TOKEN"
+ export _H2="Accept: application/json"
+ export _H3="Content-Type: application/json"
+
+ if [ "$data" ] || [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$_scaleway_url" "" "$m")"
+ else
+ response="$(_get "$_scaleway_url")"
+ fi
+ if [ "$?" != "0" ] || _contains "$response" "denied_authentication" || _contains "$response" "Method not allowed" || _contains "$response" "json parse error: unexpected EOF"; then
+ _err "error $response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_schlundtech.sh b/acme.sh-master/dnsapi/dns_schlundtech.sh
new file mode 100644
index 0000000..399c50e
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_schlundtech.sh
@@ -0,0 +1,261 @@
+#!/usr/bin/env sh
+# -*- mode: sh; tab-width: 2; indent-tabs-mode: s; coding: utf-8 -*-
+
+# Schlundtech DNS API
+# Author: mod242
+# Created: 2019-40-29
+# Completly based on the autoDNS xml api wrapper by auerswald@gmail.com
+#
+# export SCHLUNDTECH_USER="username"
+# export SCHLUNDTECH_PASSWORD="password"
+#
+# Usage:
+# acme.sh --issue --dns dns_schlundtech -d example.com
+
+SCHLUNDTECH_API="https://gateway.schlundtech.de"
+
+# Arguments:
+# txtdomain
+# txt
+dns_schlundtech_add() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}"
+ SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}"
+
+ if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then
+ _err "You didn't specify schlundtech user and password."
+ return 1
+ fi
+
+ _saveaccountconf_mutable SCHLUNDTECH_USER "$SCHLUNDTECH_USER"
+ _saveaccountconf_mutable SCHLUNDTECH_PASSWORD "$SCHLUNDTECH_PASSWORD"
+
+ _debug "First detect the root zone"
+
+ if ! _get_autodns_zone "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _zone "$_zone"
+ _debug _system_ns "$_system_ns"
+
+ _info "Adding TXT record"
+
+ autodns_response="$(_autodns_zone_update "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
+
+ if [ "$?" -eq "0" ]; then
+ _info "Added, OK"
+ return 0
+ fi
+
+ return 1
+}
+
+# Arguments:
+# txtdomain
+# txt
+dns_schlundtech_rm() {
+ fulldomain="$1"
+ txtvalue="$2"
+
+ SCHLUNDTECH_USER="${SCHLUNDTECH_USER:-$(_readaccountconf_mutable SCHLUNDTECH_USER)}"
+ SCHLUNDTECH_PASSWORD="${SCHLUNDTECH_PASSWORD:-$(_readaccountconf_mutable SCHLUNDTECH_PASSWORD)}"
+
+ if [ -z "$SCHLUNDTECH_USER" ] || [ -z "$SCHLUNDTECH_PASSWORD" ]; then
+ _err "You didn't specify schlundtech user and password."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+
+ if ! _get_autodns_zone "$fulldomain"; then
+ _err "zone not found"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _zone "$_zone"
+ _debug _system_ns "$_system_ns"
+
+ _info "Delete TXT record"
+
+ autodns_response="$(_autodns_zone_cleanup "$_zone" "$_sub_domain" "$txtvalue" "$_system_ns")"
+
+ if [ "$?" -eq "0" ]; then
+ _info "Deleted, OK"
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+# Arguments:
+# fulldomain
+# Returns:
+# _sub_domain=_acme-challenge.www
+# _zone=domain.com
+# _system_ns
+_get_autodns_zone() {
+ domain="$1"
+
+ i=2
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+
+ if [ -z "$h" ]; then
+ # not valid
+ return 1
+ fi
+
+ autodns_response="$(_autodns_zone_inquire "$h")"
+
+ if [ "$?" -ne "0" ]; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ if _contains "$autodns_response" "1" >/dev/null; then
+ _zone="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _system_ns="$(echo "$autodns_response" | _egrep_o '[^<]*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_build_request_auth_xml() {
+ printf "
+ %s
+ %s
+ 10
+ " "$SCHLUNDTECH_USER" "$SCHLUNDTECH_PASSWORD"
+}
+
+# Arguments:
+# zone
+_build_zone_inquire_xml() {
+ printf "
+
+ %s
+
+ 0205
+
+ 1
+ 1
+
+
+ name
+ eq
+ %s
+
+
+ " "$(_build_request_auth_xml)" "$1"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_build_zone_update_xml() {
+ printf "
+
+ %s
+
+ 0202001
+
+
+ %s
+ 600
+ TXT
+ %s
+
+
+
+ %s
+ %s
+
+
+ " "$(_build_request_auth_xml)" "$2" "$3" "$1" "$4"
+}
+
+# Arguments:
+# zone
+_autodns_zone_inquire() {
+ request_data="$(_build_zone_inquire_xml "$1")"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_autodns_zone_update() {
+ request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# zone
+# subdomain
+# txtvalue
+# system_ns
+_autodns_zone_cleanup() {
+ request_data="$(_build_zone_update_xml "$1" "$2" "$3" "$4")"
+ # replace 'rr_add>' with 'rr_rem>' in request_data
+ request_data="$(printf -- "%s" "$request_data" | sed 's/rr_add>/rr_rem>/g')"
+ autodns_response="$(_autodns_api_call "$request_data")"
+ ret="$?"
+
+ printf "%s" "$autodns_response"
+ return "$ret"
+}
+
+# Arguments:
+# request_data
+_autodns_api_call() {
+ request_data="$1"
+
+ _debug request_data "$request_data"
+
+ autodns_response="$(_post "$request_data" "$SCHLUNDTECH_API")"
+ ret="$?"
+
+ _debug autodns_response "$autodns_response"
+
+ if [ "$ret" -ne "0" ]; then
+ _err "error"
+ return 1
+ fi
+
+ if _contains "$autodns_response" "success" >/dev/null; then
+ _info "success"
+ printf "%s" "$autodns_response"
+ return 0
+ fi
+
+ return 1
+}
diff --git a/acme.sh-master/dnsapi/dns_selectel.sh b/acme.sh-master/dnsapi/dns_selectel.sh
new file mode 100644
index 0000000..1b09882
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_selectel.sh
@@ -0,0 +1,161 @@
+#!/usr/bin/env sh
+
+#
+#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+SL_Api="https://api.selectel.ru/domains/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_selectel_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify selectel.ru api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable SL_Key "$SL_Key"
+
+ _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"
+
+ _info "Adding record"
+ if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
+ if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_selectel_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify slectel api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _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"
+ _sl_rest GET "/${_domain_id}/records/"
+
+ if ! _contains "$response" "$txtvalue"; then
+ _err "Txt record not found"
+ return 1
+ fi
+
+ _record_seg="$(echo "$response" | _egrep_o "[^{]*\"content\" *: *\"$txtvalue\"[^}]*}")"
+ _debug2 "_record_seg" "$_record_seg"
+ if [ -z "$_record_seg" ]; then
+ _err "can not find _record_seg"
+ return 1
+ fi
+
+ _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
+ _debug2 "_record_id" "$_record_id"
+ if [ -z "$_record_id" ]; then
+ _err "can not find _record_id"
+ return 1
+ fi
+
+ if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+
+ if ! _sl_rest GET "/"; then
+ return 1
+ fi
+
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\" *: *\"$h\","; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ _debug "Getting domain id for $h"
+ if ! _sl_rest GET "/$h"; then
+ return 1
+ fi
+ _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_sl_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="X-Token: $SL_Key"
+ export _H2="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$SL_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_selfhost.sh b/acme.sh-master/dnsapi/dns_selfhost.sh
new file mode 100644
index 0000000..a6ef1f9
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_selfhost.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env sh
+#
+# Author: Marvin Edeler
+# Report Bugs here: https://github.com/Marvo2011/acme.sh/issues/1
+# Last Edit: 17.02.2022
+
+dns_selfhost_add() {
+ fulldomain=$1
+ txt=$2
+ _info "Calling acme-dns on selfhost"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txt"
+
+ SELFHOSTDNS_UPDATE_URL="https://selfhost.de/cgi-bin/api.pl"
+
+ # Get values, but don't save until we successfully validated
+ SELFHOSTDNS_USERNAME="${SELFHOSTDNS_USERNAME:-$(_readaccountconf_mutable SELFHOSTDNS_USERNAME)}"
+ SELFHOSTDNS_PASSWORD="${SELFHOSTDNS_PASSWORD:-$(_readaccountconf_mutable SELFHOSTDNS_PASSWORD)}"
+ # These values are domain dependent, so read them from there
+ SELFHOSTDNS_MAP="${SELFHOSTDNS_MAP:-$(_readdomainconf SELFHOSTDNS_MAP)}"
+ # Selfhost api can't dynamically add TXT record,
+ # so we have to store the last used RID of the domain to support a second RID for wildcard domains
+ # (format: 'fulldomainA:lastRid fulldomainB:lastRid ...')
+ SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(_readdomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL)
+
+ if [ -z "${SELFHOSTDNS_USERNAME:-}" ] || [ -z "${SELFHOSTDNS_PASSWORD:-}" ]; then
+ _err "SELFHOSTDNS_USERNAME and SELFHOSTDNS_PASSWORD must be set"
+ return 1
+ fi
+
+ # get the domain entry from SELFHOSTDNS_MAP
+ # only match full domains (at the beginning of the string or with a leading whitespace),
+ # e.g. don't match mytest.example.com or sub.test.example.com for test.example.com
+ # if the domain is defined multiple times only the last occurance will be matched
+ mapEntry=$(echo "$SELFHOSTDNS_MAP" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain)(:[[:digit:]]+)([:]?[[:digit:]]*)(.*)/\2\3\4/p")
+ _debug2 mapEntry "$mapEntry"
+ if test -z "$mapEntry"; then
+ _err "SELFHOSTDNS_MAP must contain the fulldomain incl. prefix and at least one RID"
+ return 1
+ fi
+
+ # get the RIDs from the map entry
+ rid1=$(echo "$mapEntry" | cut -d: -f2)
+ rid2=$(echo "$mapEntry" | cut -d: -f3)
+
+ # read last used rid domain
+ lastUsedRidForDomainEntry=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/(^|^.*[[:space:]])($fulldomain:[[:digit:]]+)(.*)/\2/p")
+ _debug2 lastUsedRidForDomainEntry "$lastUsedRidForDomainEntry"
+ lastUsedRidForDomain=$(echo "$lastUsedRidForDomainEntry" | cut -d: -f2)
+
+ rid="$rid1"
+ if [ "$lastUsedRidForDomain" = "$rid" ] && ! test -z "$rid2"; then
+ rid="$rid2"
+ fi
+
+ _info "Trying to add $txt on selfhost for rid: $rid"
+
+ data="?username=$SELFHOSTDNS_USERNAME&password=$SELFHOSTDNS_PASSWORD&rid=$rid&content=$txt"
+ response="$(_get "$SELFHOSTDNS_UPDATE_URL$data")"
+
+ if ! echo "$response" | grep "200 OK" >/dev/null; then
+ _err "Invalid response of acme-dns for selfhost"
+ return 1
+ fi
+
+ # write last used rid domain
+ newLastUsedRidForDomainEntry="$fulldomain:$rid"
+ if ! test -z "$lastUsedRidForDomainEntry"; then
+ # replace last used rid entry for domain
+ SELFHOSTDNS_MAP_LAST_USED_INTERNAL=$(echo "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL" | sed -n -E "s/$lastUsedRidForDomainEntry/$newLastUsedRidForDomainEntry/p")
+ else
+ # add last used rid entry for domain
+ if test -z "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"; then
+ SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$newLastUsedRidForDomainEntry"
+ else
+ SELFHOSTDNS_MAP_LAST_USED_INTERNAL="$SELFHOSTDNS_MAP_LAST_USED_INTERNAL $newLastUsedRidForDomainEntry"
+ fi
+ fi
+
+ # Now that we know the values are good, save them
+ _saveaccountconf_mutable SELFHOSTDNS_USERNAME "$SELFHOSTDNS_USERNAME"
+ _saveaccountconf_mutable SELFHOSTDNS_PASSWORD "$SELFHOSTDNS_PASSWORD"
+ # These values are domain dependent, so store them there
+ _savedomainconf SELFHOSTDNS_MAP "$SELFHOSTDNS_MAP"
+ _savedomainconf SELFHOSTDNS_MAP_LAST_USED_INTERNAL "$SELFHOSTDNS_MAP_LAST_USED_INTERNAL"
+}
+
+dns_selfhost_rm() {
+ fulldomain=$1
+ txt=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txt"
+ _info "Creating and removing of records is not supported by selfhost API, will not delete anything."
+}
diff --git a/acme.sh-master/dnsapi/dns_servercow.sh b/acme.sh-master/dnsapi/dns_servercow.sh
new file mode 100644
index 0000000..5213790
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_servercow.sh
@@ -0,0 +1,196 @@
+#!/usr/bin/env sh
+
+##########
+# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/acmesh-official/acme.sh)
+#
+# Usage:
+# export SERVERCOW_API_Username=username
+# export SERVERCOW_API_Password=password
+# acme.sh --issue -d example.com --dns dns_servercow
+#
+# Issues:
+# Any issues / questions / suggestions can be posted here:
+# https://github.com/jhartlep/servercow-dns-api/issues
+#
+# Author: Jens Hartlep
+##########
+
+SERVERCOW_API="https://api.servercow.de/dns/v1/domains"
+
+# Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz"
+dns_servercow_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using servercow"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
+ SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
+ if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
+ SERVERCOW_API_Username=""
+ SERVERCOW_API_Password=""
+ _err "You don't specify servercow api username and password yet."
+ _err "Please create your username and password and try again."
+ return 1
+ fi
+
+ # save the credentials to the account conf file
+ _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username"
+ _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password"
+
+ _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"
+
+ # check whether a txt record already exists for the subdomain
+ if printf -- "%s" "$response" | grep "{\"name\":\"$_sub_domain\",\"ttl\":20,\"type\":\"TXT\"" >/dev/null; then
+ _info "A txt record with the same name already exists."
+ # trim the string on the left
+ txtvalue_old=${response#*{\"name\":\""$_sub_domain"\",\"ttl\":20,\"type\":\"TXT\",\"content\":\"}
+ # trim the string on the right
+ txtvalue_old=${txtvalue_old%%\"*}
+
+ _debug txtvalue_old "$txtvalue_old"
+
+ _info "Add the new txtvalue to the existing txt record."
+ if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":[\"$txtvalue\",\"$txtvalue_old\"],\"ttl\":20}"; then
+ if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+ _info "Added additional txtvalue, OK"
+ return 0
+ else
+ _err "add txt record error."
+ return 1
+ fi
+ fi
+ _err "add txt record error."
+ return 1
+ else
+ _info "There is no txt record with the name yet."
+ if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
+ if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "add txt record error."
+ return 1
+ fi
+ fi
+ _err "add txt record error."
+ return 1
+ fi
+
+ return 1
+}
+
+# Usage fulldomain txtvalue
+# Remove the txt record after validation
+dns_servercow_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using servercow"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$fulldomain"
+
+ SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
+ SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
+ if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
+ SERVERCOW_API_Username=""
+ SERVERCOW_API_Password=""
+ _err "You don't specify servercow api username and password yet."
+ _err "Please create your username and password and try again."
+ 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"
+
+ if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then
+ if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+ _info "Deleted, OK"
+ _contains "$response" '"message":"ok"'
+ else
+ _err "delete txt record error."
+ return 1
+ fi
+ fi
+
+}
+
+#################### Private functions below ##################################
+
+# _acme-challenge.www.domain.com
+# returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=2
+ p=1
+
+ while true; do
+ _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+
+ _debug _domain "$_domain"
+ if [ -z "$_domain" ]; then
+ # not valid
+ return 1
+ fi
+
+ if ! _servercow_api GET "$_domain"; then
+ return 1
+ fi
+
+ if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then
+ _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+ if [ -z "$_sub_domain" ]; then
+ # not valid
+ return 1
+ fi
+
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_servercow_api() {
+ method=$1
+ domain=$2
+ data="$3"
+
+ export _H1="Content-Type: application/json"
+ export _H2="X-Auth-Username: $SERVERCOW_API_Username"
+ export _H3="X-Auth-Password: $SERVERCOW_API_Password"
+
+ if [ "$method" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")"
+ else
+ response="$(_get "$SERVERCOW_API/$domain")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $domain"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_simply.sh b/acme.sh-master/dnsapi/dns_simply.sh
new file mode 100644
index 0000000..6a8d0e1
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_simply.sh
@@ -0,0 +1,269 @@
+#!/usr/bin/env sh
+
+# API-integration for Simply.com (https://www.simply.com)
+
+#SIMPLY_AccountName="accountname"
+#SIMPLY_ApiKey="apikey"
+#
+#SIMPLY_Api="https://api.simply.com/2/"
+SIMPLY_Api_Default="https://api.simply.com/2"
+
+#This is used for determining success of REST call
+SIMPLY_SUCCESS_CODE='"status":200'
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_simply_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _simply_load_config; then
+ return 1
+ fi
+
+ _simply_save_config
+
+ _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"
+
+ _info "Adding record"
+
+ if ! _simply_add_record "$_domain" "$_sub_domain" "$txtvalue"; then
+ _err "Could not add DNS record"
+ return 1
+ fi
+ return 0
+}
+
+dns_simply_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _simply_load_config; then
+ return 1
+ fi
+
+ _simply_save_config
+
+ _debug "Find the DNS zone"
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+ _debug txtvalue "$txtvalue"
+
+ _info "Getting all existing records"
+
+ if ! _simply_get_all_records "$_domain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ records=$(echo "$response" | tr '{' "\n" | grep 'record_id\|type\|data\|\name' | sed 's/\"record_id/;\"record_id/' | tr "\n" ' ' | tr -d ' ' | tr ';' ' ')
+
+ nr_of_deleted_records=0
+ _info "Fetching txt record"
+
+ for record in $records; do
+ _debug record "$record"
+
+ record_data=$(echo "$record" | sed -n "s/.*\"data\":\"\([^\"]*\)\".*/\1/p")
+ record_type=$(echo "$record" | sed -n "s/.*\"type\":\"\([^\"]*\)\".*/\1/p")
+
+ _debug2 record_data "$record_data"
+ _debug2 record_type "$record_type"
+
+ if [ "$record_data" = "$txtvalue" ] && [ "$record_type" = "TXT" ]; then
+
+ record_id=$(echo "$record" | cut -d "," -f 1 | grep "record_id" | cut -d ":" -f 2)
+
+ _info "Deleting record $record"
+ _debug2 record_id "$record_id"
+
+ if [ "$record_id" -gt 0 ]; then
+
+ if ! _simply_delete_record "$_domain" "$_sub_domain" "$record_id"; then
+ _err "Record with id $record_id could not be deleted"
+ return 1
+ fi
+
+ nr_of_deleted_records=1
+ break
+ else
+ _err "Fetching record_id could not be done, this should not happen, exiting function. Failing record is $record"
+ break
+ fi
+ fi
+
+ done
+
+ if [ "$nr_of_deleted_records" -eq 0 ]; then
+ _err "No record deleted, the DNS record needs to be removed manually."
+ else
+ _info "Deleted $nr_of_deleted_records record"
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+
+_simply_load_config() {
+ SIMPLY_Api="${SIMPLY_Api:-$(_readaccountconf_mutable SIMPLY_Api)}"
+ SIMPLY_AccountName="${SIMPLY_AccountName:-$(_readaccountconf_mutable SIMPLY_AccountName)}"
+ SIMPLY_ApiKey="${SIMPLY_ApiKey:-$(_readaccountconf_mutable SIMPLY_ApiKey)}"
+
+ if [ -z "$SIMPLY_Api" ]; then
+ SIMPLY_Api="$SIMPLY_Api_Default"
+ fi
+
+ if [ -z "$SIMPLY_AccountName" ] || [ -z "$SIMPLY_ApiKey" ]; then
+ SIMPLY_AccountName=""
+ SIMPLY_ApiKey=""
+
+ _err "A valid Simply API account and apikey not provided."
+ _err "Please provide a valid API user and try again."
+
+ return 1
+ fi
+
+ return 0
+}
+
+_simply_save_config() {
+ if [ "$SIMPLY_Api" != "$SIMPLY_Api_Default" ]; then
+ _saveaccountconf_mutable SIMPLY_Api "$SIMPLY_Api"
+ fi
+ _saveaccountconf_mutable SIMPLY_AccountName "$SIMPLY_AccountName"
+ _saveaccountconf_mutable SIMPLY_ApiKey "$SIMPLY_ApiKey"
+}
+
+_simply_get_all_records() {
+ domain=$1
+
+ if ! _simply_rest GET "my/products/$domain/dns/records/"; then
+ return 1
+ fi
+
+ return 0
+}
+
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _simply_rest GET "my/products/$h/dns/"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
+ _debug "$h not found"
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_simply_add_record() {
+ domain=$1
+ sub_domain=$2
+ txtval=$3
+
+ data="{\"name\": \"$sub_domain\", \"type\":\"TXT\", \"data\": \"$txtval\", \"priority\":0, \"ttl\": 3600}"
+
+ if ! _simply_rest POST "my/products/$domain/dns/records/" "$data"; then
+ _err "Adding record not successfull!"
+ return 1
+ fi
+
+ if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
+ _err "Call to API not sucessfull, see below message for more details"
+ _err "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+_simply_delete_record() {
+ domain=$1
+ sub_domain=$2
+ record_id=$3
+
+ _debug record_id "Delete record with id $record_id"
+
+ if ! _simply_rest DELETE "my/products/$domain/dns/records/$record_id/"; then
+ _err "Deleting record not successfull!"
+ return 1
+ fi
+
+ if ! _contains "$response" "$SIMPLY_SUCCESS_CODE"; then
+ _err "Call to API not sucessfull, see below message for more details"
+ _err "$response"
+ return 1
+ fi
+
+ return 0
+}
+
+_simply_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+
+ _debug2 data "$data"
+ _debug2 ep "$ep"
+ _debug2 m "$m"
+
+ basicauth=$(printf "%s:%s" "$SIMPLY_AccountName" "$SIMPLY_ApiKey" | _base64)
+
+ if [ "$basicauth" ]; then
+ export _H1="Authorization: Basic $basicauth"
+ fi
+
+ export _H2="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ response="$(_post "$data" "$SIMPLY_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$SIMPLY_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+
+ response="$(echo "$response" | _normalizeJson)"
+
+ _debug2 response "$response"
+
+ if _contains "$response" "Invalid account authorization"; then
+ _err "It seems that your api key or accountnumber is not correct."
+ return 1
+ fi
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_tele3.sh b/acme.sh-master/dnsapi/dns_tele3.sh
new file mode 100644
index 0000000..76c9091
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_tele3.sh
@@ -0,0 +1,69 @@
+#!/usr/bin/env sh
+#
+# tele3.cz DNS API
+#
+# Author: Roman Blizik
+# Report Bugs here: https://github.com/par-pa/acme.sh
+#
+# --
+# export TELE3_Key="MS2I4uPPaI..."
+# export TELE3_Secret="kjhOIHGJKHg"
+# --
+
+TELE3_API="https://www.tele3.cz/acme/"
+
+######## Public functions #####################
+
+dns_tele3_add() {
+ _info "Using TELE3 DNS"
+ data="\"ope\":\"add\", \"domain\":\"$1\", \"value\":\"$2\""
+ if ! _tele3_call; then
+ _err "Publish zone failed"
+ return 1
+ fi
+
+ _info "Zone published"
+}
+
+dns_tele3_rm() {
+ _info "Using TELE3 DNS"
+ data="\"ope\":\"rm\", \"domain\":\"$1\", \"value\":\"$2\""
+ if ! _tele3_call; then
+ _err "delete TXT record failed"
+ return 1
+ fi
+
+ _info "TXT record successfully deleted"
+}
+
+#################### Private functions below ##################################
+
+_tele3_init() {
+ TELE3_Key="${TELE3_Key:-$(_readaccountconf_mutable TELE3_Key)}"
+ TELE3_Secret="${TELE3_Secret:-$(_readaccountconf_mutable TELE3_Secret)}"
+ if [ -z "$TELE3_Key" ] || [ -z "$TELE3_Secret" ]; then
+ TELE3_Key=""
+ TELE3_Secret=""
+ _err "You must export variables: TELE3_Key and TELE3_Secret"
+ return 1
+ fi
+
+ #save the config variables to the account conf file.
+ _saveaccountconf_mutable TELE3_Key "$TELE3_Key"
+ _saveaccountconf_mutable TELE3_Secret "$TELE3_Secret"
+}
+
+_tele3_call() {
+ _tele3_init
+ data="{\"key\":\"$TELE3_Key\", \"secret\":\"$TELE3_Secret\", $data}"
+
+ _debug data "$data"
+
+ response="$(_post "$data" "$TELE3_API" "" "POST")"
+ _debug response "$response"
+
+ if [ "$response" != "success" ]; then
+ _err "$response"
+ return 1
+ fi
+}
diff --git a/acme.sh-master/dnsapi/dns_transip.sh b/acme.sh-master/dnsapi/dns_transip.sh
new file mode 100644
index 0000000..64a256e
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_transip.sh
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+TRANSIP_Api_Url="https://api.transip.nl/v6"
+TRANSIP_Token_Read_Only="false"
+TRANSIP_Token_Expiration="30 minutes"
+# You can't reuse a label token, so we leave this empty normally
+TRANSIP_Token_Label=""
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_transip_add() {
+ fulldomain="$1"
+ _debug fulldomain="$fulldomain"
+ txtvalue="$2"
+ _debug txtvalue="$txtvalue"
+ _transip_setup "$fulldomain" || return 1
+ _info "Creating TXT record."
+ if ! _transip_rest POST "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
+ _err "Could not add TXT record."
+ return 1
+ fi
+ return 0
+}
+
+dns_transip_rm() {
+ fulldomain=$1
+ _debug fulldomain="$fulldomain"
+ txtvalue=$2
+ _debug txtvalue="$txtvalue"
+ _transip_setup "$fulldomain" || return 1
+ _info "Removing TXT record."
+ if ! _transip_rest DELETE "domains/$_domain/dns" "{\"dnsEntry\":{\"name\":\"$_sub_domain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"expire\":300}}"; then
+ _err "Could not remove TXT record $_sub_domain for $domain"
+ return 1
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+
+ if _transip_rest GET "domains/$h/dns" && _contains "$response" "dnsEntries"; then
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ _err "Unable to parse this domain"
+ return 1
+}
+
+_transip_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+ _debug ep "$ep"
+ export _H1="Accept: application/json"
+ export _H2="Authorization: Bearer $_token"
+ export _H4="Content-Type: application/json"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$TRANSIP_Api_Url/$ep" "" "$m")"
+ retcode=$?
+ else
+ response="$(_get "$TRANSIP_Api_Url/$ep")"
+ retcode=$?
+ fi
+
+ if [ "$retcode" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
+
+_transip_get_token() {
+ nonce=$(echo "TRANSIP$(_time)" | _digest sha1 hex | cut -c 1-32)
+ _debug nonce "$nonce"
+
+ # make IP whitelisting configurable
+ TRANSIP_Token_Global_Key="${TRANSIP_Token_Global_Key:-$(_readaccountconf_mutable TRANSIP_Token_Global_Key)}"
+ _saveaccountconf_mutable TRANSIP_Token_Global_Key "$TRANSIP_Token_Global_Key"
+
+ data="{\"login\":\"${TRANSIP_Username}\",\"nonce\":\"${nonce}\",\"read_only\":\"${TRANSIP_Token_Read_Only}\",\"expiration_time\":\"${TRANSIP_Token_Expiration}\",\"label\":\"${TRANSIP_Token_Label}\",\"global_key\":\"${TRANSIP_Token_Global_Key:-false}\"}"
+ _debug data "$data"
+
+ #_signature=$(printf "%s" "$data" | openssl dgst -sha512 -sign "$TRANSIP_Key_File" | _base64)
+ _signature=$(printf "%s" "$data" | _sign "$TRANSIP_Key_File" "sha512")
+ _debug2 _signature "$_signature"
+
+ export _H1="Signature: $_signature"
+ export _H2="Content-Type: application/json"
+
+ response="$(_post "$data" "$TRANSIP_Api_Url/auth" "" "POST")"
+ retcode=$?
+ _debug2 response "$response"
+ if [ "$retcode" != "0" ]; then
+ _err "Authentication failed."
+ return 1
+ fi
+ if _contains "$response" "token"; then
+ _token="$(echo "$response" | _normalizeJson | sed -n 's/^{"token":"\(.*\)"}/\1/p')"
+ _debug _token "$_token"
+ return 0
+ fi
+ return 1
+}
+
+_transip_setup() {
+ fulldomain=$1
+
+ # retrieve the transip creds
+ TRANSIP_Username="${TRANSIP_Username:-$(_readaccountconf_mutable TRANSIP_Username)}"
+ TRANSIP_Key_File="${TRANSIP_Key_File:-$(_readaccountconf_mutable TRANSIP_Key_File)}"
+ # check their vals for null
+ if [ -z "$TRANSIP_Username" ] || [ -z "$TRANSIP_Key_File" ]; then
+ TRANSIP_Username=""
+ TRANSIP_Key_File=""
+ _err "You didn't specify a TransIP username and api key file location"
+ _err "Please set those values and try again."
+ return 1
+ fi
+ # save the username and api key to the account conf file.
+ _saveaccountconf_mutable TRANSIP_Username "$TRANSIP_Username"
+ _saveaccountconf_mutable TRANSIP_Key_File "$TRANSIP_Key_File"
+
+ # download key file if it's an URL
+ if _startswith "$TRANSIP_Key_File" "http"; then
+ _debug "download transip key file"
+ TRANSIP_Key_URL=$TRANSIP_Key_File
+ TRANSIP_Key_File="$(_mktemp)"
+ chmod 600 "$TRANSIP_Key_File"
+ if ! _get "$TRANSIP_Key_URL" >"$TRANSIP_Key_File"; then
+ _err "Error getting key file from : $TRANSIP_Key_URL"
+ return 1
+ fi
+ fi
+
+ if [ -f "$TRANSIP_Key_File" ]; then
+ if ! grep "BEGIN PRIVATE KEY" "$TRANSIP_Key_File" >/dev/null 2>&1; then
+ _err "Key file doesn't seem to be a valid key: ${TRANSIP_Key_File}"
+ return 1
+ fi
+ else
+ _err "Can't read private key file: ${TRANSIP_Key_File}"
+ return 1
+ fi
+
+ if [ -z "$_token" ]; then
+ if ! _transip_get_token; then
+ _err "Can not get token."
+ return 1
+ fi
+ fi
+
+ if [ -n "${TRANSIP_Key_URL}" ]; then
+ _debug "delete transip key file"
+ rm "${TRANSIP_Key_File}"
+ TRANSIP_Key_File=$TRANSIP_Key_URL
+ fi
+
+ _get_root "$fulldomain" || return 1
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_udr.sh b/acme.sh-master/dnsapi/dns_udr.sh
new file mode 100644
index 0000000..caada82
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_udr.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env sh
+
+# united-domains Reselling (https://www.ud-reselling.com/) DNS API
+# Author: Andreas Scherer (https://github.com/andischerer)
+# Created: 2021-02-01
+#
+# Set the environment variables as below:
+#
+# export UDR_USER="your_username_goes_here"
+# export UDR_PASS="some_password_goes_here"
+#
+
+UDR_API="https://api.domainreselling.de/api/call.cgi"
+UDR_TTL="30"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
+dns_udr_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
+ UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
+ if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
+ UDR_USER=""
+ UDR_PASS=""
+ _err "You didn't specify an UD-Reselling username and password yet"
+ return 1
+ fi
+ # save the username and password to the account conf file.
+ _saveaccountconf_mutable UDR_USER "$UDR_USER"
+ _saveaccountconf_mutable UDR_PASS "$UDR_PASS"
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _dnszone "${_dnszone}"
+
+ _debug "Getting txt records"
+ if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
+ return 1
+ fi
+
+ rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
+ _debug resource_record "${rr}"
+ if _contains "$response" "$rr" >/dev/null; then
+ _err "Error, it would appear that this record already exists. Please review existing TXT records for this domain."
+ return 1
+ fi
+
+ _info "Adding record"
+ if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&addrr0=${rr}"; then
+ _err "Adding the record did not succeed, please verify/check."
+ return 1
+ fi
+
+ _info "Added, OK"
+ return 0
+}
+
+dns_udr_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UDR_USER="${UDR_USER:-$(_readaccountconf_mutable UDR_USER)}"
+ UDR_PASS="${UDR_PASS:-$(_readaccountconf_mutable UDR_PASS)}"
+ if [ -z "$UDR_USER" ] || [ -z "$UDR_PASS" ]; then
+ UDR_USER=""
+ UDR_PASS=""
+ _err "You didn't specify an UD-Reselling username and password yet"
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _dnszone "${_dnszone}"
+
+ _debug "Getting txt records"
+ if ! _udr_rest "QueryDNSZoneRRList" "dnszone=${_dnszone}"; then
+ return 1
+ fi
+
+ rr="${fulldomain}. ${UDR_TTL} IN TXT ${txtvalue}"
+ _debug resource_record "${rr}"
+ if _contains "$response" "$rr" >/dev/null; then
+ if ! _udr_rest "UpdateDNSZone" "dnszone=${_dnszone}&delrr0=${rr}"; then
+ _err "Deleting the record did not succeed, please verify/check."
+ return 1
+ fi
+ _info "Removed, OK"
+ return 0
+ else
+ _info "Text record is not present, will not delete anything."
+ return 0
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+
+ if ! _udr_rest "QueryDNSZoneList" ""; then
+ return 1
+ fi
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "${response}" "${h}." >/dev/null; then
+ _dnszone=$(echo "$response" | _egrep_o "${h}")
+ if [ "$_dnszone" ]; then
+ return 0
+ fi
+ return 1
+ fi
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_udr_rest() {
+ if [ -n "$2" ]; then
+ data="command=$1&$2"
+ else
+ data="command=$1"
+ fi
+
+ _debug data "${data}"
+ response="$(_post "${data}" "${UDR_API}?s_login=${UDR_USER}&s_pw=${UDR_PASS}" "" "POST")"
+
+ _code=$(echo "$response" | _egrep_o "code = ([0-9]+)" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+ _description=$(echo "$response" | _egrep_o "description = .*" | _head_n 1 | cut -d = -f 2 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
+
+ _debug response_code "$_code"
+ _debug response_description "$_description"
+
+ if [ ! "$_code" = "200" ]; then
+ _err "DNS-API-Error: $_description"
+ return 1
+ fi
+
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_ultra.sh b/acme.sh-master/dnsapi/dns_ultra.sh
new file mode 100644
index 0000000..0f26bd9
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_ultra.sh
@@ -0,0 +1,167 @@
+#!/usr/bin/env sh
+
+#
+# ULTRA_USR="your_user_goes_here"
+#
+# ULTRA_PWD="some_password_goes_here"
+
+ULTRA_API="https://api.ultradns.com/v3/"
+ULTRA_AUTH_API="https://api.ultradns.com/v2/"
+
+#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
+dns_ultra_add() {
+ fulldomain=$1
+ txtvalue=$2
+ export txtvalue
+ ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}"
+ ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}"
+ if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then
+ ULTRA_USR=""
+ ULTRA_PWD=""
+ _err "You didn't specify an UltraDNS username and password yet"
+ return 1
+ fi
+ # save the username and password to the account conf file.
+ _saveaccountconf_mutable ULTRA_USR "$ULTRA_USR"
+ _saveaccountconf_mutable ULTRA_PWD "$ULTRA_PWD"
+ _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"
+ _ultra_rest GET "zones/${_domain_id}/rrsets/TXT?q=value:${fulldomain}"
+ if printf "%s" "$response" | grep \"totalCount\" >/dev/null; then
+ _err "Error, it would appear that this record already exists. Please review existing TXT records for this domain."
+ return 1
+ fi
+
+ _info "Adding record"
+ if _ultra_rest POST "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then
+ if _contains "$response" "Successful"; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" "Resource Record of type 16 with these attributes already exists"; then
+ _info "Already exists, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+
+}
+
+dns_ultra_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ export txtvalue
+ ULTRA_USR="${ULTRA_USR:-$(_readaccountconf_mutable ULTRA_USR)}"
+ ULTRA_PWD="${ULTRA_PWD:-$(_readaccountconf_mutable ULTRA_PWD)}"
+ if [ -z "$ULTRA_USR" ] || [ -z "$ULTRA_PWD" ]; then
+ ULTRA_USR=""
+ ULTRA_PWD=""
+ _err "You didn't specify an UltraDNS username and password yet"
+ return 1
+ fi
+
+ _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"
+ _ultra_rest GET "zones/${_domain_id}/rrsets?q=kind:RECORDS+owner:${_sub_domain}"
+
+ if ! printf "%s" "$response" | grep \"resultInfo\" >/dev/null; then
+ _err "There was an error in obtaining the resource records for ${_domain_id}"
+ return 1
+ fi
+
+ count=$(echo "$response" | _egrep_o "\"returnedCount\":[^,]*" | cut -d: -f2 | cut -d'}' -f1)
+ _debug count "${count}"
+ if [ "${count}" = "" ]; then
+ _info "Text record is not present, will not delete anything."
+ else
+ if ! _ultra_rest DELETE "zones/$_domain_id/rrsets/TXT/${_sub_domain}" '{"ttl":300,"rdata":["'"${txtvalue}"'"]}'; then
+ _err "Deleting the record did not succeed, please verify/check."
+ return 1
+ fi
+ _contains "$response" ""
+ 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=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ _debug response "$response"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+ if ! _ultra_rest GET "zones"; then
+ return 1
+ fi
+ if _contains "${response}" "${h}." >/dev/null; then
+ _domain_id=$(echo "$response" | _egrep_o "${h}" | head -1)
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="${h}"
+ _debug sub_domain "${_sub_domain}"
+ _debug domain "${_domain}"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_ultra_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+ if [ -z "$AUTH_TOKEN" ]; then
+ _ultra_login
+ fi
+ _debug TOKEN "$AUTH_TOKEN"
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: Bearer $AUTH_TOKEN"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$ULTRA_API$ep" "" "$m")"
+ else
+ response="$(_get "$ULTRA_API$ep")"
+ fi
+}
+
+_ultra_login() {
+ export _H1=""
+ export _H2=""
+ AUTH_TOKEN=$(_post "grant_type=password&username=${ULTRA_USR}&password=${ULTRA_PWD}" "${ULTRA_AUTH_API}authorization/token" | cut -d, -f3 | cut -d\" -f4)
+ export AUTH_TOKEN
+}
diff --git a/acme.sh-master/dnsapi/dns_unoeuro.sh b/acme.sh-master/dnsapi/dns_unoeuro.sh
new file mode 100644
index 0000000..13ba8a0
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_unoeuro.sh
@@ -0,0 +1,179 @@
+#!/usr/bin/env sh
+
+#
+#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#UNO_User="UExxxxxx"
+
+Uno_Api="https://api.simply.com/1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_unoeuro_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
+ UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
+ if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
+ UNO_Key=""
+ UNO_User=""
+ _err "You haven't specified a UnoEuro api key and account yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable UNO_Key "$UNO_Key"
+ _saveaccountconf_mutable UNO_User "$UNO_User"
+
+ _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"
+ _uno_rest GET "my/products/$h/dns/records"
+
+ if ! _contains "$response" "\"status\": 200" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ _info "Adding record"
+
+ if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120,\"priority\":0}"; then
+ if _contains "$response" "\"status\": 200" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+}
+
+#fulldomain txtvalue
+dns_unoeuro_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
+ UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
+ if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
+ UNO_Key=""
+ UNO_User=""
+ _err "You haven't specified a UnoEuro api key and account yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ if ! _contains "$UNO_User" "UE"; then
+ _err "It seems that the UNO_User=$UNO_User is not a valid username."
+ _err "Please check and retry."
+ return 1
+ fi
+
+ _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"
+ _uno_rest GET "my/products/$h/dns/records"
+
+ if ! _contains "$response" "\"status\": 200"; then
+ _err "Error"
+ return 1
+ fi
+
+ if ! _contains "$response" "$_sub_domain"; then
+ _info "Don't need to remove."
+ else
+ for record_line_number in $(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1); do
+ record_line_number=$(_math "$record_line_number" - 1)
+ _debug "record_line_number" "$record_line_number"
+ record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}")
+ _debug "record_id" "$record_id"
+
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+
+ if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" "\"status\": 200"
+ done
+ 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=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _uno_rest GET "my/products/$h/dns/records"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"status\": 200"; then
+ _domain_id=$h
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_uno_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")"
+ else
+ response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_variomedia.sh b/acme.sh-master/dnsapi/dns_variomedia.sh
new file mode 100644
index 0000000..a35b8f0
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_variomedia.sh
@@ -0,0 +1,147 @@
+#!/usr/bin/env sh
+
+#
+#VARIOMEDIA_API_TOKEN=000011112222333344445555666677778888
+
+VARIOMEDIA_API="https://api.variomedia.de"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_variomedia_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}"
+ if test -z "$VARIOMEDIA_API_TOKEN"; then
+ VARIOMEDIA_API_TOKEN=""
+ _err 'VARIOMEDIA_API_TOKEN was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ if ! _variomedia_rest POST "dns-records" "{\"data\": {\"type\": \"dns-record\", \"attributes\": {\"record_type\": \"TXT\", \"name\": \"$_sub_domain\", \"domain\": \"$_domain\", \"data\": \"$txtvalue\", \"ttl\":300}}}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#fulldomain txtvalue
+dns_variomedia_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}"
+ if test -z "$VARIOMEDIA_API_TOKEN"; then
+ VARIOMEDIA_API_TOKEN=""
+ _err 'VARIOMEDIA_API_TOKEN was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug 'Getting txt records'
+
+ if ! _variomedia_rest GET "dns-records?filter[domain]=$_domain"; then
+ _err 'Error'
+ return 1
+ fi
+
+ _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
+ _debug _record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _info "Successfully retrieved the record id for ACME challenge."
+ else
+ _info "Empty record id, it seems no such record."
+ return 0
+ fi
+
+ if ! _variomedia_rest DELETE "/dns-records/$_record_id"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=1
+ while true; do
+ h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ if ! _variomedia_rest GET "domains/$h"; then
+ return 1
+ fi
+
+ if _startswith "$response" "\{\"data\":"; then
+ if _contains "$response" "\"id\":\"$h\""; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
+ _domain=$h
+ return 0
+ fi
+ fi
+ i=$(_math "$i" + 1)
+ done
+
+ _debug "root domain not found"
+ return 1
+}
+
+_variomedia_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Authorization: token $VARIOMEDIA_API_TOKEN"
+ export _H2="Content-Type: application/vnd.api+json"
+ export _H3="Accept: application/vnd.variomedia.v1+json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$VARIOMEDIA_API/$ep" "" "$m")"
+ else
+ response="$(_get "$VARIOMEDIA_API/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "Error $ep"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_veesp.sh b/acme.sh-master/dnsapi/dns_veesp.sh
new file mode 100644
index 0000000..b8a41d0
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_veesp.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env sh
+
+# bug reports to stepan@plyask.in
+
+#
+# export VEESP_User="username"
+# export VEESP_Password="password"
+
+VEESP_Api="https://secure.veesp.com/api"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_veesp_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}"
+ VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}"
+ VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64)
+
+ if [ -z "$VEESP_Password" ] || [ -z "$VEESP_User" ]; then
+ VEESP_Password=""
+ VEESP_User=""
+ _err "You don't specify veesp api key and email yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable VEESP_Password "$VEESP_Password"
+ _saveaccountconf_mutable VEESP_User "$VEESP_User"
+
+ _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"
+
+ _info "Adding record"
+ if VEESP_rest POST "service/$_service_id/dns/$_domain_id/records" "{\"name\":\"$fulldomain\",\"ttl\":1,\"priority\":0,\"type\":\"TXT\",\"content\":\"$txtvalue\"}"; then
+ if _contains "$response" "\"success\":true"; then
+ _info "Added"
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+}
+
+# Usage: fulldomain txtvalue
+# Used to remove the txt record after validation
+dns_veesp_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ VEESP_Password="${VEESP_Password:-$(_readaccountconf_mutable VEESP_Password)}"
+ VEESP_User="${VEESP_User:-$(_readaccountconf_mutable VEESP_User)}"
+ VEESP_auth=$(printf "%s" "$VEESP_User:$VEESP_Password" | _base64)
+
+ _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"
+ VEESP_rest GET "service/$_service_id/dns/$_domain_id"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o "{\"id\":[^}]*\"type\":\"TXT\",\"content\":\".\"$txtvalue.\"\"" | cut -d\" -f4)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! VEESP_rest DELETE "service/$_service_id/dns/$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" "\"success\":true"
+ 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=2
+ p=1
+ if ! VEESP_rest GET "dns"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"domain_id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1 | cut -d '"' -f 2)
+ _debug _domain_id "$_domain_id"
+ _service_id=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$h\",\"service_id\":[^}]*" | cut -d : -f 3 | cut -d '"' -f 2)
+ _debug _service_id "$_service_id"
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+VEESP_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Authorization: Basic $VEESP_auth"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ export _H3="Content-Type: application/json"
+ response="$(_post "$data" "$VEESP_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$VEESP_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_vercel.sh b/acme.sh-master/dnsapi/dns_vercel.sh
new file mode 100644
index 0000000..7bf6b0e
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_vercel.sh
@@ -0,0 +1,142 @@
+#!/usr/bin/env sh
+
+# Vercel DNS API
+#
+# This is your API token which can be acquired on the account page.
+# https://vercel.com/account/tokens
+#
+# VERCEL_TOKEN="sdfsdfsdfljlbjkljlkjsdfoiwje"
+
+VERCEL_API="https://api.vercel.com"
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_vercel_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ VERCEL_TOKEN="${VERCEL_TOKEN:-$(_readaccountconf_mutable VERCEL_TOKEN)}"
+
+ if [ -z "$VERCEL_TOKEN" ]; then
+ VERCEL_TOKEN=""
+ _err "You have not set the Vercel API token yet."
+ _err "Please visit https://vercel.com/account/tokens to generate it."
+ return 1
+ fi
+
+ _saveaccountconf_mutable VERCEL_TOKEN "$VERCEL_TOKEN"
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _info "Adding record"
+ if _vercel_rest POST "v2/domains/$_domain/records" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\"}"; then
+ if printf -- "%s" "$response" | grep "\"uid\":\"" >/dev/null; then
+ _info "Added"
+ return 0
+ else
+ _err "Unexpected response while adding text record."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+}
+
+dns_vercel_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _vercel_rest GET "v2/domains/$_domain/records"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
+
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ _record_id=$(printf "%s" "$response" | _egrep_o "\"id\":[^,]*,\"slug\":\"[^,]*\",\"name\":\"$_sub_domain\",[^{]*\"type\":\"TXT\",\"value\":\"$txtvalue\"" | cut -d: -f2 | cut -d, -f1 | tr -d '"')
+
+ if [ "$_record_id" ]; then
+ echo "$_record_id" | while read -r item; do
+ if _vercel_rest DELETE "v2/domains/$_domain/records/$item"; then
+ _info "removed record" "$item"
+ return 0
+ else
+ _err "failed to remove record" "$item"
+ return 1
+ fi
+ done
+ fi
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain="$1"
+ ep="$2"
+ i=1
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _vercel_rest GET "v4/domains/$h"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\"" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_vercel_rest() {
+ m="$1"
+ ep="$2"
+ data="$3"
+
+ path="$VERCEL_API/$ep"
+
+ export _H1="Content-Type: application/json"
+ export _H2="Authorization: Bearer $VERCEL_TOKEN"
+
+ if [ "$m" != "GET" ]; then
+ _secure_debug2 data "$data"
+ response="$(_post "$data" "$path" "" "$m")"
+ else
+ response="$(_get "$path")"
+ fi
+ _ret="$?"
+ _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")"
+ _debug "http response code $_code"
+ _secure_debug2 response "$response"
+ if [ "$_ret" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+
+ response="$(printf "%s" "$response" | _normalizeJson)"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_vscale.sh b/acme.sh-master/dnsapi/dns_vscale.sh
new file mode 100644
index 0000000..d717d6e
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_vscale.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env sh
+
+#This is the vscale.io api wrapper for acme.sh
+#
+#Author: Alex Loban
+#Report Bugs here: https://github.com/LAV45/acme.sh
+
+#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
+VSCALE_API_URL="https://api.vscale.io/v1"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_vscale_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$VSCALE_API_KEY" ]; then
+ VSCALE_API_KEY=""
+ _err "You didn't specify the VSCALE api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY"
+
+ _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"
+
+ _vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}"
+
+ if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then
+ response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2)
+ if [ -z "$response" ]; then
+ _info "txt record updated success."
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+#fulldomain txtvalue
+dns_vscale_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _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"
+ _vscale_rest GET "domains/$_domain_id/records/"
+
+ if [ -n "$response" ]; then
+ record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"")
+ _debug record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then
+ _info "txt record deleted success."
+ return 0
+ fi
+ _debug response "$response"
+ return 1
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _vscale_rest GET "domains/"; then
+ response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | tr "{" "\n" | _egrep_o "\"name\":\s*\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method uri qstr data
+_vscale_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+ export _H3="X-Token: ${VSCALE_API_KEY}"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")"
+ else
+ response="$(_get "$VSCALE_API_URL/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_vultr.sh b/acme.sh-master/dnsapi/dns_vultr.sh
new file mode 100644
index 0000000..54e5b6c
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_vultr.sh
@@ -0,0 +1,161 @@
+#!/usr/bin/env sh
+
+#
+#VULTR_API_KEY=000011112222333344445555666677778888
+
+VULTR_Api="https://api.vultr.com/v2"
+
+######## Public functions #####################
+#
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_vultr_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}"
+ if test -z "$VULTR_API_KEY"; then
+ VULTR_API_KEY=''
+ _err 'VULTR_API_KEY was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug 'Getting txt records'
+ _vultr_rest GET "domains/$_domain/records"
+
+ if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
+ _err 'Error'
+ return 1
+ fi
+
+ if ! _vultr_rest POST "domains/$_domain/records" "{\"name\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"type\":\"TXT\"}"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#fulldomain txtvalue
+dns_vultr_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}"
+ if test -z "$VULTR_API_KEY"; then
+ VULTR_API_KEY=""
+ _err 'VULTR_API_KEY was not exported'
+ return 1
+ fi
+
+ _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY"
+
+ _debug 'First detect the root zone'
+ if ! _get_root "$fulldomain"; then
+ return 1
+ fi
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug 'Getting txt records'
+ _vultr_rest GET "domains/$_domain/records"
+
+ if printf "%s\n" "$response" | grep -- "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then
+ _err 'Error'
+ return 1
+ fi
+
+ _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep -- "$txtvalue" | tr ',' '\n' | grep -i 'id' | cut -d : -f 2 | tr -d '"')"
+ _debug _record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _info "Successfully retrieved the record id for ACME challenge."
+ else
+ _info "Empty record id, it seems no such record."
+ return 0
+ fi
+
+ if ! _vultr_rest DELETE "domains/$_domain/records/$_record_id"; then
+ _err "$response"
+ return 1
+ fi
+
+ _debug2 _response "$response"
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=1
+ while true; do
+ _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$_domain"
+ if [ -z "$_domain" ]; then
+ return 1
+ fi
+
+ if ! _vultr_rest GET "domains"; then
+ return 1
+ fi
+
+ if printf "%s\n" "$response" | grep -E '^\{.*\}' >/dev/null; then
+ if _contains "$response" "\"domain\":\"$_domain\""; then
+ _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+ return 0
+ else
+ _debug "Go to next level of $_domain"
+ fi
+ else
+ _err "$response"
+ return 1
+ fi
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_vultr_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ api_key_trimmed=$(echo "$VULTR_API_KEY" | tr -d '"')
+
+ export _H1="Authorization: Bearer $api_key_trimmed"
+ export _H2='Content-Type: application/json'
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$VULTR_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$VULTR_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "Error $ep"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/acme.sh-master/dnsapi/dns_websupport.sh b/acme.sh-master/dnsapi/dns_websupport.sh
new file mode 100644
index 0000000..e824c9c
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_websupport.sh
@@ -0,0 +1,207 @@
+#!/usr/bin/env sh
+
+# Acme.sh DNS API wrapper for websupport.sk
+#
+# Original author: trgo.sk (https://github.com/trgosk)
+# Tweaks by: akulumbeg (https://github.com/akulumbeg)
+# Report Bugs here: https://github.com/akulumbeg/acme.sh
+
+# Requirements: API Key and Secret from https://admin.websupport.sk/en/auth/apiKey
+#
+# WS_ApiKey="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+# (called "Identifier" in the WS Admin)
+#
+# WS_ApiSecret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+# (called "Secret key" in the WS Admin)
+
+WS_Api="https://rest.websupport.sk"
+
+######## Public functions #####################
+
+dns_websupport_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ WS_ApiKey="${WS_ApiKey:-$(_readaccountconf_mutable WS_ApiKey)}"
+ WS_ApiSecret="${WS_ApiSecret:-$(_readaccountconf_mutable WS_ApiSecret)}"
+
+ if [ "$WS_ApiKey" ] && [ "$WS_ApiSecret" ]; then
+ _saveaccountconf_mutable WS_ApiKey "$WS_ApiKey"
+ _saveaccountconf_mutable WS_ApiSecret "$WS_ApiSecret"
+ else
+ WS_ApiKey=""
+ WS_ApiSecret=""
+ _err "You did not specify the API Key and/or API Secret"
+ _err "You can get the API login credentials from https://admin.websupport.sk/en/auth/apiKey"
+ 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"
+
+ # 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 _ws_rest POST "/v1/user/self/zone/$_domain/record" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+ if _contains "$response" "$txtvalue"; then
+ _info "Added, OK"
+ return 0
+ elif _contains "$response" "The record already exists"; then
+ _info "Already exists, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+
+}
+
+dns_websupport_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _debug2 fulldomain "$fulldomain"
+ _debug2 txtvalue "$txtvalue"
+
+ _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"
+ _ws_rest GET "/v1/user/self/zone/$_domain/record"
+
+ if [ "$(printf "%s" "$response" | tr -d " " | grep -c \"items\")" -lt "1" ]; then
+ _err "Error: $response"
+ return 1
+ fi
+
+ record_line="$(_get_from_array "$response" "$txtvalue")"
+ _debug record_line "$record_line"
+ if [ -z "$record_line" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(echo "$record_line" | _egrep_o "\"id\": *[^,]*" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _ws_rest DELETE "/v1/user/self/zone/$_domain/record/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ if [ "$(printf "%s" "$response" | tr -d " " | grep -c \"success\")" -lt "1" ]; then
+ return 1
+ else
+ return 0
+ fi
+ fi
+
+}
+
+#################### Private Functions ##################################
+
+_get_root() {
+ domain=$1
+ i=1
+ p=1
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _ws_rest GET "/v1/user/self/zone"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(echo "$response" | _egrep_o "\[.\"id\": *[^,]*" | _head_n 1 | cut -d : -f 2 | tr -d \" | tr -d " ")
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_ws_rest() {
+ me=$1
+ pa="$2"
+ da="$3"
+
+ _debug2 api_key "$WS_ApiKey"
+ _debug2 api_secret "$WS_ApiSecret"
+
+ timestamp=$(_time)
+ datez="$(_utc_date | sed "s/ /T/" | sed "s/$/+0000/")"
+ canonical_request="${me} ${pa} ${timestamp}"
+ signature_hash=$(printf "%s" "$canonical_request" | _hmac sha1 "$(printf "%s" "$WS_ApiSecret" | _hex_dump | tr -d " ")" hex)
+ basicauth="$(printf "%s:%s" "$WS_ApiKey" "$signature_hash" | _base64)"
+
+ _debug2 method "$me"
+ _debug2 path "$pa"
+ _debug2 data "$da"
+ _debug2 timestamp "$timestamp"
+ _debug2 datez "$datez"
+ _debug2 canonical_request "$canonical_request"
+ _debug2 signature_hash "$signature_hash"
+ _debug2 basicauth "$basicauth"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+ export _H3="Authorization: Basic ${basicauth}"
+ export _H4="Date: ${datez}"
+
+ _debug2 H1 "$_H1"
+ _debug2 H2 "$_H2"
+ _debug2 H3 "$_H3"
+ _debug2 H4 "$_H4"
+
+ if [ "$me" != "GET" ]; then
+ _debug2 "${me} $WS_Api${pa}"
+ _debug data "$da"
+ response="$(_post "$da" "${WS_Api}${pa}" "" "$me")"
+ else
+ _debug2 "GET $WS_Api${pa}"
+ response="$(_get "$WS_Api${pa}")"
+ fi
+
+ _debug2 response "$response"
+ return "$?"
+}
+
+_get_from_array() {
+ va="$1"
+ fi="$2"
+ for i in $(echo "$va" | sed "s/{/ /g"); do
+ if _contains "$i" "$fi"; then
+ echo "$i"
+ break
+ fi
+ done
+}
diff --git a/acme.sh-master/dnsapi/dns_world4you.sh b/acme.sh-master/dnsapi/dns_world4you.sh
new file mode 100644
index 0000000..dfda4ef
--- /dev/null
+++ b/acme.sh-master/dnsapi/dns_world4you.sh
@@ -0,0 +1,220 @@
+#!/usr/bin/env sh
+
+# World4You - www.world4you.com
+# Lorenz Stechauner, 2020 - https://www.github.com/NerLOR
+
+WORLD4YOU_API="https://my.world4you.com/en"
+PAKETNR=''
+TLD=''
+RECORD=''
+
+################ Public functions ################
+
+# Usage: dns_world4you_add
+dns_world4you_add() {
+ fqdn=$(echo "$1" | _lower_case)
+ value="$2"
+ _info "Using world4you to add record"
+ _debug fulldomain "$fqdn"
+ _debug txtvalue "$value"
+
+ _login
+ if [ "$?" != 0 ]; then
+ return 1
+ fi
+
+ export _H1="Cookie: W4YSESSID=$sessid"
+ form=$(_get "$WORLD4YOU_API/")
+ _get_paketnr "$fqdn" "$form"
+ paketnr="$PAKETNR"
+ if [ -z "$paketnr" ]; then
+ _err "Unable to parse paketnr"
+ return 3
+ fi
+ _debug paketnr "$paketnr"
+
+ export _H1="Cookie: W4YSESSID=$sessid"
+ form=$(_get "$WORLD4YOU_API/$paketnr/dns")
+ formiddp=$(echo "$form" | grep 'AddDnsRecordForm\[uniqueFormIdDP\]' | sed 's/^.*name="AddDnsRecordForm\[uniqueFormIdDP\]" value="\([^"]*\)".*$/\1/')
+ form_token=$(echo "$form" | grep 'AddDnsRecordForm\[_token\]' | sed 's/^.*name="AddDnsRecordForm\[_token\]" value="\([^"]*\)".*$/\1/')
+ if [ -z "$formiddp" ]; then
+ _err "Unable to parse form"
+ return 3
+ fi
+
+ _resethttp
+ export ACME_HTTP_NO_REDIRECTS=1
+ body="AddDnsRecordForm[name]=$RECORD&AddDnsRecordForm[dnsType][type]=TXT&AddDnsRecordForm[value]=$value&AddDnsRecordForm[uniqueFormIdDP]=$formiddp&AddDnsRecordForm[_token]=$form_token"
+ _info "Adding record..."
+ ret=$(_post "$body" "$WORLD4YOU_API/$paketnr/dns" '' POST 'application/x-www-form-urlencoded')
+ _resethttp
+
+ if _contains "$(_head_n 1 <"$HTTP_HEADER")" '302'; then
+ res=$(_get "$WORLD4YOU_API/$paketnr/dns")
+ 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')
+ if [ "$msg" = '' ]; then
+ _err "Unable to add record: Unknown error"
+ echo "$ret" >'error-01.html'
+ echo "$res" >'error-02.html'
+ _err "View error-01.html and error-02.html for debugging"
+ else
+ _err "Unable to add record: my.world4you.com: $msg"
+ fi
+ return 1
+ fi
+ else
+ msg=$(echo "$ret" | grep '"form-error-message"' | sed 's/^.*