Browse Source

Merge branch 'dev' into patch-3

shar0119 8 years ago
parent
commit
90c70fa5bf
10 changed files with 455 additions and 28 deletions
  1. 8 1
      Dockerfile
  2. 3 1
      README.md
  3. 43 20
      acme.sh
  4. 1 0
      dnsapi/README.md
  5. 16 0
      dnsapi/dns_aws.sh
  6. 15 2
      dnsapi/dns_cf.sh
  7. 216 0
      dnsapi/dns_dynu.sh
  8. 3 3
      dnsapi/dns_freedns.sh
  9. 1 1
      dnsapi/dns_ovh.sh
  10. 149 0
      dnsapi/dns_vscale.sh

+ 8 - 1
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} \
     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
   ; 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"]
 CMD ["--help"]

+ 3 - 1
README.md

@@ -9,7 +9,7 @@
 - DOES NOT require `root/sudoer` access.
 - DOES NOT require `root/sudoer` access.
 - Docker friendly
 - 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
 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)
 - [Centminmod](http://centminmod.com/letsencrypt-acmetool-https.html)
 - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
 - [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297)
 - [archlinux](https://aur.archlinux.org/packages/acme.sh-git/)
 - [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)
 - [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials)
 
 
 # Tested OS
 # Tested OS
@@ -313,6 +314,7 @@ You don't have to do anything manually!
 1. DigitalOcean API (native)
 1. DigitalOcean API (native)
 1. ClouDNS.net API
 1. ClouDNS.net API
 1. Infoblox NIOS API (https://www.infoblox.com/)
 1. Infoblox NIOS API (https://www.infoblox.com/)
+1. VSCALE (https://vscale.io/)
 
 
 **More APIs coming soon...**
 **More APIs coming soon...**
 
 

+ 43 - 20
acme.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env sh
 #!/usr/bin/env sh
 
 
-VER=2.6.8
+VER=2.6.9
 
 
 PROJECT_NAME="acme.sh"
 PROJECT_NAME="acme.sh"
 
 
@@ -107,7 +107,7 @@ __green() {
   if [ "$__INTERACTIVE" ]; then
   if [ "$__INTERACTIVE" ]; then
     printf '\033[1;31;32m'
     printf '\033[1;31;32m'
   fi
   fi
-  printf -- "$1"
+  printf -- "%b" "$1"
   if [ "$__INTERACTIVE" ]; then
   if [ "$__INTERACTIVE" ]; then
     printf '\033[0m'
     printf '\033[0m'
   fi
   fi
@@ -117,7 +117,7 @@ __red() {
   if [ "$__INTERACTIVE" ]; then
   if [ "$__INTERACTIVE" ]; then
     printf '\033[1;31;40m'
     printf '\033[1;31;40m'
   fi
   fi
-  printf -- "$1"
+  printf -- "%b" "$1"
   if [ "$__INTERACTIVE" ]; then
   if [ "$__INTERACTIVE" ]; then
     printf '\033[0m'
     printf '\033[0m'
   fi
   fi
@@ -347,7 +347,7 @@ _hasfield() {
     fi
     fi
   done
   done
   _debug2 "'$_str' does not contain '$_field'"
   _debug2 "'$_str' does not contain '$_field'"
-  return 1 #not contains 
+  return 1 #not contains
 }
 }
 
 
 _getfield() {
 _getfield() {
@@ -722,7 +722,7 @@ _url_encode() {
       "7e")
       "7e")
         printf "%s" "~"
         printf "%s" "~"
         ;;
         ;;
-      #other hex  
+      #other hex
       *)
       *)
         printf '%%%s' "$_hex_code"
         printf '%%%s' "$_hex_code"
         ;;
         ;;
@@ -1025,7 +1025,7 @@ _createcsr() {
     else
     else
       alt="DNS:$domainlist"
       alt="DNS:$domainlist"
     fi
     fi
-    #multi 
+    #multi
     _info "Multi domain" "$alt"
     _info "Multi domain" "$alt"
     printf -- "\nsubjectAltName=$alt" >>"$csrconf"
     printf -- "\nsubjectAltName=$alt" >>"$csrconf"
   fi
   fi
@@ -1093,7 +1093,7 @@ _readSubjectAltNamesFromCSR() {
   printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
   printf "%s" "$_dnsAltnames" | sed "s/DNS://g"
 }
 }
 
 
-#_csrfile 
+#_csrfile
 _readKeyLengthFromCSR() {
 _readKeyLengthFromCSR() {
   _csrfile="$1"
   _csrfile="$1"
   if [ -z "$_csrfile" ]; then
   if [ -z "$_csrfile" ]; then
@@ -1102,12 +1102,13 @@ _readKeyLengthFromCSR() {
   fi
   fi
 
 
   _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
   _outcsr="$(${ACME_OPENSSL_BIN:-openssl} req -noout -text -in "$_csrfile")"
+  _debug2 _outcsr "$_outcsr"
   if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
   if _contains "$_outcsr" "Public Key Algorithm: id-ecPublicKey"; then
     _debug "ECC CSR"
     _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
   else
     _debug "RSA CSR"
     _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
   fi
 }
 }
 
 
@@ -1191,7 +1192,7 @@ toPkcs8() {
 
 
 }
 }
 
 
-#[2048]  
+#[2048]
 createAccountKey() {
 createAccountKey() {
   _info "Creating account key"
   _info "Creating account key"
   if [ -z "$1" ]; then
   if [ -z "$1" ]; then
@@ -1846,6 +1847,24 @@ _saveaccountconf() {
   _save_conf "$ACCOUNT_CONF_PATH" "$1" "$2"
   _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   key
 _clearaccountconf() {
 _clearaccountconf() {
   _clear_conf "$ACCOUNT_CONF_PATH" "$1"
   _clear_conf "$ACCOUNT_CONF_PATH" "$1"
@@ -2527,7 +2546,7 @@ _setNginx() {
 location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
 location ~ \"^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)\$\" {
   default_type text/plain;
   default_type text/plain;
   return 200 \"\$1.$_thumbpt\";
   return 200 \"\$1.$_thumbpt\";
-}  
+}
 #NGINX_START
 #NGINX_START
 " >>"$FOUND_REAL_NGINX_CONF"
 " >>"$FOUND_REAL_NGINX_CONF"
 
 
@@ -2564,7 +2583,7 @@ _checkConf() {
   if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
   if [ ! -f "$2" ] && ! echo "$2" | grep '*$' >/dev/null && echo "$2" | grep '*' >/dev/null; then
     _debug "wildcard"
     _debug "wildcard"
     for _w_f in $2; do
     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
         return 0
       fi
       fi
     done
     done
@@ -3114,12 +3133,16 @@ __trigger_validation() {
   _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
   _send_signed_request "$_t_url" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$_t_key_authz\"}"
 }
 }
 
 
-#webroot, domain domainlist  keylength 
+#webroot, domain domainlist  keylength
 issue() {
 issue() {
   if [ -z "$2" ]; then
   if [ -z "$2" ]; then
     _usage "Usage: $PROJECT_ENTRY --issue  -d  a.com  -w /path/to/webroot/a.com/ "
     _usage "Usage: $PROJECT_ENTRY --issue  -d  a.com  -w /path/to/webroot/a.com/ "
     return 1
     return 1
   fi
   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"
   _web_roots="$1"
   _main_domain="$2"
   _main_domain="$2"
   _alt_domains="$3"
   _alt_domains="$3"
@@ -3643,7 +3666,7 @@ issue() {
 
 
     #if ! _get "$Le_LinkCert" | _base64 "multiline"  >> "$CERT_PATH" ; then
     #if ! _get "$Le_LinkCert" | _base64 "multiline"  >> "$CERT_PATH" ; then
     #  _debug "Get cert failed. Let's try last response."
     #  _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
     #fi
 
 
     if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
     if ! printf -- "%s" "$_rcert" | _dbase64 "multiline" | _base64 "multiline" >>"$CERT_PATH"; then
@@ -3860,7 +3883,7 @@ renewAll() {
         return "$rc"
         return "$rc"
       else
       else
         _ret="$rc"
         _ret="$rc"
-        _err "Error renew $d, Go ahead to next one."
+        _err "Error renew $d."
       fi
       fi
     fi
     fi
   done
   done
@@ -4784,7 +4807,7 @@ Commands:
   --create-domain-key      Create an domain private key, professional use.
   --create-domain-key      Create an domain private key, professional use.
   --createCSR, -ccsr       Create CSR , professional use.
   --createCSR, -ccsr       Create CSR , professional use.
   --deactivate             Deactivate the domain authz, professional use.
   --deactivate             Deactivate the domain authz, professional use.
-  
+
 Parameters:
 Parameters:
   --domain, -d   domain.tld         Specifies a domain, used to issue, renew or revoke etc.
   --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.
   --force, -f                       Used to force to install or force to renew a cert immediately.
@@ -4798,20 +4821,20 @@ Parameters:
   --apache                          Use apache mode.
   --apache                          Use apache mode.
   --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file]   Use dns mode or dns api.
   --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.
   --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.
   --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.
   --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    [/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.
   --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.
   --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:
   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.
   --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.
   --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.
   --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.
   --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.
   --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
 
 
   --accountconf                     Specifies a customized account config file.
   --accountconf                     Specifies a customized account config file.

+ 1 - 0
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.
 The `Dynu_ClientId` and `Dynu_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 
 
+
 # Use custom API
 # Use custom API
 
 
 If your API is not supported yet, you can write your own DNS API.
 If your API is not supported yet, you can write your own DNS API.

+ 16 - 0
dnsapi/dns_aws.sh

@@ -88,6 +88,19 @@ _get_root() {
     while true; do
     while true; do
       h=$(printf "%s" "$domain" | cut -d . -f $i-100)
       h=$(printf "%s" "$domain" | cut -d . -f $i-100)
       if [ -z "$h" ]; then
       if [ -z "$h" ]; then
+        if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
+          _debug "IsTruncated"
+          _nextMarker="$(echo "$response" | _egrep_o "<NextMarker>.*</NextMarker>" | 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
         #not valid
         return 1
         return 1
       fi
       fi
@@ -208,6 +221,9 @@ aws_rest() {
   _debug _H2 "$_H2"
   _debug _H2 "$_H2"
 
 
   url="$AWS_URL/$ep"
   url="$AWS_URL/$ep"
+  if [ "$qsr" ]; then
+    url="$AWS_URL/$ep?$qsr"
+  fi
 
 
   if [ "$mtd" = "GET" ]; then
   if [ "$mtd" = "GET" ]; then
     response="$(_get "$url")"
     response="$(_get "$url")"

+ 15 - 2
dnsapi/dns_cf.sh

@@ -14,6 +14,8 @@ dns_cf_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   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
   if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then
     CF_Key=""
     CF_Key=""
     CF_Email=""
     CF_Email=""
@@ -29,8 +31,8 @@ dns_cf_add() {
   fi
   fi
 
 
   #save the api key and email to the account conf file.
   #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"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -83,6 +85,17 @@ dns_cf_add() {
 dns_cf_rm() {
 dns_cf_rm() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   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"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
     _err "invalid domain"
     _err "invalid domain"

+ 216 - 0
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
+}

+ 3 - 3
dnsapi/dns_freedns.sh

@@ -53,7 +53,7 @@ dns_freedns_add() {
   i="$(_math "$i" - 1)"
   i="$(_math "$i" - 1)"
   sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
   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
   # returns a page regarding becoming a premium member.  This usually
   # happens after a period of inactivity.  Immediately trying again
   # happens after a period of inactivity.  Immediately trying again
   # returns the correct subdomain page.  So, we will try twice to
   # returns the correct subdomain page.  So, we will try twice to
@@ -72,7 +72,7 @@ dns_freedns_add() {
     fi
     fi
 
 
     # Now convert the tables in the HTML to CSV.  This litte gem from
     # 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" \
     subdomain_csv="$(echo "$htmlpage" \
       | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
       | grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
       | sed 's/^[\ \t]*//g' \
       | sed 's/^[\ \t]*//g' \
@@ -196,7 +196,7 @@ dns_freedns_rm() {
   FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")"
   FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")"
   _debug "FreeDNS login cookies: $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
   # returns a page regarding becoming a premium member.  This usually
   # happens after a period of inactivity.  Immediately trying again
   # happens after a period of inactivity.  Immediately trying again
   # returns the correct subdomain page.  So, we will try twice to
   # returns the correct subdomain page.  So, we will try twice to

+ 1 - 1
dnsapi/dns_ovh.sh

@@ -14,7 +14,7 @@
 #'ovh-eu'
 #'ovh-eu'
 OVH_EU='https://eu.api.ovh.com/1.0'
 OVH_EU='https://eu.api.ovh.com/1.0'
 
 
-#'ovh-ca': 
+#'ovh-ca':
 OVH_CA='https://ca.api.ovh.com/1.0'
 OVH_CA='https://ca.api.ovh.com/1.0'
 
 
 #'kimsufi-eu'
 #'kimsufi-eu'

+ 149 - 0
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
+}