Browse Source

Merge remote-tracking branch 'upstream/master' into ssh-deploy

David Kerr 8 years ago
parent
commit
b30c1daf72
12 changed files with 753 additions and 89 deletions
  1. 3 3
      .travis.yml
  2. 2 0
      README.md
  3. 164 76
      acme.sh
  4. 50 3
      deploy/README.md
  5. 1 1
      deploy/apache.sh
  6. 90 2
      deploy/exim4.sh
  7. 86 2
      deploy/vsftpd.sh
  8. 26 0
      dnsapi/README.md
  9. 148 0
      dnsapi/dns_do.sh
  10. 123 0
      dnsapi/dns_gandi_livedns.sh
  11. 30 1
      dnsapi/dns_lua.sh
  12. 30 1
      dnsapi/dns_me.sh

+ 3 - 3
.travis.yml

@@ -26,9 +26,9 @@ install:
       _old_path="$PATH";
       _old_path="$PATH";
       echo "PATH=$PATH";
       echo "PATH=$PATH";
       export PATH="";
       export PATH="";
-      export OPENSSL_BIN="/usr/local/openssl";
+      export ACME_OPENSSL_BIN="/usr/local/openssl";
       openssl version 2>&1 || true;
       openssl version 2>&1 || true;
-      $OPENSSL_BIN version 2>&1 || true;
+      $ACME_OPENSSL_BIN version 2>&1 || true;
       export PATH="$_old_path";
       export PATH="$_old_path";
     fi
     fi
   
   
@@ -44,7 +44,7 @@ script:
   - cd ..
   - cd ..
   - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
   - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
   - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
   - if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
-  - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./letest.sh ; fi
+  - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
 
 
 
 
 matrix:
 matrix:

+ 2 - 0
README.md

