diff --git a/Dockerfile b/Dockerfile
index 8d0f618..feb89b0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -48,5 +48,12 @@ RUN for verb in help \
printf -- "%b" "#!/usr/bin/env sh\n/root/.acme.sh/acme.sh --${verb} --config-home /acme.sh \"\$@\"" >/usr/local/bin/--${verb} && chmod +x /usr/local/bin/--${verb} \
; done
-ENTRYPOINT ["/root/.acme.sh/acme.sh", "--config-home", "/acme.sh"]
+RUN printf "%b" '#!'"/usr/bin/env sh\n \
+if [ \"\$1\" = \"daemon\" ]; then \n \
+ crond; tail -f /dev/null;\n \
+else \n \
+ /root/.acme.sh/acme.sh --config-home /acme.sh \"\$@\"\n \
+fi" >/entry.sh && chmod +x /entry.sh
+
+ENTRYPOINT ["/entry.sh"]
CMD ["--help"]
diff --git a/README.md b/README.md
index e0c1e7d..65f2d0d 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
- DOES NOT require `root/sudoer` access.
- Docker friendly
-It's probably the `easiest&smallest&smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
+It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt.
Wiki: https://github.com/Neilpang/acme.sh/wiki
@@ -31,6 +31,7 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
- [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html)
- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/)
+- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient)
- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
# Tested OS
@@ -313,6 +314,7 @@ You don't have to do anything manually!
1. DigitalOcean API (native)
1. ClouDNS.net API
1. Infoblox NIOS API (https://www.infoblox.com/)
+1. VSCALE (https://vscale.io/)
**More APIs coming soon...**
diff --git a/acme.sh b/acme.sh
index e2bc928..27bc4fb 100755
--- a/acme.sh
+++ b/acme.sh
@@ -1,6 +1,6 @@
#!/usr/bin/env sh
-VER=2.6.8
+VER=2.6.9
PROJECT_NAME="acme.sh"
@@ -107,7 +107,7 @@ __green() {
if [ "$__INTERACTIVE" ]; then
printf '\033[1;31;32m'
fi
- printf -- "$1"
+ printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then
printf '\033[0m'
fi
@@ -117,7 +117,7 @@ __red() {
if [ "$__INTERACTIVE" ]; then
printf '\033[1;31;40m'
fi
- printf -- "$1"
+ printf -- "%b" "$1"
if [ "$__INTERACTIVE" ]; then
printf '\033[0m'
fi
@@ -347,7 +347,7 @@ _hasfield() {
fi
done
_debug2 "'$_str' does not contain '$_field'"
- return 1 #not contains
+ return 1 #not contains
}
_getfield() {
@@ -722,7 +722,7 @@ _url_encode() {
"7e")
printf "%s" "~"
;;
- #other hex
+ #other hex
*)
printf '%%%s' "$_hex_code"
;;
@@ -1025,7 +1025,7 @@ _createcsr() {
else
alt="DNS:$domainlist"
fi
- #multi
+ #multi
_info "Multi domain" "$alt"
printf -- "\nsubjectAltName=$alt" >>"$csrconf"
fi
@@ -1093,7 +1093,7 @@ _readSubjectAltNamesFromCSR() {
printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
}
-#_csrfile
+#_csrfile
_readKeyLengthFromCSR() {
_csrfile="$1"
if [ -z "$_csrfile" ]; then
@@ -1102,12 +1102,13 @@ _readKeyLengthFromCSR() {
fi
_outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
+ _debug2 _outcsr "$_outcsr"
if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
_debug "ECC CSR"
- echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
+ echo "$_outcsr" | tr "\t" " " | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
else
_debug "RSA CSR"
- echo "$_outcsr" | _egrep_o "(^ *|^RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
+ echo "$_outcsr" | tr "\t" " " | _egrep_o "(^ *|RSA )Public.Key:.*" | cut -d '(' -f 2 | cut -d ' ' -f 1
fi
}
@@ -1191,7 +1192,7 @@ toPkcs8() {
}
-#[2048]
+#[2048]
createAccountKey() {
_info "Creating account key"
if [ -z "$1" ]; then
@@ -1846,6 +1847,24 @@ _saveaccountconf() {
_save_conf "$ACCOUNT_CONF_PATH" "$1" "$2"
}
+#key value
+_saveaccountconf_mutable() {
+ _save_conf "$ACCOUNT_CONF_PATH" "SAVED_$1" "$2"
+ #remove later
+ _clearaccountconf "$1"
+}
+
+#key
+_readaccountconf() {
+ _read_conf "$ACCOUNT_CONF_PATH" "$1"
+}
+
+#key
+_readaccountconf_mutable() {
+ _rac_key="$1"
+ _readaccountconf "SAVED_$_rac_key"
+}
+
#_clearaccountconf key
_clearaccountconf() {
_clear_conf "$ACCOUNT_CONF_PATH" "$1"
@@ -2527,7 +2546,7 @@ _setNginx() {
location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
default_type text/plain;
return 200 \"\$1.$_thumbpt\";
-}
+}
#NGINX_START
" >>"$FOUND_REAL_NGINX_CONF"
@@ -2564,7 +2583,7 @@ _checkConf() {
if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
_debug "wildcard"
for _w_f in $2; do
- if [ -f "$_w_f"] && _checkConf "$1" "$_w_f"; then
+ if [ -f "$_w_f" ] && _checkConf "$1" "$_w_f"; then
return 0
fi
done
@@ -3114,12 +3133,16 @@ __trigger_validation() {
_send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
}
-#webroot, domain domainlist keylength
+#webroot, domain domainlist keylength
issue() {
if [ -z "$2" ]; then
_usage "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
return 1
fi
+ if [ -z "$1" ]; then
+ _usage "Please specify at least one validation method: '--webroot', '--standalone', '--apache', '--nginx' or '--dns' etc."
+ return 1
+ fi
_web_roots="$1"
_main_domain="$2"
_alt_domains="$3"
@@ -3643,7 +3666,7 @@ issue() {
#if ! _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH" ; then
# _debug "Get cert failed. Let's try last response."
- # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
+ # printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >> "$CERT_PATH"
#fi
if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
@@ -3860,7 +3883,7 @@ renewAll() {
return "$rc"
else
_ret="$rc"
- _err "Error renew $d, Go ahead to next one."
+ _err "Error renew $d."
fi
fi
done
@@ -4784,7 +4807,7 @@ Commands:
--create-domain-key Create an domain private key, professional use.
--createCSR, -ccsr Create CSR , professional use.
--deactivate Deactivate the domain authz, professional use.
-
+
Parameters:
--domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
--force, -f Used to force to install or force to renew a cert immediately.
@@ -4798,20 +4821,20 @@ Parameters:
--apache Use apache mode.
--dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
--dnssleep [$DEFAULT_DNS_SLEEP] The time in seconds to wait for all the txt records to take effect in dns api mode. Default $DEFAULT_DNS_SLEEP seconds.
-
+
--keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
--accountkeylength, -ak [2048] Specifies the account key length.
--log [/path/to/logfile] Specifies the log file. The default is: \"$DEFAULT_LOG_FILE\" if you don't give a file path here.
--log-level 1|2 Specifies the log level, default is 1.
--syslog [0|3|6|7] Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
-
+
These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
-
+
--cert-file After issue/renew, the cert will be copied to this path.
--key-file After issue/renew, the key will be copied to this path.
--ca-file After issue/renew, the intermediate cert will be copied to this path.
--fullchain-file After issue/renew, the fullchain cert will be copied to this path.
-
+
--reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
--accountconf Specifies a customized account config file.
diff --git a/dnsapi/README.md b/dnsapi/README.md
index 41cc80a..12d76be 100644
--- a/dnsapi/README.md
+++ b/dnsapi/README.md
@@ -468,6 +468,7 @@ acme.sh --issue --dns dns_dynu -d example.com -d www.example.com
The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
+
# Use custom API
If your API is not supported yet, you can write your own DNS API.
diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh
index d5d52de..21e8668 100755
--- a/dnsapi/dns_aws.sh
+++ b/dnsapi/dns_aws.sh
@@ -88,6 +88,19 @@ _get_root() {
while true; do
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
if [ -z "$h" ]; then
+ if _contains "$response" "true" && _contains "$response" ""; then
+ _debug "IsTruncated"
+ _nextMarker="$(echo "$response" | _egrep_o ".*" | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _debug "NextMarker" "$_nextMarker"
+ if aws_rest GET "2013-04-01/hostedzone" "marker=$_nextMarker"; then
+ _debug "Truncated request OK"
+ i=2
+ p=1
+ continue
+ else
+ _err "Truncated request error."
+ fi
+ fi
#not valid
return 1
fi
@@ -208,6 +221,9 @@ aws_rest() {
_debug _H2 "$_H2"
url="$AWS_URL/$ep"
+ if [ "$qsr" ]; then
+ url="$AWS_URL/$ep?$qsr"
+ fi
if [ "$mtd" = "GET" ]; then
response="$(_get "$url")"
diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh
index 3718f9d..57a2e88 100755
--- a/dnsapi/dns_cf.sh
+++ b/dnsapi/dns_cf.sh
@@ -14,6 +14,8 @@ dns_cf_add() {
fulldomain=$1
txtvalue=$2
+ CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
+ CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
CF_Key=""
CF_Email=""
@@ -29,8 +31,8 @@ dns_cf_add() {
fi
#save the api key and email to the account conf file.
- _saveaccountconf CF_Key "$CF_Key"
- _saveaccountconf CF_Email "$CF_Email"
+ _saveaccountconf_mutable CF_Key "$CF_Key"
+ _saveaccountconf_mutable CF_Email "$CF_Email"
_debug "First detect the root zone"
if ! _get_root "$fulldomain"; then
@@ -83,6 +85,17 @@ dns_cf_add() {
dns_cf_rm() {
fulldomain=$1
txtvalue=$2
+
+ CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}"
+ CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}"
+ if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
+ CF_Key=""
+ CF_Email=""
+ _err "You don't specify cloudflare api key and email 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"
diff --git a/dnsapi/dns_dynu.sh b/dnsapi/dns_dynu.sh
new file mode 100644
index 0000000..19e7fc7
--- /dev/null
+++ b/dnsapi/dns_dynu.sh
@@ -0,0 +1,216 @@
+#!/usr/bin/env sh
+
+#Client ID
+#Dynu_ClientId="0b71cae7-a099-4f6b-8ddf-94571cdb760d"
+#
+#Secret
+#Dynu_Secret="aCUEY4BDCV45KI8CSIC3sp2LKQ9"
+#
+#Token
+Dynu_Token=""
+#
+#Endpoint
+Dynu_EndPoint="https://api.dynu.com/v1"
+#
+#Author: Dynu Systems, Inc.
+#Report Bugs here: https://github.com/shar0119/acme.sh
+#
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dynu_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
+ Dynu_ClientId=""
+ Dynu_Secret=""
+ _err "Dynu client id and secret is not specified."
+ _err "Please create you API client id and secret and try again."
+ return 1
+ fi
+
+ #save the client id and secret to the account conf file.
+ _saveaccountconf Dynu_ClientId "$Dynu_ClientId"
+ _saveaccountconf Dynu_Secret "$Dynu_Secret"
+
+ if [ -z "$Dynu_Token" ]; then
+ _info "Getting Dynu token."
+ if ! _dynu_authentication; then
+ _err "Can not get token."
+ fi
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain."
+ return 1
+ fi
+
+ _debug _node "$_node"
+ _debug _domain_name "$_domain_name"
+
+ _info "Creating TXT record."
+ if ! _dynu_rest POST "dns/record/add" "{\"domain_name\":\"$_domain_name\",\"node_name\":\"$_node\",\"record_type\":\"TXT\",\"text_data\":\"$txtvalue\",\"state\":true,\"ttl\":90}"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "text_data"; then
+ _err "Could not add TXT record."
+ return 1
+ fi
+
+ return 0
+}
+
+#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dynu_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$Dynu_ClientId" ] || [ -z "$Dynu_Secret" ]; then
+ Dynu_ClientId=""
+ Dynu_Secret=""
+ _err "Dynu client id and secret is not specified."
+ _err "Please create you API client id and secret and try again."
+ return 1
+ fi
+
+ #save the client id and secret to the account conf file.
+ _saveaccountconf Dynu_ClientId "$Dynu_ClientId"
+ _saveaccountconf Dynu_Secret "$Dynu_Secret"
+
+ if [ -z "$Dynu_Token" ]; then
+ _info "Getting Dynu token."
+ if ! _dynu_authentication; then
+ _err "Can not get token."
+ fi
+ fi
+
+ _debug "Detect root zone."
+ if ! _get_root "$fulldomain"; then
+ _err "Invalid domain."
+ return 1
+ fi
+
+ _debug _node "$_node"
+ _debug _domain_name "$_domain_name"
+
+ _info "Checking for TXT record."
+ if ! _get_recordid "$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 "$_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
+# _node=_acme-challenge.www
+# _domain_name=domain.com
+_get_root() {
+ domain=$1
+ if ! _dynu_rest GET "dns/getroot/$domain"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "domain_name"; then
+ _debug "Domain name not found."
+ return 1
+ fi
+
+ _domain_name=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 1 | cut -d : -f 2 | cut -d '"' -f 2)
+ _node=$(printf "%s" "$response" | tr -d "{}" | cut -d , -f 3 | cut -d : -f 2 | cut -d '"' -f 2)
+ return 0
+}
+
+_get_recordid() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _dynu_rest GET "dns/record/get?hostname=$fulldomain&rrtype=TXT"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "$txtvalue"; then
+ _dns_record_id=0
+ return 0
+ fi
+
+ _dns_record_id=$(printf "%s" "$response" | _egrep_o "{[^}]*}" | grep "\"text_data\":\"$txtvalue\"" | grep -Po '"id":\K[0-9]+')
+
+ return 0
+}
+
+_delete_txt_record() {
+ _dns_record_id=$1
+
+ if ! _dynu_rest GET "dns/record/delete/$_dns_record_id"; then
+ return 1
+ fi
+
+ if ! _contains "$response" "true"; then
+ return 1
+ fi
+
+ return 0
+}
+
+_dynu_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Authorization: Bearer $Dynu_Token"
+ export _H2="Content-Type: application/json"
+
+ if [ "$data" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$Dynu_EndPoint/$ep" "" "$m")"
+ else
+ _info "Getting $Dynu_EndPoint/$ep"
+ response="$(_get "$Dynu_EndPoint/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
+
+_dynu_authentication() {
+ realm="$(printf "%s" "$Dynu_ClientId:$Dynu_Secret" | _base64)"
+
+ export _H1="Authorization: Basic $realm"
+ export _H2="Content-Type: application/json"
+
+ response="$(_get "$Dynu_EndPoint/oauth2/token")"
+ if [ "$?" != "0" ]; then
+ _err "Authentication failed."
+ return 1
+ fi
+ if _contains "$response" "accessToken"; then
+ Dynu_Token=$(printf "%s" "$response" | tr -d "[]" | cut -d , -f 2 | cut -d : -f 2 | cut -d '"' -f 2)
+ fi
+ if _contains "$Dynu_Token" "null"; then
+ Dynu_Token=""
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh
index 272a124..53da411 100755
--- a/dnsapi/dns_freedns.sh
+++ b/dnsapi/dns_freedns.sh
@@ -53,7 +53,7 @@ dns_freedns_add() {
i="$(_math "$i" - 1)"
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
- # Sometimes FreeDNS does not return the subdomain page but rather
+ # Sometimes FreeDNS does not return the subdomain page but rather
# returns a page regarding becoming a premium member. This usually
# happens after a period of inactivity. Immediately trying again
# returns the correct subdomain page. So, we will try twice to
@@ -72,7 +72,7 @@ dns_freedns_add() {
fi
# Now convert the tables in the HTML to CSV. This litte gem from
- # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
+ # http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
subdomain_csv="$(echo "$htmlpage" \
| grep -i -e '\?TABLE\|\?TD\|\?TR\|\?TH' \
| sed 's/^[\ \t]*//g' \
@@ -196,7 +196,7 @@ dns_freedns_rm() {
FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")"
_debug "FreeDNS login cookies: $FREEDNS_COOKIE"
- # Sometimes FreeDNS does not return the subdomain page but rather
+ # Sometimes FreeDNS does not return the subdomain page but rather
# returns a page regarding becoming a premium member. This usually
# happens after a period of inactivity. Immediately trying again
# returns the correct subdomain page. So, we will try twice to
diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh
index 71642bd..18f9c7d 100755
--- a/dnsapi/dns_ovh.sh
+++ b/dnsapi/dns_ovh.sh
@@ -14,7 +14,7 @@
#'ovh-eu'
OVH_EU='https://eu.api.ovh.com/1.0'
-#'ovh-ca':
+#'ovh-ca':
OVH_CA='https://ca.api.ovh.com/1.0'
#'kimsufi-eu'
diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh
new file mode 100755
index 0000000..e50b7d8
--- /dev/null
+++ b/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" | _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
+}