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";
       echo "PATH=$PATH";
       export PATH="";
-      export OPENSSL_BIN="/usr/local/openssl";
+      export ACME_OPENSSL_BIN="/usr/local/openssl";
       openssl version 2>&1 || true;
-      $OPENSSL_BIN version 2>&1 || true;
+      $ACME_OPENSSL_BIN version 2>&1 || true;
       export PATH="$_old_path";
     fi
   
@@ -44,7 +44,7 @@ script:
   - cd ..
   - 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" = "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:

+ 2 - 0
README.md

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

+ 164 - 76
acme.sh

@@ -137,16 +137,16 @@ _printargs() {
 
 _dlg_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
-    echo "$OPENSSL_BIN doesn't exists."
+    echo "$ACME_OPENSSL_BIN doesn't exists."
   fi
 
   echo "apache:"
   if [ "$_APACHECTL" ] && _exists "$_APACHECTL"; then
-    _APACHECTL -V 2>&1
+    $_APACHECTL -V 2>&1
   else
     echo "apache doesn't exists."
   fi
@@ -780,19 +780,19 @@ _base64() {
   [ "" ] #urgly
   if [ "$1" ]; then
     _debug3 "base64 multiline:'$1'"
-    $OPENSSL_BIN base64 -e
+    $ACME_OPENSSL_BIN base64 -e
   else
     _debug3 "base64 single line."
-    $OPENSSL_BIN base64 -e | tr -d '\r\n'
+    $ACME_OPENSSL_BIN base64 -e | tr -d '\r\n'
   fi
 }
 
 #Usage: multiline
 _dbase64() {
   if [ "$1" ]; then
-    $OPENSSL_BIN base64 -d -A
+    $ACME_OPENSSL_BIN base64 -d -A
   else
-    $OPENSSL_BIN base64 -d
+    $ACME_OPENSSL_BIN base64 -d
   fi
 }
 
@@ -809,9 +809,9 @@ _digest() {
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ] || [ "$alg" = "md5" ]; 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
-      $OPENSSL_BIN dgst -"$alg" -binary | _base64
+      $ACME_OPENSSL_BIN dgst -"$alg" -binary | _base64
     fi
   else
     _err "$alg is not supported yet"
@@ -834,9 +834,9 @@ _hmac() {
 
   if [ "$alg" = "sha256" ] || [ "$alg" = "sha1" ]; 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
-      $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
   else
     _err "$alg is not supported yet"
@@ -855,7 +855,7 @@ _sign() {
     return 1
   fi
 
-  _sign_openssl="$OPENSSL_BIN   dgst -sign $keyfile "
+  _sign_openssl="$ACME_OPENSSL_BIN   dgst -sign $keyfile "
   if [ "$alg" = "sha256" ]; then
     _sign_openssl="$_sign_openssl -$alg"
   else
@@ -866,7 +866,7 @@ _sign() {
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     $_sign_openssl | _base64
   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 "Key file: $keyfile"
       _err "Key content:$(wc -l <"$keyfile") lises"
@@ -927,12 +927,21 @@ _createkey() {
 
   _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
     _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
     _debug "Using RSA: $length"
-    $OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f"
+    $ACME_OPENSSL_BIN genrsa "$length" 2>/dev/null >"$f"
   fi
 
   if [ "$?" != "0" ]; then
@@ -1019,9 +1028,9 @@ _createcsr() {
   _csr_cn="$(_idn "$domain")"
   _debug2 _csr_cn "$_csr_cn"
   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
-    $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
 }
 
@@ -1033,7 +1042,7 @@ _signcsr() {
   cert="$4"
   _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="$?"
   _debug "$_msg"
   return $_ret
@@ -1046,7 +1055,7 @@ _readSubjectFromCSR() {
     _usage "_readSubjectFromCSR mycsr.csr"
     return 1
   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
@@ -1061,7 +1070,7 @@ _readSubjectAltNamesFromCSR() {
   _csrsubj="$(_readSubjectFromCSR "$_csrfile")"
   _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"
 
   if _contains "$_dnsAltnames," "DNS:$_csrsubj,"; then
@@ -1082,7 +1091,7 @@ _readKeyLengthFromCSR() {
     return 1
   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
     _debug "ECC CSR"
     echo "$_outcsr" | _egrep_o "^ *ASN1 OID:.*" | cut -d ':' -f 2 | tr -d ' '
@@ -1136,9 +1145,9 @@ toPkcs() {
   _initpath "$domain" "$_isEcc"
 
   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
-    $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
 
   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]  
 createAccountKey() {
   _info "Creating account key"
@@ -1249,12 +1279,12 @@ _url_replace() {
 }
 
 _time2str() {
-  #BSD
+  #Linux
   if date -u -d@"$1" 2>/dev/null; then
     return
   fi
 
-  #Linux
+  #BSD
   if date -u -r "$1" 2>/dev/null; then
     return
   fi
@@ -1300,7 +1330,7 @@ _calcjwk() {
 
   if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     _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
       pub_exp=0$pub_exp
     fi
@@ -1309,7 +1339,7 @@ _calcjwk() {
     e=$(echo "$pub_exp" | _h2b | _base64)
     _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"
     n="$(printf "%s" "$modulus" | _h2b | _base64 | _url_replace)"
     _debug3 n "$n"
@@ -1322,12 +1352,12 @@ _calcjwk() {
     JWK_HEADERPLACE_PART2='", "alg": "RS256", "jwk": '$jwk'}'
   elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
     _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"
 
     if [ -z "$crv" ]; then
       _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"
       case "${crv_oid}" in
         "prime256v1")
@@ -1347,15 +1377,15 @@ _calcjwk() {
       _debug3 crv "$crv"
     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)
     _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)
     _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"
 
     xlen="$(printf "%s" "$pubtext" | tr -d ':' | wc -c)"
@@ -1455,6 +1485,11 @@ _inithttp() {
     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
 
 }
@@ -1475,7 +1510,7 @@ _post() {
 
   _inithttp
 
-  if [ "$_ACME_CURL" ]; then
+  if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
     _CURL="$_ACME_CURL"
     if [ "$HTTPS_INSECURE" ]; then
       _CURL="$_CURL --insecure  "
@@ -1516,7 +1551,7 @@ _post() {
     _ret="$?"
     if [ "$_ret" = "8" ]; then
       _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
     if [ "$_ret" != "0" ]; then
       _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
 
-  if [ "$_ACME_CURL" ]; then
+  if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then
     _CURL="$_ACME_CURL"
     if [ "$HTTPS_INSECURE" ]; then
       _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"
     fi
     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
     if [ "$ret" != "0" ]; then
       _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
   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
     __S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
   else
@@ -2143,8 +2178,8 @@ _initpath() {
     CERT_HOME="$_DEFAULT_CERT_HOME"
   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
 
   if [ -z "$1" ]; then
@@ -2200,6 +2235,9 @@ _initpath() {
   if [ -z "$CERT_PFX_PATH" ]; then
     CERT_PFX_PATH="$DOMAIN_PATH/$domain.pfx"
   fi
+  if [ -z "$CERT_PKCS8_PATH" ]; then
+    CERT_PKCS8_PATH="$DOMAIN_PATH/$domain.pkcs8"
+  fi
 
   if [ -z "$TLS_CONF" ]; then
     TLS_CONF="$DOMAIN_PATH/tls.valdation.conf"
@@ -2795,6 +2833,7 @@ _on_before_issue() {
 
 _on_issue_err() {
   _chk_post_hook="$1"
+  _chk_vlist="$2"
   _debug _on_issue_err
   if [ "$LOG_FILE" ]; then
     _err "Please check log file for more details: $LOG_FILE"
@@ -2803,10 +2842,6 @@ _on_issue_err() {
     _err "See: $_DEBUG_WIKI"
   fi
 
-  if [ "$DEBUG" ] && [ "$DEBUG" -gt "0" ]; then
-    _debug "$(_dlg_versions)"
-  fi
-
   #run the post hook
   if [ "$_chk_post_hook" ]; then
     _info "Run post hook:'$_chk_post_hook'"
@@ -2817,6 +2852,28 @@ _on_issue_err() {
       return 1
     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() {
@@ -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 
 issue() {
   if [ -z "$2" ]; then
@@ -3342,7 +3409,7 @@ issue() {
         _startserver "$keyauthorization" "$_ncaddr" &
         if [ "$?" != "0" ]; then
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
         fi
         serverproc="$!"
@@ -3358,7 +3425,7 @@ issue() {
         BACKUP_NGINX_CONF=""
         if ! _setNginx "$d" "$_currentRoot" "$thumbprint"; then
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
         fi
 
@@ -3393,7 +3460,7 @@ issue() {
           _err "$d:Can not write token to file : $wellknown_path/$token"
           _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
           _clearup
-          _on_issue_err "$_post_hook"
+          _on_issue_err "$_post_hook" "$vlist"
           return 1
         fi
 
@@ -3438,16 +3505,16 @@ issue() {
         _err "Start tls server error."
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
       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"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearup
-      _on_issue_err "$_post_hook"
+      _on_issue_err "$_post_hook" "$vlist"
       return 1
     fi
 
@@ -3455,7 +3522,7 @@ issue() {
       _err "$d:Challenge error: $response"
       _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
       _clearup
-      _on_issue_err "$_post_hook"
+      _on_issue_err "$_post_hook" "$vlist"
       return 1
     fi
 
@@ -3470,7 +3537,7 @@ issue() {
         _err "$d:Timeout"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
-        _on_issue_err
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
       fi
 
@@ -3482,7 +3549,7 @@ issue() {
         _err "$d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
       fi
       _debug2 original "$response"
@@ -3517,7 +3584,7 @@ issue() {
         fi
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
       fi
 
@@ -3527,7 +3594,7 @@ issue() {
         _err "$d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
-        _on_issue_err "$_post_hook"
+        _on_issue_err "$_post_hook" "$vlist"
         return 1
       fi
 
@@ -3653,7 +3720,7 @@ issue() {
     _savedomainconf "Le_RealKeyPath" "$_real_key"
     _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
     _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
 
 }
@@ -3964,16 +4031,18 @@ installcert() {
   _savedomainconf "Le_ReloadCmd" "$_reload_cmd"
   _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() {
   _main_domain="$1"
   _real_cert="$2"
   _real_key="$3"
   _real_ca="$4"
-  _reload_cmd="$5"
-  _real_fullchain="$6"
+  _real_fullchain="$5"
+  _reload_cmd="$6"
+  _backup_prefix="$7"
 
   if [ "$_real_cert" = "$NO_VALUE" ]; then
     _real_cert=""
@@ -3991,11 +4060,13 @@ _installcert() {
     _real_fullchain=""
   fi
 
+  _backup_path="$DOMAIN_BACKUP_PATH/$_backup_prefix"
+  mkdir -p "$_backup_path"
+
   if [ "$_real_cert" ]; then
     _info "Installing cert to:$_real_cert"
     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
     cat "$CERT_PATH" >"$_real_cert"
   fi
@@ -4007,8 +4078,7 @@ _installcert() {
       cat "$CA_CERT_PATH" >>"$_real_ca"
     else
       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
       cat "$CA_CERT_PATH" >"$_real_ca"
     fi
@@ -4017,8 +4087,7 @@ _installcert() {
   if [ "$_real_key" ]; then
     _info "Installing key to:$_real_key"
     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
     cat "$CERT_KEY_PATH" >"$_real_key"
   fi
@@ -4026,8 +4095,7 @@ _installcert() {
   if [ "$_real_fullchain" ]; then
     _info "Installing full chain to:$_real_fullchain"
     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
     cat "$CERT_FULLCHAIN_PATH" >"$_real_fullchain"
   fi
@@ -4367,8 +4435,8 @@ _precheck() {
     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."
     return 1
   fi
@@ -4660,6 +4728,7 @@ Commands:
   --uninstall-cronjob      Uninstall the cron job. The 'uninstall' command can do this automatically.
   --cron                   Run cron job to renew all the certs.
   --toPkcs                 Export the certificate and key to a pfx file.
+  --toPkcs8                Convert to pkcs8 format.
   --update-account         Update account info.
   --register-account       Register account key.
   --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-v6                       Force standalone/tls server to listen at ipv6.
   --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
 
   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
 
   if [ "$_auto_upgrade" ]; then
@@ -4801,6 +4871,12 @@ _processAccountConf() {
     _saveaccountconf "AUTO_UPGRADE" "$AUTO_UPGRADE"
   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() {
@@ -4845,6 +4921,7 @@ _process() {
   _listen_v6=""
   _openssl_bin=""
   _syslog=""
+  _use_wget=""
   while [ ${#} -gt 0 ]; do
     case "${1}" in
 
@@ -4907,6 +4984,9 @@ _process() {
       --toPkcs)
         _CMD="toPkcs"
         ;;
+      --toPkcs8)
+        _CMD="toPkcs8"
+        ;;
       --createAccountKey | --createaccountkey | -cak | --create-account-key)
         _CMD="createAccountKey"
         ;;
@@ -5218,7 +5298,12 @@ _process() {
         ;;
       --openssl-bin)
         _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"
@@ -5319,6 +5404,9 @@ _process() {
     toPkcs)
       toPkcs "$_domain" "$_password" "$_ecc"
       ;;
+    toPkcs8)
+      toPkcs8 "$_domain" "$_ecc"
+      ;;
     createAccountKey)
       createAccountKey "$_accountkeylength"
       ;;

+ 50 - 3
deploy/README.md

@@ -1,19 +1,21 @@
 # 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.
 
 ## 1. Deploy the certs to your cpanel host.
 
 (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:
 
 ```sh
 export DEPLOY_CPANEL_USER=myusername
 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.
@@ -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.
 
+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
 using SSH command to connect to the remote server.  The ssh plugin is invoked
 with the following command...
@@ -107,7 +111,6 @@ user
 Any backups older than 180 days will be deleted when new certificates
 are deployed.  This defaults to "yes" set to "no" to disable backup.
 
-
 ###Eamples using SSH deploy
 The following example illustrates deploying certifcates to a QNAP NAS
 (tested with QTS version 4.2.3)
@@ -165,3 +168,47 @@ export DEPLOY_SSH_BACKUP=no
       /var/lib/unifi/unifi.example.com.p12 \
 && 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
 
-#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.
 

+ 90 - 2
deploy/exim4.sh

@@ -4,6 +4,9 @@
 
 #returns 0 means success, otherwise error.
 
+#DEPLOY_EXIM4_CONF="/etc/exim/exim.conf"
+#DEPLOY_EXIM4_RELOAD="service exim4 restart"
+
 ########  Public functions #####################
 
 #domain keyfile certfile cafile fullchain
@@ -20,7 +23,92 @@ exim4_deploy() {
   _debug _cca "$_cca"
   _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.
 
+#DEPLOY_VSFTPD_CONF="/etc/vsftpd.conf"
+#DEPLOY_VSFTPD_RELOAD="service vsftpd restart"
+
 ########  Public functions #####################
 
 #domain keyfile certfile cafile fullchain
@@ -20,7 +23,88 @@ vsftpd_deploy() {
   _debug _cca "$_cca"
   _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.
 
+## 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
 
 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
 dns_lua_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"
+  _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 ##################################
@@ -129,7 +158,7 @@ _LUA_rest() {
 
   export _H1="Accept: application/json"
   export _H2="Authorization: Basic $LUA_auth"
-  if [ "$data" ]; then
+  if [ "$m" != "GET" ]; then
     _debug data "$data"
     response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
   else

+ 30 - 1
dnsapi/dns_me.sh

@@ -78,7 +78,36 @@ dns_me_add() {
 #fulldomain
 dns_me_rm() {
   fulldomain=$1
+  txtvalue=$2
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+  _debug _domain_id "$_domain_id"
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _debug "Getting txt records"
+  _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
 
+  count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
+  _debug count "$count"
+  if [ "$count" = "0" ]; then
+    _info "Don't need to remove."
+  else
+    record_id=$(printf "%s\n" "$response" | _egrep_o "\"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 ##################################
@@ -130,7 +159,7 @@ _me_rest() {
   export _H2="x-dnsme-requestDate: $cdate"
   export _H3="x-dnsme-hmac: $hmac"
 
-  if [ "$data" ]; then
+  if [ "$m" != "GET" ]; then
     _debug data "$data"
     response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
   else