@@ -293,6 +293,8 @@ You don't have to do anything manually!
 1. Linode.com API
 1. Linode.com API
 1. FreeDNS (https://freedns.afraid.org/)
 1. FreeDNS (https://freedns.afraid.org/)
 1. cyon.ch
 1. cyon.ch
+1. Domain-Offensive/Resellerinterface/Domainrobot API
+1. Gandi LiveDNS API
 
 
 **More APIs coming soon...**
 **More APIs coming soon...**
 
 

+ 164 - 76
acme.sh

@@ -137,16 +137,16 @@ _printargs() {
 
 
 _dlg_versions() {
 _dlg_versions() {
   echo "Diagnosis versions: "
   echo "Diagnosis versions: "
-  echo "openssl:$OPENSSL_BIN"
-  if _exists "$OPENSSL_BIN"; then
-    $OPENSSL_BIN version 2>&1
+  echo "openssl:$ACME_OPENSSL_BIN"
+  if _exists "$ACME_OPENSSL_BIN"; then
+    $ACME_OPENSSL_BIN version 2>&1
   else
   else
-    echo "$OPENSSL_BIN doesn't exists."
+    echo "$ACME_OPENSSL_BIN doesn't exists."
   fi
   fi
 
 
   echo "apache:"
   echo "apache:"
   if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
   if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
-    _APACHECTL -V 2>&1
+    $_APACHECTL -V 2>&1
   else
   else
     echo "apache doesn't exists."
     echo "apache doesn't exists."
   fi
   fi
@@ -780,19 +780,19 @@ _base64() {
   [ "" ] #urgly
   [ "" ] #urgly
   if [ "$1" ]; then
   if [ "$1" ]; then
     _debug3 "base64 multiline:'$1'"
     _debug3 "base64 multiline:'$1'"
-    $OPENSSL_BIN base64 -e
+    $ACME_OPENSSL_BIN base64 -e
   else
   else
     _debug3 "base64 single line."
     _debug3 "base64 single line."
-    $OPENSSL_BIN base64 -e | tr -d '\r\n'
+    $ACME_OPENSSL_BIN base64 -e | tr -d '\r\n'
   fi
   fi
 }
 }
 
 
 #Usage: multiline
 #Usage: multiline
 _dbase64() {
 _dbase64() {
   if [ "$1" ]; then
   if [ "$1" ]; then
-    $OPENSSL_BIN base64 -d -A
+    $ACME_OPENSSL_BIN base64 -d -A
   else
   else
-    $OPENSSL_BIN base64 -d
+    $ACME_OPENSSL_BIN base64 -d
   fi
   fi
 }
 }
 
 
@@ -809,9 +809,9 @@ _digest() {
 
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; then
     if [ "$outputhex" ]; then
     if [ "$outputhex" ]; then
-      $OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
+      $ACME_OPENSSL_BIN dgst -"$alg" -hex | cut -d = -f 2 | tr -d ' '
     else
     else
-      $OPENSSL_BIN dgst -"$alg" -binary | _base64
+      $ACME_OPENSSL_BIN dgst -"$alg" -binary | _base64
     fi
     fi
   else
   else
     _err "$alg is not supported yet"
     _err "$alg is not supported yet"
@@ -834,9 +834,9 @@ _hmac() {
 
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; then
     if [ "$outputhex" ]; then
     if [ "$outputhex" ]; then
-      ($OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || $OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' '
+      ($ACME_OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" 2>/dev/null || $ACME_OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)") | cut -d = -f 2 | tr -d ' '
     else
     else
-      $OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || $OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary
+      $ACME_OPENSSL_BIN dgst -"$alg" -mac HMAC -macopt "hexkey:$secret_hex" -binary 2>/dev/null || $ACME_OPENSSL_BIN dgst -"$alg" -hmac "$(printf "%s" "$secret_hex" | _h2b)" -binary
     fi
     fi
   else
   else
     _err "$alg is not supported yet"
     _err "$alg is not supported yet"
@@ -855,7 +855,7 @@ _sign() {
     return 1
     return 1
   fi
   fi
 
 
-  _sign_openssl="$OPENSSL_BIN   dgst -sign $keyfile "
+  _sign_openssl="$ACME_OPENSSL_BIN   dgst -sign $keyfile "
   if [ "$alg" = "sha256" ]; then
   if [ "$alg" = "sha256" ]; then
     _sign_openssl="$_sign_openssl -$alg"
     _sign_openssl="$_sign_openssl -$alg"
   else
   else
@@ -866,7 +866,7 @@ _sign() {
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     $_sign_openssl | _base64
     $_sign_openssl | _base64
   elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
   elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
-    if ! _signedECText="$($_sign_openssl | $OPENSSL_BIN asn1parse -inform DER)"; then
+    if ! _signedECText="$($_sign_openssl | $ACME_OPENSSL_BIN asn1parse -inform DER)"; then
       _err "Sign failed: $_sign_openssl"
       _err "Sign failed: $_sign_openssl"
       _err "Key file: $keyfile"
       _err "Key file: $keyfile"
       _err "Key content:$(wc -l <"$keyfile") lises"
       _err "Key content:$(wc -l <"$keyfile") lises"
@@ -927,12 +927,21 @@ _createkey() {
 
 
   _debug "Use length $length"
   _debug "Use length $length"
 
 
+  if ! touch "$f" >/dev/null 2>&1; then
+    _f_path="$(dirname "$f")"
+    _debug _f_path "$_f_path"
+    if ! mkdir -p "$_f_path"; then
+      _err "Can not create path: $_f_path"
+      return 1
+    fi
+  fi
+
   if _isEccKey "$length"; then
   if _isEccKey "$length"; then
     _debug "Using ec name: $eccname"
     _debug "Using ec name: $eccname"
-    $OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f"
+    $ACME_OPENSSL_BIN ecparam -name "$eccname" -genkey 2>/dev/null >"$f"
   else
   else
     _debug "Using RSA: $length"
     _debug "Using RSA: $length"
-    $OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f"
+    $ACME_OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f"
   fi
   fi
 
 
   if [ "$?" != "0" ]; then
   if [ "$?" != "0" ]; then
@@ -1019,9 +1028,9 @@ _createcsr() {
   _csr_cn="$(_idn "$domain")"
   _csr_cn="$(_idn "$domain")"
   _debug2 _csr_cn "$_csr_cn"
   _debug2 _csr_cn "$_csr_cn"
   if _contains "$(uname -a)" "MINGW"; then
   if _contains "$(uname -a)" "MINGW"; then
-    $OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
+    $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "//CN=$_csr_cn" -config "$csrconf" -out "$csr"
   else
   else
-    $OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
+    $ACME_OPENSSL_BIN req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr"
   fi
   fi
 }
 }
 
 
@@ -1033,7 +1042,7 @@ _signcsr() {
   cert="$4"
   cert="$4"
   _debug "_signcsr"
   _debug "_signcsr"
 
 
-  _msg="$($OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
+  _msg="$($ACME_OPENSSL_BIN x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
   _ret="$?"
   _ret="$?"
   _debug "$_msg"
   _debug "$_msg"
   return $_ret
   return $_ret
@@ -1046,7 +1055,7 @@ _readSubjectFromCSR() {
     _usage "_readSubjectFromCSR mycsr.csr"
     _usage "_readSubjectFromCSR mycsr.csr"
     return 1
     return 1
   fi
   fi
-  $OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n'
+  $ACME_OPENSSL_BIN req -noout -in "$_csrfile" -subject | _egrep_o "CN *=.*" | cut -d = -f 2 | cut -d / -f 1 | tr -d '\n'
 }
 }
 
 
 #_csrfile
 #_csrfile
@@ -1061,7 +1070,7 @@ _readSubjectAltNamesFromCSR() {
   _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
   _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
   _debug _csrsubj "$_csrsubj"
   _debug _csrsubj "$_csrsubj"
 
 
-  _dnsAltnames="$($OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
+  _dnsAltnames="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile" | grep "^ *DNS:.*" | tr -d ' \n')"
   _debug _dnsAltnames "$_dnsAltnames"
   _debug _dnsAltnames "$_dnsAltnames"
 
 
   if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
   if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
@@ -1082,7 +1091,7 @@ _readKeyLengthFromCSR() {
     return 1
     return 1
   fi
   fi
 
 
-  _outcsr="$($OPENSSL_BIN req -noout -text -in "$_csrfile")"
+  _outcsr="$($ACME_OPENSSL_BIN req -noout -text -in "$_csrfile")"
   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" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
@@ -1136,9 +1145,9 @@ toPkcs() {
   _initpath "$domain" "$_isEcc"
   _initpath "$domain" "$_isEcc"
 
 
   if [ "$pfxPassword" ]; then
   if [ "$pfxPassword" ]; then
-    $OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
+    $ACME_OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
   else
   else
-    $OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
+    $ACME_OPENSSL_BIN pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
   fi
   fi
 
 
   if [ "$?" = "0" ]; then
   if [ "$?" = "0" ]; then
@@ -1147,6 +1156,27 @@ toPkcs() {
 
 
 }
 }
 
 
+#domain [isEcc]
+toPkcs8() {
+  domain="$1"
+
+  if [ -z "$domain" ]; then
+    _usage "Usage: $PROJECT_ENTRY --toPkcs8 -d domain [--ecc]"
+    return 1
+  fi
+
+  _isEcc="$2"
+
+  _initpath "$domain" "$_isEcc"
+
+  $ACME_OPENSSL_BIN pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in "$CERT_KEY_PATH" -out "$CERT_PKCS8_PATH"
+
+  if [ "$?" = "0" ]; then
+    _info "Success, $CERT_PKCS8_PATH"
+  fi
+
+}
+
 #[2048]  
 #[2048]  
 createAccountKey() {
 createAccountKey() {
   _info "Creating account key"
   _info "Creating account key"
@@ -1249,12 +1279,12 @@ _url_replace() {
 }
 }
 
 
 _time2str() {
 _time2str() {
-  #BSD
+  #Linux
   if date -u -d@"$1" 2>/dev/null; then
   if date -u -d@"$1" 2>/dev/null; then
     return
     return
   fi
   fi
 
 
-  #Linux
+  #BSD
   if date -u -r "$1" 2>/dev/null; then
   if date -u -r "$1" 2>/dev/null; then
     return
     return
   fi
   fi
@@ -1300,7 +1330,7 @@ _calcjwk() {
 
 
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     _debug "RSA key"
     _debug "RSA key"
-    pub_exp=$($OPENSSL_BIN rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
+    pub_exp=$($ACME_OPENSSL_BIN rsa -in "$keyfile" -noout -text | grep "^publicExponent:" | cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
     if [ "${#pub_exp}" = "5" ]; then
     if [ "${#pub_exp}" = "5" ]; then
       pub_exp=0$pub_exp
       pub_exp=0$pub_exp
     fi
     fi
@@ -1309,7 +1339,7 @@ _calcjwk() {
     e=$(echo "$pub_exp" | _h2b | _base64)
     e=$(echo "$pub_exp" | _h2b | _base64)
     _debug3 e "$e"
     _debug3 e "$e"
 
 
-    modulus=$($OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
+    modulus=$($ACME_OPENSSL_BIN rsa -in "$keyfile" -modulus -noout | cut -d '=' -f 2)
     _debug3 modulus "$modulus"
     _debug3 modulus "$modulus"
     n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
     n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
     _debug3 n "$n"
     _debug3 n "$n"
@@ -1322,12 +1352,12 @@ _calcjwk() {
     JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
     JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
   elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
   elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     _debug "EC key"
     _debug "EC key"
-    crv="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
+    crv="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
     _debug3 crv "$crv"
     _debug3 crv "$crv"
 
 
     if [ -z "$crv" ]; then
     if [ -z "$crv" ]; then
       _debug "Let's try ASN1 OID"
       _debug "Let's try ASN1 OID"
-      crv_oid="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
+      crv_oid="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
       _debug3 crv_oid "$crv_oid"
       _debug3 crv_oid "$crv_oid"
       case "${crv_oid}" in
       case "${crv_oid}" in
         "prime256v1")
         "prime256v1")
@@ -1347,15 +1377,15 @@ _calcjwk() {
       _debug3 crv "$crv"
       _debug3 crv "$crv"
     fi
     fi
 
 
-    pubi="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
+    pubi="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
     pubi=$(_math "$pubi" + 1)
     pubi=$(_math "$pubi" + 1)
     _debug3 pubi "$pubi"
     _debug3 pubi "$pubi"
 
 
-    pubj="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
+    pubj="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
     pubj=$(_math "$pubj" - 1)
     pubj=$(_math "$pubj" - 1)
     _debug3 pubj "$pubj"
     _debug3 pubj "$pubj"
 
 
-    pubtext="$($OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
+    pubtext="$($ACME_OPENSSL_BIN ec -in "$keyfile" -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
     _debug3 pubtext "$pubtext"
     _debug3 pubtext "$pubtext"
 
 
     xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
     xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
@@ -1455,6 +1485,11 @@ _inithttp() {
     fi
     fi
   fi
   fi
 
 
+  #from wget 1.14: do not skip body on 404 error
+  if [ "$_ACME_WGET" ] && _contains "$($_ACME_WGET --help 2>&1)" "--content-on-error"; then
+    _ACME_WGET="$_ACME_WGET --content-on-error "
+  fi
+
   __HTTP_INITIALIZED=1
   __HTTP_INITIALIZED=1
 
 
 }
 }
@@ -1475,7 +1510,7 @@ _post() {
 
 
   _inithttp
   _inithttp
 
 
-  if [ "$_ACME_CURL" ]; then
+  if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
     _CURL="$_ACME_CURL"
     _CURL="$_ACME_CURL"
     if [ "$HTTPS_INSECURE" ]; then
     if [ "$HTTPS_INSECURE" ]; then
       _CURL="$_CURL --insecure  "
       _CURL="$_CURL --insecure  "
@@ -1516,7 +1551,7 @@ _post() {
     _ret="$?"
     _ret="$?"
     if [ "$_ret" = "8" ]; then
     if [ "$_ret" = "8" ]; then
       _ret=0
       _ret=0
-      _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later."
+      _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
     fi
     fi
     if [ "$_ret" != "0" ]; then
     if [ "$_ret" != "0" ]; then
       _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
       _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
@@ -1542,7 +1577,7 @@ _get() {
 
 
   _inithttp
   _inithttp
 
 
-  if [ "$_ACME_CURL" ]; then
+  if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
     _CURL="$_ACME_CURL"
     _CURL="$_ACME_CURL"
     if [ "$HTTPS_INSECURE" ]; then
     if [ "$HTTPS_INSECURE" ]; then
       _CURL="$_CURL --insecure  "
       _CURL="$_CURL --insecure  "
@@ -1579,9 +1614,9 @@ _get() {
       $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
       $_WGET --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - "$url"
     fi
     fi
     ret=$?
     ret=$?
-    if [ "$_ret" = "8" ]; then
-      _ret=0
-      _debug "wget returns 8, the server returns a 'Bad request' respons, lets process the response later."
+    if [ "$ret" = "8" ]; then
+      ret=0
+      _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later."
     fi
     fi
     if [ "$ret" != "0" ]; then
     if [ "$ret" != "0" ]; then
       _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
       _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $ret"
@@ -1964,7 +1999,7 @@ _starttlsserver() {
     return 1
     return 1
   fi
   fi
 
 
-  __S_OPENSSL="$OPENSSL_BIN s_server -cert $TLS_CERT  -key $TLS_KEY "
+  __S_OPENSSL="$ACME_OPENSSL_BIN s_server -cert $TLS_CERT  -key $TLS_KEY "
   if [ "$opaddr" ]; then
   if [ "$opaddr" ]; then
     __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
     __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
   else
   else
@@ -2143,8 +2178,8 @@ _initpath() {
     CERT_HOME="$_DEFAULT_CERT_HOME"
     CERT_HOME="$_DEFAULT_CERT_HOME"
   fi
   fi
 
 
-  if [ -z "$OPENSSL_BIN" ]; then
-    OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
+  if [ -z "$ACME_OPENSSL_BIN" ] || [ ! -f "$ACME_OPENSSL_BIN" ] || [ ! -x "$ACME_OPENSSL_BIN" ]; then
+    ACME_OPENSSL_BIN="$DEFAULT_OPENSSL_BIN"
   fi
   fi
 
 
   if [ -z "$1" ]; then
   if [ -z "$1" ]; then
@@ -2200,6 +2235,9 @@ _initpath() {
   if [ -z "$CERT_PFX_PATH" ]; then
   if [ -z "$CERT_PFX_PATH" ]; then
     CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
     CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
   fi
   fi
+  if [ -z "$CERT_PKCS8_PATH" ]; then
+    CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8"
+  fi
 
 
   if [ -z "$TLS_CONF" ]; then
   if [ -z "$TLS_CONF" ]; then
     TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
     TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
@@ -2795,6 +2833,7 @@ _on_before_issue() {
 
 
 _on_issue_err() {
 _on_issue_err() {
   _chk_post_hook="$1"
   _chk_post_hook="$1"
+  _chk_vlist="$2"
   _debug _on_issue_err
   _debug _on_issue_err
   if [ "$LOG_FILE" ]; then
   if [ "$LOG_FILE" ]; then
     _err "Please check log file for more details: $LOG_FILE"
     _err "Please check log file for more details: $LOG_FILE"
@@ -2803,10 +2842,6 @@ _on_issue_err() {
     _err "See: $_DEBUG_WIKI"
     _err "See: $_DEBUG_WIKI"
   fi
   fi
 
 
-  if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
-    _debug "$(_dlg_versions)"
-  fi
-
   #run the post hook
   #run the post hook
   if [ "$_chk_post_hook" ]; then
   if [ "$_chk_post_hook" ]; then
     _info "Run post hook:'$_chk_post_hook'"
     _info "Run post hook:'$_chk_post_hook'"
@@ -2817,6 +2852,28 @@ _on_issue_err() {
       return 1
       return 1
     fi
     fi
   fi
   fi
+
+  #trigger the validation to flush the pending authz
+  if [ "$_chk_vlist" ]; then
+    (
+      _debug2 "_chk_vlist" "$_chk_vlist"
+      _debug2 "start to deactivate authz"
+      ventries=$(echo "$_chk_vlist" | tr "$dvsep" ' ')
+      for ventry in $ventries; do
+        d=$(echo "$ventry" | cut -d "$sep" -f 1)
+        keyauthorization=$(echo "$ventry" | cut -d "$sep" -f 2)
+        uri=$(echo "$ventry" | cut -d "$sep" -f 3)
+        vtype=$(echo "$ventry" | cut -d "$sep" -f 4)
+        _currentRoot=$(echo "$ventry" | cut -d "$sep" -f 5)
+        __trigger_validaton "$uri" "$keyauthorization"
+      done
+    )
+  fi
+
+  if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
+    _debug "$(_dlg_versions)"
+  fi
+
 }
 }
 
 
 _on_issue_success() {
 _on_issue_success() {
@@ -3029,6 +3086,16 @@ __get_domain_new_authz() {
 
 
 }
 }
 
 
+#uri keyAuthorization
+__trigger_validaton() {
+  _debug2 "tigger domain validation."
+  _t_url="$1"
+  _debug2 _t_url "$_t_url"
+  _t_key_authz="$2"
+  _debug2 _t_key_authz "$_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
@@ -3342,7 +3409,7 @@ issue() {
         _startserver "$keyauthorization" "$_ncaddr" &
         _startserver "$keyauthorization" "$_ncaddr" &
         if [ "$?" != "0" ]; then
         if [ "$?" != "0" ]; then
           _clearup
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
           return 1
         fi
         fi
         serverproc="$!"
         serverproc="$!"
@@ -3358,7 +3425,7 @@ issue() {
         BACKUP_NGINX_CONF=""
         BACKUP_NGINX_CONF=""
         if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
         if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
           _clearup
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
           return 1
         fi
         fi
 
 
@@ -3393,7 +3460,7 @@ issue() {
           _err "$d:Can not write token to file : $wellknown_path/$token"
           _err "$d:Can not write token to file : $wellknown_path/$token"
           _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
           _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
           _clearup
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
           return 1
         fi
         fi
 
 
@@ -3438,16 +3505,16 @@ issue() {
         _err "Start tls server error."
         _err "Start tls server error."
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
     fi
     fi
 
 
-    if ! _send_signed_request "$uri" "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"; then
+    if ! __trigger_validaton "$uri" "$keyauthorization"; then
       _err "$d:Can not get challenge: $response"
       _err "$d:Can not get challenge: $response"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearup
       _clearup
-      _on_issue_err "$_post_hook"
+      _on_issue_err "$_post_hook" "$vlist"
       return 1
       return 1
     fi
     fi
 
 
@@ -3455,7 +3522,7 @@ issue() {
       _err "$d:Challenge error: $response"
       _err "$d:Challenge error: $response"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearup
       _clearup
-      _on_issue_err "$_post_hook"
+      _on_issue_err "$_post_hook" "$vlist"
       return 1
       return 1
     fi
     fi
 
 
@@ -3470,7 +3537,7 @@ issue() {
         _err "$d:Timeout"
         _err "$d:Timeout"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
-        _on_issue_err
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
 
 
@@ -3482,7 +3549,7 @@ issue() {
         _err "$d:Verify error:$response"
         _err "$d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
       _debug2 original "$response"
       _debug2 original "$response"
@@ -3517,7 +3584,7 @@ issue() {
         fi
         fi
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
 
 
@@ -3527,7 +3594,7 @@ issue() {
         _err "$d:Verify error:$response"
         _err "$d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
         return 1
       fi
       fi
 
 
@@ -3653,7 +3720,7 @@ issue() {
     _savedomainconf "Le_RealKeyPath" "$_real_key"
     _savedomainconf "Le_RealKeyPath" "$_real_key"
     _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
     _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
     _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
     _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
-    _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain"
+    _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
   fi
   fi
 
 
 }
 }
@@ -3964,16 +4031,18 @@ installcert() {
   _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
   _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
   _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
   _savedomainconf "Le_RealFullChainPath" "$_real_fullchain"
 
 
-  _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_reload_cmd" "$_real_fullchain"
+  _installcert "$_main_domain" "$_real_cert" "$_real_key" "$_real_ca" "$_real_fullchain" "$_reload_cmd"
 }
 }
 
 
+#domain  cert  key  ca  fullchain reloadcmd backup-prefix
 _installcert() {
 _installcert() {
   _main_domain="$1"
   _main_domain="$1"
   _real_cert="$2"
   _real_cert="$2"
   _real_key="$3"
   _real_key="$3"
   _real_ca="$4"
   _real_ca="$4"
-  _reload_cmd="$5"
-  _real_fullchain="$6"
+  _real_fullchain="$5"
+  _reload_cmd="$6"
+  _backup_prefix="$7"
 
 
   if [ "$_real_cert" = "$NO_VALUE" ]; then
   if [ "$_real_cert" = "$NO_VALUE" ]; then
     _real_cert=""
     _real_cert=""
@@ -3991,11 +4060,13 @@ _installcert() {
     _real_fullchain=""
     _real_fullchain=""
   fi
   fi
 
 
+  _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
+  mkdir -p "$_backup_path"
+
   if [ "$_real_cert" ]; then
   if [ "$_real_cert" ]; then
     _info "Installing cert to:$_real_cert"
     _info "Installing cert to:$_real_cert"
     if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then
     if [ -f "$_real_cert" ] && [ ! "$IS_RENEW" ]; then
-      mkdir -p "$DOMAIN_BACKUP_PATH"
-      cp "$_real_cert" "$DOMAIN_BACKUP_PATH/cert.bak"
+      cp "$_real_cert" "$_backup_path/cert.bak"
     fi
     fi
     cat "$CERT_PATH" >"$_real_cert"
     cat "$CERT_PATH" >"$_real_cert"
   fi
   fi
@@ -4007,8 +4078,7 @@ _installcert() {
       cat "$CA_CERT_PATH" >>"$_real_ca"
       cat "$CA_CERT_PATH" >>"$_real_ca"
     else
     else
       if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then
       if [ -f "$_real_ca" ] && [ ! "$IS_RENEW" ]; then
-        mkdir -p "$DOMAIN_BACKUP_PATH"
-        cp "$_real_ca" "$DOMAIN_BACKUP_PATH/ca.bak"
+        cp "$_real_ca" "$_backup_path/ca.bak"
       fi
       fi
       cat "$CA_CERT_PATH" >"$_real_ca"
       cat "$CA_CERT_PATH" >"$_real_ca"
     fi
     fi
@@ -4017,8 +4087,7 @@ _installcert() {
   if [ "$_real_key" ]; then
   if [ "$_real_key" ]; then
     _info "Installing key to:$_real_key"
     _info "Installing key to:$_real_key"
     if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
     if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
-      mkdir -p "$DOMAIN_BACKUP_PATH"
-      cp "$_real_key" "$DOMAIN_BACKUP_PATH/key.bak"
+      cp "$_real_key" "$_backup_path/key.bak"
     fi
     fi
     cat "$CERT_KEY_PATH" >"$_real_key"
     cat "$CERT_KEY_PATH" >"$_real_key"
   fi
   fi
@@ -4026,8 +4095,7 @@ _installcert() {
   if [ "$_real_fullchain" ]; then
   if [ "$_real_fullchain" ]; then
     _info "Installing full chain to:$_real_fullchain"
     _info "Installing full chain to:$_real_fullchain"
     if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then
     if [ -f "$_real_fullchain" ] && [ ! "$IS_RENEW" ]; then
-      mkdir -p "$DOMAIN_BACKUP_PATH"
-      cp "$_real_fullchain" "$DOMAIN_BACKUP_PATH/fullchain.bak"
+      cp "$_real_fullchain" "$_backup_path/fullchain.bak"
     fi
     fi
     cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
     cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
   fi
   fi
@@ -4367,8 +4435,8 @@ _precheck() {
     fi
     fi
   fi
   fi
 
 
-  if ! _exists "$OPENSSL_BIN"; then
-    _err "Please install openssl first."
+  if ! _exists "$ACME_OPENSSL_BIN"; then
+    _err "Please install openssl first. ACME_OPENSSL_BIN=$ACME_OPENSSL_BIN"
     _err "We need openssl to generate keys."
     _err "We need openssl to generate keys."
     return 1
     return 1
   fi
   fi
@@ -4660,6 +4728,7 @@ Commands:
   --uninstall-cronjob      Uninstall the cron job. The 'uninstall' command can do this automatically.
   --uninstall-cronjob      Uninstall the cron job. The 'uninstall' command can do this automatically.
   --cron                   Run cron job to renew all the certs.
   --cron                   Run cron job to renew all the certs.
   --toPkcs                 Export the certificate and key to a pfx file.
   --toPkcs                 Export the certificate and key to a pfx file.
+  --toPkcs8                Convert to pkcs8 format.
   --update-account         Update account info.
   --update-account         Update account info.
   --register-account       Register account key.
   --register-account       Register account key.
   --create-account-key     Create an account private key, professional use.
   --create-account-key     Create an account private key, professional use.
@@ -4723,6 +4792,7 @@ Parameters:
   --listen-v4                       Force standalone/tls server to listen at ipv4.
   --listen-v4                       Force standalone/tls server to listen at ipv4.
   --listen-v6                       Force standalone/tls server to listen at ipv6.
   --listen-v6                       Force standalone/tls server to listen at ipv6.
   --openssl-bin                     Specifies a custom openssl bin location.
   --openssl-bin                     Specifies a custom openssl bin location.
+  --use-wget                        Force to use wget, if you have both curl and wget installed.
   "
   "
 }
 }
 
 
@@ -4790,9 +4860,9 @@ _processAccountConf() {
   fi
   fi
 
 
   if [ "$_openssl_bin" ]; then
   if [ "$_openssl_bin" ]; then
-    _saveaccountconf "OPENSSL_BIN" "$_openssl_bin"
-  elif [ "$OPENSSL_BIN" ] && [ "$OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
-    _saveaccountconf "OPENSSL_BIN" "$OPENSSL_BIN"
+    _saveaccountconf "ACME_OPENSSL_BIN" "$_openssl_bin"
+  elif [ "$ACME_OPENSSL_BIN" ] && [ "$ACME_OPENSSL_BIN" != "$DEFAULT_OPENSSL_BIN" ]; then
+    _saveaccountconf "ACME_OPENSSL_BIN" "$ACME_OPENSSL_BIN"
   fi
   fi
 
 
   if [ "$_auto_upgrade" ]; then
   if [ "$_auto_upgrade" ]; then
@@ -4801,6 +4871,12 @@ _processAccountConf() {
     _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
     _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
   fi
   fi
 
 
+  if [ "$_use_wget" ]; then
+    _saveaccountconf "ACME_USE_WGET" "$_use_wget"
+  elif [ "$ACME_USE_WGET" ]; then
+    _saveaccountconf "ACME_USE_WGET" "$ACME_USE_WGET"
+  fi
+
 }
 }
 
 
 _process() {
 _process() {
@@ -4845,6 +4921,7 @@ _process() {
   _listen_v6=""
   _listen_v6=""
   _openssl_bin=""
   _openssl_bin=""
   _syslog=""
   _syslog=""
+  _use_wget=""
   while [ ${#} -gt 0 ]; do
   while [ ${#} -gt 0 ]; do
     case "${1}" in
     case "${1}" in
 
 
@@ -4907,6 +4984,9 @@ _process() {
       --toPkcs)
       --toPkcs)
         _CMD="toPkcs"
         _CMD="toPkcs"
         ;;
         ;;
+      --toPkcs8)
+        _CMD="toPkcs8"
+        ;;
       --createAccountKey | --createaccountkey | -cak | --create-account-key)
       --createAccountKey | --createaccountkey | -cak | --create-account-key)
         _CMD="createAccountKey"
         _CMD="createAccountKey"
         ;;
         ;;
@@ -5218,7 +5298,12 @@ _process() {
         ;;
         ;;
       --openssl-bin)
       --openssl-bin)
         _openssl_bin="$2"
         _openssl_bin="$2"
-        OPENSSL_BIN="$_openssl_bin"
+        ACME_OPENSSL_BIN="$_openssl_bin"
+        shift
+        ;;
+      --use-wget)
+        _use_wget="1"
+        ACME_USE_WGET="1"
         ;;
         ;;
       *)
       *)
         _err "Unknown parameter : $1"
         _err "Unknown parameter : $1"
@@ -5319,6 +5404,9 @@ _process() {
     toPkcs)
     toPkcs)
       toPkcs "$_domain" "$_password" "$_ecc"
       toPkcs "$_domain" "$_password" "$_ecc"
       ;;
       ;;
+    toPkcs8)
+      toPkcs8 "$_domain" "$_ecc"
+      ;;
     createAccountKey)
     createAccountKey)
       createAccountKey "$_accountkeylength"
       createAccountKey "$_accountkeylength"
       ;;
       ;;

+ 50 - 3
deploy/README.md

@@ -1,19 +1,21 @@
 # Using deploy api
 # Using deploy api
 
 
+Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
+
 Here are the scripts to deploy the certs/key to the server/services.
 Here are the scripts to deploy the certs/key to the server/services.
 
 
 ## 1. Deploy the certs to your cpanel host.
 ## 1. Deploy the certs to your cpanel host.
 
 
 (cpanel deploy hook is not finished yet, this is just an example.)
 (cpanel deploy hook is not finished yet, this is just an example.)
 
 
-Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
+
 
 
 Then you can deploy now:
 Then you can deploy now:
 
 
 ```sh
 ```sh
 export DEPLOY_CPANEL_USER=myusername
 export DEPLOY_CPANEL_USER=myusername
 export DEPLOY_CPANEL_PASSWORD=PASSWORD
 export DEPLOY_CPANEL_PASSWORD=PASSWORD
-acme.sh --deploy -d example.com --deploy --deploy-hook cpanel
+acme.sh --deploy -d example.com --deploy-hook cpanel
 ```
 ```
 
 
 ## 2. Deploy ssl cert on kong proxy engine based on api.
 ## 2. Deploy ssl cert on kong proxy engine based on api.
@@ -24,6 +26,8 @@ Before you can deploy your cert, you must [issue the cert first](https://github.
 
 
 ## 3. Deploy the cert to remote server through SSH access.
 ## 3. Deploy the cert to remote server through SSH access.
 
 
+Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
+
 The ssh deploy plugin allows you to deploy certificates to a remote host
 The ssh deploy plugin allows you to deploy certificates to a remote host
 using SSH command to connect to the remote server.  The ssh plugin is invoked
 using SSH command to connect to the remote server.  The ssh plugin is invoked
 with the following command...
 with the following command...
@@ -107,7 +111,6 @@ user
 Any backups older than 180 days will be deleted when new certificates
 Any backups older than 180 days will be deleted when new certificates
 are deployed.  This defaults to "yes" set to "no" to disable backup.
 are deployed.  This defaults to "yes" set to "no" to disable backup.
 
 
-
 ###Eamples using SSH deploy
 ###Eamples using SSH deploy
 The following example illustrates deploying certifcates to a QNAP NAS
 The following example illustrates deploying certifcates to a QNAP NAS
 (tested with QTS version 4.2.3)
 (tested with QTS version 4.2.3)
@@ -165,3 +168,47 @@ export DEPLOY_SSH_BACKUP=no
       /var/lib/unifi/unifi.example.com.p12 \
       /var/lib/unifi/unifi.example.com.p12 \
 && service unifi restart
 && service unifi restart
 ```
 ```
+
+## 4. Deploy the cert to local vsftpd server.
+
+```sh
+acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
+```
+
+The default vsftpd conf file is `/etc/vsftpd.conf`,  if your vsftpd conf is not in the default location, you can specify one:
+
+```sh
+export DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
+
+acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
+```
+
+The default command to restart vsftpd server is `service vsftpd restart`, if it doesn't work, you can specify one:
+
+```sh
+export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
+
+acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
+```
+
+## 5. Deploy the cert to local exim4 server.
+
+```sh
+acme.sh --deploy -d ftp.example.com --deploy-hook exim4
+```
+
+The default exim4 conf file is `/etc/exim/exim.conf`,  if your exim4 conf is not in the default location, you can specify one:
+
+```sh
+export DEPLOY_EXIM4_CONF="/etc/exim4/exim4.conf.template"
+
+acme.sh --deploy -d ftp.example.com --deploy-hook exim4
+```
+
+The default command to restart exim4 server is `service exim4 restart`, if it doesn't work, you can specify one:
+
+```sh
+export DEPLOY_EXIM4_RELOAD="/etc/init.d/exim4 restart"
+
+acme.sh --deploy -d ftp.example.com --deploy-hook exim4
+```

+ 1 - 1
deploy/apache.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env sh
 #!/usr/bin/env sh
 
 
-#Here is a script to deploy cert to dovecot server.
+#Here is a script to deploy cert to apache server.
 
 
 #returns 0 means success, otherwise error.
 #returns 0 means success, otherwise error.
 
 

+ 90 - 2
deploy/exim4.sh

@@ -4,6 +4,9 @@
 
 
 #returns 0 means success, otherwise error.
 #returns 0 means success, otherwise error.
 
 
+#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
+#DEPLOY_EXIM4_RELOAD="service exim4 restart"
+
 ########  Public functions #####################
 ########  Public functions #####################
 
 
 #domain keyfile certfile cafile fullchain
 #domain keyfile certfile cafile fullchain
@@ -20,7 +23,92 @@ exim4_deploy() {
   _debug _cca "$_cca"
   _debug _cca "$_cca"
   _debug _cfullchain "$_cfullchain"
   _debug _cfullchain "$_cfullchain"
 
 
-  _err "deploy cert to exim4 server, Not implemented yet"
-  return 1
+  _ssl_path="/etc/acme.sh/exim4"
+  if ! mkdir -p "$_ssl_path"; then
+    _err "Can not create folder:$_ssl_path"
+    return 1
+  fi
+
+  _info "Copying key and cert"
+  _real_key="$_ssl_path/exim4.key"
+  if ! cat "$_ckey" >"$_real_key"; then
+    _err "Error: write key file to: $_real_key"
+    return 1
+  fi
+  _real_fullchain="$_ssl_path/exim4.pem"
+  if ! cat "$_cfullchain" >"$_real_fullchain"; then
+    _err "Error: write key file to: $_real_fullchain"
+    return 1
+  fi
+
+  DEFAULT_EXIM4_RELOAD="service exim4 restart"
+  _reload="${DEPLOY_EXIM4_RELOAD:-$DEFAULT_EXIM4_RELOAD}"
+
+  if [ -z "$IS_RENEW" ]; then
+    DEFAULT_EXIM4_CONF="/etc/exim/exim.conf"
+    if [ ! -f "$DEFAULT_EXIM4_CONF" ]; then
+      DEFAULT_EXIM4_CONF="/etc/exim4/exim4.conf.template"
+    fi
+    _exim4_conf="${DEPLOY_EXIM4_CONF:-$DEFAULT_EXIM4_CONF}"
+    _debug _exim4_conf "$_exim4_conf"
+    if [ ! -f "$_exim4_conf" ]; then
+      if [ -z "$DEPLOY_EXIM4_CONF" ]; then
+        _err "exim4 conf is not found, please define DEPLOY_EXIM4_CONF"
+        return 1
+      else
+        _err "It seems that the specified exim4 conf is not valid, please check."
+        return 1
+      fi
+    fi
+    if [ ! -w "$_exim4_conf" ]; then
+      _err "The file $_exim4_conf is not writable, please change the permission."
+      return 1
+    fi
+    _backup_conf="$DOMAIN_BACKUP_PATH/exim4.conf.bak"
+    _info "Backup $_exim4_conf to $_backup_conf"
+    cp "$_exim4_conf" "$_backup_conf"
+
+    _info "Modify exim4 conf: $_exim4_conf"
+    if _setopt "$_exim4_conf" "tls_certificate" "=" "$_real_fullchain" \
+      && _setopt "$_exim4_conf" "tls_privatekey" "=" "$_real_key"; then
+      _info "Set config success!"
+    else
+      _err "Config exim4 server error, please report bug to us."
+      _info "Restoring exim4 conf"
+      if cat "$_backup_conf" >"$_exim4_conf"; then
+        _info "Restore conf success"
+        eval "$_reload"
+      else
+        _err "Opps, error restore exim4 conf, please report bug to us."
+      fi
+      return 1
+    fi
+  fi
+
+  _info "Run reload: $_reload"
+  if eval "$_reload"; then
+    _info "Reload success!"
+    if [ "$DEPLOY_EXIM4_CONF" ]; then
+      _savedomainconf DEPLOY_EXIM4_CONF "$DEPLOY_EXIM4_CONF"
+    else
+      _cleardomainconf DEPLOY_EXIM4_CONF
+    fi
+    if [ "$DEPLOY_EXIM4_RELOAD" ]; then
+      _savedomainconf DEPLOY_EXIM4_RELOAD "$DEPLOY_EXIM4_RELOAD"
+    else
+      _cleardomainconf DEPLOY_EXIM4_RELOAD
+    fi
+    return 0
+  else
+    _err "Reload error, restoring"
+    if cat "$_backup_conf" >"$_exim4_conf"; then
+      _info "Restore conf success"
+      eval "$_reload"
+    else
+      _err "Opps, error restore exim4 conf, please report bug to us."
+    fi
+    return 1
+  fi
+  return 0
 
 
 }
 }

+ 86 - 2
deploy/vsftpd.sh

@@ -4,6 +4,9 @@
 
 
 #returns 0 means success, otherwise error.
 #returns 0 means success, otherwise error.
 
 
+#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
+#DEPLOY_VSFTPD_RELOAD="service vsftpd restart"
+
 ########  Public functions #####################
 ########  Public functions #####################
 
 
 #domain keyfile certfile cafile fullchain
 #domain keyfile certfile cafile fullchain
@@ -20,7 +23,88 @@ vsftpd_deploy() {
   _debug _cca "$_cca"
   _debug _cca "$_cca"
   _debug _cfullchain "$_cfullchain"
   _debug _cfullchain "$_cfullchain"
 
 
-  _err "deploy cert to vsftpd server, Not implemented yet"
-  return 1
+  _ssl_path="/etc/acme.sh/vsftpd"
+  if ! mkdir -p "$_ssl_path"; then
+    _err "Can not create folder:$_ssl_path"
+    return 1
+  fi
+
+  _info "Copying key and cert"
+  _real_key="$_ssl_path/vsftpd.key"
+  if ! cat "$_ckey" >"$_real_key"; then
+    _err "Error: write key file to: $_real_key"
+    return 1
+  fi
+  _real_fullchain="$_ssl_path/vsftpd.chain.pem"
+  if ! cat "$_cfullchain" >"$_real_fullchain"; then
+    _err "Error: write key file to: $_real_fullchain"
+    return 1
+  fi
+
+  DEFAULT_VSFTPD_RELOAD="service vsftpd restart"
+  _reload="${DEPLOY_VSFTPD_RELOAD:-$DEFAULT_VSFTPD_RELOAD}"
+
+  if [ -z "$IS_RENEW" ]; then
+    DEFAULT_VSFTPD_CONF="/etc/vsftpd.conf"
+    _vsftpd_conf="${DEPLOY_VSFTPD_CONF:-$DEFAULT_VSFTPD_CONF}"
+    if [ ! -f "$_vsftpd_conf" ]; then
+      if [ -z "$DEPLOY_VSFTPD_CONF" ]; then
+        _err "vsftpd conf is not found, please define DEPLOY_VSFTPD_CONF"
+        return 1
+      else
+        _err "It seems that the specified vsftpd conf is not valid, please check."
+        return 1
+      fi
+    fi
+    if [ ! -w "$_vsftpd_conf" ]; then
+      _err "The file $_vsftpd_conf is not writable, please change the permission."
+      return 1
+    fi
+    _backup_conf="$DOMAIN_BACKUP_PATH/vsftpd.conf.bak"
+    _info "Backup $_vsftpd_conf to $_backup_conf"
+    cp "$_vsftpd_conf" "$_backup_conf"
+
+    _info "Modify vsftpd conf: $_vsftpd_conf"
+    if _setopt "$_vsftpd_conf" "rsa_cert_file" "=" "$_real_fullchain" \
+      && _setopt "$_vsftpd_conf" "rsa_private_key_file" "=" "$_real_key" \
+      && _setopt "$_vsftpd_conf" "ssl_enable" "=" "YES"; then
+      _info "Set config success!"
+    else
+      _err "Config vsftpd server error, please report bug to us."
+      _info "Restoring vsftpd conf"
+      if cat "$_backup_conf" >"$_vsftpd_conf"; then
+        _info "Restore conf success"
+        eval "$_reload"
+      else
+        _err "Opps, error restore vsftpd conf, please report bug to us."
+      fi
+      return 1
+    fi
+  fi
 
 
+  _info "Run reload: $_reload"
+  if eval "$_reload"; then
+    _info "Reload success!"
+    if [ "$DEPLOY_VSFTPD_CONF" ]; then
+      _savedomainconf DEPLOY_VSFTPD_CONF "$DEPLOY_VSFTPD_CONF"
+    else
+      _cleardomainconf DEPLOY_VSFTPD_CONF
+    fi
+    if [ "$DEPLOY_VSFTPD_RELOAD" ]; then
+      _savedomainconf DEPLOY_VSFTPD_RELOAD "$DEPLOY_VSFTPD_RELOAD"
+    else
+      _cleardomainconf DEPLOY_VSFTPD_RELOAD
+    fi
+    return 0
+  else
+    _err "Reload error, restoring"
+    if cat "$_backup_conf" >"$_vsftpd_conf"; then
+      _info "Restore conf success"
+      eval "$_reload"
+    else
+      _err "Opps, error restore vsftpd conf, please report bug to us."
+    fi
+    return 1
+  fi
+  return 0
 }
 }

+ 26 - 0
dnsapi/README.md

@@ -323,6 +323,32 @@ acme.sh --issue --dns dns_cyon -d example.com -d www.example.com
 
 
 The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 The `CY_Username`, `CY_Password` and `CY_OTP_Secret` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 
 
+## 17. Use Domain-Offensive/Resellerinterface/Domainrobot API
+
+You will need your login credentials (Partner ID+Password) to the Resellerinterface, and export them before you run `acme.sh`:
+```
+export DO_PID="KD-1234567"
+export DO_PW="cdfkjl3n2"
+```
+
+Ok, let's issue a cert now:
+```
+acme.sh --issue --dns dns_do -d example.com -d www.example.com
+```
+
+## 18. Use Gandi LiveDNS API
+
+You must enable the new Gandi LiveDNS API first and the create your api key, See: http://doc.livedns.gandi.net/
+
+```
+export GANDI_LIVEDNS_KEY="fdmlfsdklmfdkmqsdfk"
+```
+
+Ok, let's issue a cert now:
+```
+acme.sh --issue --dns dns_gandi_livedns -d example.com -d www.example.com
+```
+
 # 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.

+ 148 - 0
dnsapi/dns_do.sh

@@ -0,0 +1,148 @@
+#!/usr/bin/env sh
+
+# DNS API for Domain-Offensive / Resellerinterface / Domainrobot
+
+# Report bugs at https://github.com/seidler2547/acme.sh/issues
+
+# set these environment variables to match your customer ID and password:
+# DO_PID="KD-1234567"
+# DO_PW="cdfkjl3n2"
+
+DO_URL="https://soap.resellerinterface.de/"
+
+########  Public functions #####################
+
+#Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_do_add() {
+  fulldomain=$1
+  txtvalue=$2
+  if _dns_do_authenticate; then
+    _info "Adding TXT record to ${_domain} as ${fulldomain}"
+    _dns_do_soap createRR origin "${_domain}" name "${fulldomain}" type TXT data "${txtvalue}" ttl 300
+    if _contains "${response}" '>success<'; then
+      return 0
+    fi
+    _err "Could not create resource record, check logs"
+  fi
+  return 1
+}
+
+#fulldomain
+dns_do_rm() {
+  fulldomain=$1
+  if _dns_do_authenticate; then
+    if _dns_do_list_rrs; then
+      _dns_do_had_error=0
+      for _rrid in ${_rr_list}; do
+        _info "Deleting resource record $_rrid for $_domain"
+        _dns_do_soap deleteRR origin "${_domain}" rrid "${_rrid}"
+        if ! _contains "${response}" '>success<'; then
+          _dns_do_had_error=1
+          _err "Could not delete resource record for ${_domain}, id ${_rrid}"
+        fi
+      done
+      return $_dns_do_had_error
+    fi
+  fi
+  return 1
+}
+
+####################  Private functions below ##################################
+_dns_do_authenticate() {
+  _info "Authenticating as ${DO_PID}"
+  _dns_do_soap authPartner partner "${DO_PID}" password "${DO_PW}"
+  if _contains "${response}" '>success<'; then
+    _get_root "$fulldomain"
+    _debug "_domain $_domain"
+    return 0
+  else
+    _err "Authentication failed, are DO_PID and DO_PW set correctly?"
+  fi
+  return 1
+}
+
+_dns_do_list_rrs() {
+  _dns_do_soap getRRList origin "${_domain}"
+  if ! _contains "${response}" 'SOAP-ENC:Array'; then
+    _err "getRRList origin ${_domain} failed"
+    return 1
+  fi
+  _rr_list="$(echo "${response}" \
+    | tr -d "\n\r\t" \
+    | sed -e 's/<item xsi:type="ns2:Map">/\n/g' \
+    | grep ">$(_regexcape "$fulldomain")</value>" \
+    | sed -e 's/<\/item>/\n/g' \
+    | grep '>id</key><value' \
+    | _egrep_o '>[0-9]{1,16}<' \
+    | tr -d '><')"
+  [ "${_rr_list}" ]
+}
+
+_dns_do_soap() {
+  func="$1"
+  shift
+  # put the parameters to xml
+  body="<tns:${func} xmlns:tns=\"${DO_URL}\">"
+  while [ "$1" ]; do
+    _k="$1"
+    shift
+    _v="$1"
+    shift
+    body="$body<$_k>$_v</$_k>"
+  done
+  body="$body</tns:${func}>"
+  _debug2 "SOAP request ${body}"
+
+  # build SOAP XML
+  _xml='<?xml version="1.0" encoding="UTF-8"?>
+<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
+  <env:Body>'"$body"'</env:Body>
+</env:Envelope>'
+
+  # set SOAP headers
+  export _H1="SOAPAction: ${DO_URL}#${func}"
+
+  if ! response="$(_post "${_xml}" "${DO_URL}")"; then
+    _err "Error <$1>"
+    return 1
+  fi
+  _debug2 "SOAP response $response"
+
+  # retrieve cookie header
+  _H2="$(_egrep_o 'Cookie: [^;]+' <"$HTTP_HEADER" | _head_n 1)"
+  export _H2
+
+  return 0
+}
+
+_get_root() {
+  domain=$1
+  i=1
+
+  _dns_do_soap getDomainList
+  _all_domains="$(echo "${response}" \
+    | tr -d "\n\r\t " \
+    | _egrep_o 'domain</key><value[^>]+>[^<]+' \
+    | sed -e 's/^domain<\/key><value[^>]*>//g')"
+
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    if [ -z "$h" ]; then
+      return 1
+    fi
+
+    if _contains "${_all_domains}" "^$(_regexcape "$h")\$"; then
+      _domain="$h"
+      return 0
+    fi
+
+    i=$(_math $i + 1)
+  done
+  _debug "$domain not found"
+
+  return 1
+}
+
+_regexcape() {
+  echo "$1" | sed -e 's/\([]\.$*^[]\)/\\\1/g'
+}

+ 123 - 0
dnsapi/dns_gandi_livedns.sh

@@ -0,0 +1,123 @@
+#!/usr/bin/env sh
+
+# Gandi LiveDNS v5 API
+# http://doc.livedns.gandi.net/
+# currently under beta
+#
+# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
+#
+#Author: Frédéric Crozat <fcrozat@suse.com>
+#Report Bugs here: https://github.com/fcrozat/acme.sh
+#
+########  Public functions #####################
+
+GANDI_LIVEDNS_API="https://dns.beta.gandi.net/api/v5"
+
+#Usage: dns_gandi_livedns_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_gandi_livedns_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if [ -z "$GANDI_LIVEDNS_KEY" ]; then
+    _err "No API key specifed for Gandi LiveDNS."
+    _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+    return 1
+  fi
+
+  _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+
+  _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"
+
+  _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \
+    && _contains "$response" '{"message": "Zone Record Created"}' \
+    && _info "Add $(__green "success")"
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_gandi_livedns_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug fulldomain "$fulldomain"
+  _debug domain "$_domain"
+  _debug sub_domain "$_sub_domain"
+
+  _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" ""
+
+}
+
+####################  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 ! _gandi_livedns_rest GET "domains/$h"; then
+      return 1
+    fi
+
+    if _contains "$response" '"code": 401'; then
+      _err "$response"
+      return 1
+    elif _contains "$response" '"code": 404'; 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
+}
+
+_gandi_livedns_rest() {
+  m=$1
+  ep="$2"
+  data="$3"
+  _debug "$ep"
+
+  export _H1="Content-Type: application/json"
+  export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+  if [ "$m" = "GET" ]; then
+    response="$(_get "$GANDI_LIVEDNS_API/$ep")"
+  else
+    _debug data "$data"
+    response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}

+ 30 - 1
dnsapi/dns_lua.sh

@@ -81,7 +81,36 @@ dns_lua_add() {
 #fulldomain
 #fulldomain
 dns_lua_rm() {
 dns_lua_rm() {
   fulldomain=$1
   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"
+  _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 ##################################
 ####################  Private functions below ##################################
@@ -129,7 +158,7 @@ _LUA_rest() {
 
 
   export _H1="Accept: application/json"
   export _H1="Accept: application/json"
   export _H2="Authorization: Basic $LUA_auth"
   export _H2="Authorization: Basic $LUA_auth"
-  if [ "$data" ]; then
+  if [ "$m" != "GET" ]; then
     _debug data "$data"
     _debug data "$data"
     response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
     response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
   else
   else

+ 30 - 1
dnsapi/dns_me.sh

@@ -78,7 +78,36 @@ dns_me_add() {
 #fulldomain
 #fulldomain
 dns_me_rm() {
 dns_me_rm() {
   fulldomain=$1
   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 "\"id\":[^,]*" | cut -d : -f 2 | 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 ##################################
 ####################  Private functions below ##################################
@@ -130,7 +159,7 @@ _me_rest() {
   export _H2="x-dnsme-requestDate: $cdate"
   export _H2="x-dnsme-requestDate: $cdate"
   export _H3="x-dnsme-hmac: $hmac"
   export _H3="x-dnsme-hmac: $hmac"
 
 
-  if [ "$data" ]; then
+  if [ "$m" != "GET" ]; then
     _debug data "$data"
     _debug data "$data"
     response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
     response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
   else
   else