Browse Source

Merge remote-tracking branch 'upstream/master'

nytral 8 years ago
parent
commit
4ab6786163
23 changed files with 1736 additions and 139 deletions
  1. 2 2
      .github/ISSUE_TEMPLATE.md
  2. 4 5
      .travis.yml
  3. 39 14
      README.md
  4. 529 107
      acme.sh
  5. 74 1
      deploy/README.md
  6. 26 0
      deploy/apache.sh
  7. 29 0
      deploy/cpanel.sh
  8. 26 0
      deploy/dovecot.sh
  9. 114 0
      deploy/exim4.sh
  10. 26 0
      deploy/haproxy.sh
  11. 26 0
      deploy/kong.sh
  12. 26 0
      deploy/nginx.sh
  13. 26 0
      deploy/opensshd.sh
  14. 26 0
      deploy/pureftpd.sh
  15. 110 0
      deploy/vsftpd.sh
  16. 44 0
      dnsapi/README.md
  17. 3 3
      dnsapi/dns_ad.sh
  18. 328 0
      dnsapi/dns_cyon.sh
  19. 148 0
      dnsapi/dns_do.sh
  20. 123 0
      dnsapi/dns_gandi_livedns.sh
  21. 1 1
      dnsapi/dns_gd.sh
  22. 4 4
      dnsapi/dns_lexicon.sh
  23. 2 2
      dnsapi/dns_ovh.sh

+ 2 - 2
.github/ISSUE_TEMPLATE.md

@@ -1,4 +1,6 @@
 <!--
+请确保已经更新到最新的代码, 然后贴上来 `--debug 2` 的调试输出. 没有调试输出,我帮不了你.
+如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh
 
 If it is a bug report:
 - make sure you are able to repro it on the latest released version. 
@@ -8,13 +10,11 @@ You can install the latest version by: `acme.sh --upgrade`
 - Refer to the [WIKI](https://wiki.acme.sh).
 - Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
 
-
 -->
 
 Steps to reproduce
 ------------------
 
-
 Debug log
 -----------------
 

+ 4 - 5
.travis.yml

@@ -26,14 +26,13 @@ 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
   
 script:
-  - echo "TEST_LOCAL=$TEST_LOCAL"
   - echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
   - command -V openssl && openssl version
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then curl -sSL $SHFMT_URL -o ~/shfmt ; fi
@@ -44,8 +43,8 @@ script:
   - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck **/*.sh && echo "shellcheck OK" ; fi
   - 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 NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
-  - if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo NGROK_TOKEN="$NGROK_TOKEN" OPENSSL_BIN="$OPENSSL_BIN" ./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" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
 
 
 matrix:

+ 39 - 14
README.md

@@ -54,7 +54,9 @@ https://github.com/Neilpang/acmetest
 - Webroot mode
 - Standalone mode
 - Apache mode
+- Nginx mode ( Beta )
 - DNS mode
+- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 
 
 # 1. How to install
@@ -146,7 +148,7 @@ You **MUST** use this command to copy the certs to the target files, **DO NOT**
 
 **Apache** example:
 ```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
 --certpath      /path/to/certfile/in/apache/cert.pem  \
 --keypath       /path/to/keyfile/in/apache/key.pem  \
 --fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
@@ -155,7 +157,7 @@ acme.sh --installcert -d example.com \
 
 **Nginx** example:
 ```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
 --keypath       /path/to/keyfile/in/nginx/key.pem  \
 --fullchainpath /path/to/fullchain/nginx/cert.pem \
 --reloadcmd     "service nginx force-reload"
@@ -214,8 +216,27 @@ acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
 
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 
+# 7. Use Nginx mode
 
-# 7. Use DNS mode:
+**(requires you to be root/sudoer, since it is required to interact with Nginx server)**
+
+If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
+
+Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder.
+
+Just set string "nginx" as the second argument.
+
+It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version.
+
+So, the config is not changed.
+
+```
+acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
+```
+
+More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
+
+# 8. Use DNS mode:
 
 Support the `dns-01` challenge.
 
@@ -246,7 +267,7 @@ acme.sh --renew -d example.com
 Ok, it's finished.
 
 
-# 8. Automatic DNS API integration
+# 9. Automatic DNS API integration
 
 If your DNS provider supports API access, we can use that API to automatically issue the certs.
 
@@ -271,6 +292,9 @@ You don't have to do anything manually!
 1. Alwaysdata.com API
 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...**
 
@@ -279,7 +303,7 @@ If your DNS provider is not on the supported list above, you can write your own
 For more details: [How to use DNS API](dnsapi)
 
 
-# 9. Issue ECC certificates
+# 10. Issue ECC certificates
 
 `Let's Encrypt` can now issue **ECDSA** certificates.
 
@@ -310,7 +334,7 @@ Valid values are:
 3. **ec-521 (secp521r1,  "ECDSA P-521", which is not supported by Let's Encrypt yet.)**
 
 
-# 10. How to renew the issued certs
+# 11. How to renew the issued certs
 
 No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days.
 
@@ -327,7 +351,7 @@ acme.sh --renew -d example.com --force --ecc
 ```
 
 
-# 11. How to upgrade `acme.sh`
+# 12. How to upgrade `acme.sh`
 
 acme.sh is in constant development, so it's strongly recommended to use the latest code.
 
@@ -352,26 +376,26 @@ acme.sh --upgrade --auto-upgrade 0
 ```
 
 
-# 12. Issue a cert from an existing CSR
+# 13. Issue a cert from an existing CSR
 
 https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR
 
 
-# Under the Hood
+# 14. Under the Hood
 
 Speak ACME language using shell, directly to "Let's Encrypt".
 
 TODO:
 
 
-# Acknowledgments
+# 15. Acknowledgments
 
 1. Acme-tiny: https://github.com/diafygi/acme-tiny
 2. ACME protocol: https://github.com/ietf-wg-acme/acme
 3. Certbot: https://github.com/certbot/certbot
 
 
-# License & Others
+# 16. License & Others
 
 License is GPLv3
 
@@ -380,8 +404,9 @@ Please Star and Fork me.
 [Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome.
 
 
-# Donate
-
-1. PayPal: donate@acme.sh
+# 17. Donate
+Your donation makes **acme.sh** better:
 
+1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/)
+  
 [Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list)

File diff suppressed because it is too large
+ 529 - 107
acme.sh


+ 74 - 1
deploy/README.md

@@ -1 +1,74 @@
-#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.
+
+## 1. Deploy the certs to your cpanel host.
+
+(cpanel deploy hook is not finished yet, this is just an example.)
+
+
+
+Then you can deploy now:
+
+```sh
+export DEPLOY_CPANEL_USER=myusername
+export DEPLOY_CPANEL_PASSWORD=PASSWORD
+acme.sh --deploy -d example.com --deploy-hook cpanel
+```
+
+## 2. Deploy ssl cert on kong proxy engine based on 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).
+
+(TODO)
+
+## 3. Deploy the cert to remote server through SSH access.
+
+(TODO)
+
+## 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
+```
+

+ 26 - 0
deploy/apache.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to dovecot server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+apache_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "Deploy cert to apache server, Not implemented yet"
+  return 1
+
+}

+ 29 - 0
deploy/cpanel.sh

@@ -0,0 +1,29 @@
+#!/usr/bin/env sh
+
+#Here is the script to deploy the cert to your cpanel account by the cpanel APIs.
+
+#returns 0 means success, otherwise error.
+
+#export DEPLOY_CPANEL_USER=myusername
+#export DEPLOY_CPANEL_PASSWORD=PASSWORD
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+cpanel_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "Not implemented yet"
+  return 1
+
+}

+ 26 - 0
deploy/dovecot.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to dovecot server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+dovecot_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "Not implemented yet"
+  return 1
+
+}

+ 114 - 0
deploy/exim4.sh

@@ -0,0 +1,114 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to exim4 server.
+
+#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
+exim4_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _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
+
+}

+ 26 - 0
deploy/haproxy.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to haproxy server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+haproxy_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "deploy cert to haproxy server, Not implemented yet"
+  return 1
+
+}

+ 26 - 0
deploy/kong.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to mysqld server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+mysqld_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "deploy cert to mysqld server, Not implemented yet"
+  return 1
+
+}

+ 26 - 0
deploy/nginx.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to nginx server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+nginx_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "deploy cert to nginx server, Not implemented yet"
+  return 1
+
+}

+ 26 - 0
deploy/opensshd.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to opensshd server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+opensshd_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "deploy cert to opensshd server, Not implemented yet"
+  return 1
+
+}

+ 26 - 0
deploy/pureftpd.sh

@@ -0,0 +1,26 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to pureftpd server.
+
+#returns 0 means success, otherwise error.
+
+########  Public functions #####################
+
+#domain keyfile certfile cafile fullchain
+pureftpd_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _err "deploy cert to pureftpd server, Not implemented yet"
+  return 1
+
+}

+ 110 - 0
deploy/vsftpd.sh

@@ -0,0 +1,110 @@
+#!/usr/bin/env sh
+
+#Here is a script to deploy cert to vsftpd server.
+
+#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
+vsftpd_deploy() {
+  _cdomain="$1"
+  _ckey="$2"
+  _ccert="$3"
+  _cca="$4"
+  _cfullchain="$5"
+
+  _debug _cdomain "$_cdomain"
+  _debug _ckey "$_ckey"
+  _debug _ccert "$_ccert"
+  _debug _cca "$_cca"
+  _debug _cfullchain "$_cfullchain"
+
+  _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
+}

+ 44 - 0
dnsapi/README.md

@@ -305,6 +305,50 @@ Note that you cannot use acme.sh automatic DNS validation for FreeDNS public dom
 you create under a FreeDNS public domain.  You must own the top level domain in order to automaitcally
 validate with acme.sh at FreeDNS.
 
+## 16. Use cyon.ch
+
+You only need to set your cyon.ch login credentials.
+If you also have 2 Factor Authentication (OTP) enabled, you need to set your secret token too and have `oathtool` installed.
+
+```
+export CY_Username="your_cyon_username"
+export CY_Password="your_cyon_password"
+export CY_OTP_Secret="your_otp_secret" # Only required if using 2FA
+```
+
+To issue a cert:
+```
+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.

+ 3 - 3
dnsapi/dns_ad.sh

@@ -93,7 +93,7 @@ _get_root() {
       fi
 
       if _contains "$response" "<Name>$h.</Name>"; then
-        hostedzone="$(echo "$response" | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.HostedZone>")"
+        hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<.HostedZone>")"
         _debug hostedzone "$hostedzone"
         if [ -z "$hostedzone" ]; then
           _err "Error, can not get hostedzone."
@@ -181,10 +181,10 @@ aws_rest() {
 
   #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
 
-  _debug2 kSecret "$kSecret"
+  _secure_debug2 kSecret "$kSecret"
 
   kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
-  _debug2 kSecretH "$kSecretH"
+  _secure_debug2 kSecretH "$kSecretH"
 
   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
   _debug2 kDateH "$kDateH"

+ 328 - 0
dnsapi/dns_cyon.sh

@@ -0,0 +1,328 @@
+#!/usr/bin/env sh
+
+########
+# Custom cyon.ch DNS API for use with [acme.sh](https://github.com/Neilpang/acme.sh)
+#
+# Usage: acme.sh --issue --dns dns_cyon -d www.domain.com
+#
+# Dependencies:
+# -------------
+# - oathtool (When using 2 Factor Authentication)
+#
+# Issues:
+# -------
+# Any issues / questions / suggestions can be posted here:
+# https://github.com/noplanman/cyon-api/issues
+#
+# Author: Armando Lüscher <armando@noplanman.ch>
+########
+
+dns_cyon_add() {
+  _cyon_load_credentials \
+    && _cyon_load_parameters "$@" \
+    && _cyon_print_header "add" \
+    && _cyon_login \
+    && _cyon_change_domain_env \
+    && _cyon_add_txt \
+    && _cyon_logout
+}
+
+dns_cyon_rm() {
+  _cyon_load_credentials \
+    && _cyon_load_parameters "$@" \
+    && _cyon_print_header "delete" \
+    && _cyon_login \
+    && _cyon_change_domain_env \
+    && _cyon_delete_txt \
+    && _cyon_logout
+}
+
+#########################
+### PRIVATE FUNCTIONS ###
+#########################
+
+_cyon_load_credentials() {
+  # Convert loaded password to/from base64 as needed.
+  if [ "${CY_Password_B64}" ]; then
+    CY_Password="$(printf "%s" "${CY_Password_B64}" | _dbase64 "multiline")"
+  elif [ "${CY_Password}" ]; then
+    CY_Password_B64="$(printf "%s" "${CY_Password}" | _base64)"
+  fi
+
+  if [ -z "${CY_Username}" ] || [ -z "${CY_Password}" ]; then
+    # Dummy entries to satify script checker.
+    CY_Username=""
+    CY_Password=""
+    CY_OTP_Secret=""
+
+    _err ""
+    _err "You haven't set your cyon.ch login credentials yet."
+    _err "Please set the required cyon environment variables."
+    _err ""
+    return 1
+  fi
+
+  # Save the login credentials to the account.conf file.
+  _debug "Save credentials to account.conf"
+  _saveaccountconf CY_Username "${CY_Username}"
+  _saveaccountconf CY_Password_B64 "$CY_Password_B64"
+  if [ ! -z "${CY_OTP_Secret}" ]; then
+    _saveaccountconf CY_OTP_Secret "$CY_OTP_Secret"
+  else
+    _clearaccountconf CY_OTP_Secret
+  fi
+}
+
+_cyon_is_idn() {
+  _idn_temp="$(printf "%s" "${1}" | tr -d "0-9a-zA-Z.,-_")"
+  _idn_temp2="$(printf "%s" "${1}" | grep -o "xn--")"
+  [ "$_idn_temp" ] || [ "$_idn_temp2" ]
+}
+
+_cyon_load_parameters() {
+  # Read the required parameters to add the TXT entry.
+  # shellcheck disable=SC2018,SC2019
+  fulldomain="$(printf "%s" "${1}" | tr "A-Z" "a-z")"
+  fulldomain_idn="${fulldomain}"
+
+  # Special case for IDNs, as cyon needs a domain environment change,
+  # which uses the "pretty" instead of the punycode version.
+  if _cyon_is_idn "${fulldomain}"; then
+    if ! _exists idn; then
+      _err "Please install idn to process IDN names."
+      _err ""
+      return 1
+    fi
+
+    fulldomain="$(idn -u "${fulldomain}")"
+    fulldomain_idn="$(idn -a "${fulldomain}")"
+  fi
+
+  _debug fulldomain "${fulldomain}"
+  _debug fulldomain_idn "${fulldomain_idn}"
+
+  txtvalue="${2}"
+  _debug txtvalue "${txtvalue}"
+
+  # This header is required for curl calls.
+  _H1="X-Requested-With: XMLHttpRequest"
+  export _H1
+}
+
+_cyon_print_header() {
+  if [ "${1}" = "add" ]; then
+    _info ""
+    _info "+---------------------------------------------+"
+    _info "| Adding DNS TXT entry to your cyon.ch domain |"
+    _info "+---------------------------------------------+"
+    _info ""
+    _info "  * Full Domain: ${fulldomain}"
+    _info "  * TXT Value:   ${txtvalue}"
+    _info ""
+  elif [ "${1}" = "delete" ]; then
+    _info ""
+    _info "+-------------------------------------------------+"
+    _info "| Deleting DNS TXT entry from your cyon.ch domain |"
+    _info "+-------------------------------------------------+"
+    _info ""
+    _info "  * Full Domain: ${fulldomain}"
+    _info ""
+  fi
+}
+
+_cyon_get_cookie_header() {
+  printf "Cookie: %s" "$(grep "cyon=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'cyon=[^;]*;' | tr -d ';')"
+}
+
+_cyon_login() {
+  _info "  - Logging in..."
+
+  username_encoded="$(printf "%s" "${CY_Username}" | _url_encode)"
+  password_encoded="$(printf "%s" "${CY_Password}" | _url_encode)"
+
+  login_url="https://my.cyon.ch/auth/index/dologin-async"
+  login_data="$(printf "%s" "username=${username_encoded}&password=${password_encoded}&pathname=%2F")"
+
+  login_response="$(_post "$login_data" "$login_url")"
+  _debug login_response "${login_response}"
+
+  # Bail if login fails.
+  if [ "$(printf "%s" "${login_response}" | _cyon_get_response_success)" != "success" ]; then
+    _err "    $(printf "%s" "${login_response}" | _cyon_get_response_message)"
+    _err ""
+    return 1
+  fi
+
+  _info "    success"
+
+  # NECESSARY!! Load the main page after login, to get the new cookie.
+  _H2="$(_cyon_get_cookie_header)"
+  export _H2
+
+  _get "https://my.cyon.ch/" >/dev/null
+
+  # todo: instead of just checking if the env variable is defined, check if we actually need to do a 2FA auth request.
+
+  # 2FA authentication with OTP?
+  if [ ! -z "${CY_OTP_Secret}" ]; then
+    _info "  - Authorising with OTP code..."
+
+    if ! _exists oathtool; then
+      _err "Please install oathtool to use 2 Factor Authentication."
+      _err ""
+      return 1
+    fi
+
+    # Get OTP code with the defined secret.
+    otp_code="$(oathtool --base32 --totp "${CY_OTP_Secret}" 2>/dev/null)"
+
+    login_otp_url="https://my.cyon.ch/auth/multi-factor/domultifactorauth-async"
+    login_otp_data="totpcode=${otp_code}&pathname=%2F&rememberme=0"
+
+    login_otp_response="$(_post "$login_otp_data" "$login_otp_url")"
+    _debug login_otp_response "${login_otp_response}"
+
+    # Bail if OTP authentication fails.
+    if [ "$(printf "%s" "${login_otp_response}" | _cyon_get_response_success)" != "success" ]; then
+      _err "    $(printf "%s" "${login_otp_response}" | _cyon_get_response_message)"
+      _err ""
+      return 1
+    fi
+
+    _info "    success"
+  fi
+
+  _info ""
+}
+
+_cyon_logout() {
+  _info "  - Logging out..."
+
+  _get "https://my.cyon.ch/auth/index/dologout" >/dev/null
+
+  _info "    success"
+  _info ""
+}
+
+_cyon_change_domain_env() {
+  _info "  - Changing domain environment..."
+
+  # Get the "example.com" part of the full domain name.
+  domain_env="$(printf "%s" "${fulldomain}" | sed -E -e 's/.*\.(.*\..*)$/\1/')"
+  _debug "Changing domain environment to ${domain_env}"
+
+  gloo_item_key="$(_get "https://my.cyon.ch/domain/" | tr '\n' ' ' | sed -E -e "s/.*data-domain=\"${domain_env}\"[^<]*data-itemkey=\"([^\"]*).*/\1/")"
+  _debug gloo_item_key "${gloo_item_key}"
+
+  domain_env_url="https://my.cyon.ch/user/environment/setdomain/d/${domain_env}/gik/${gloo_item_key}"
+
+  domain_env_response="$(_get "${domain_env_url}")"
+  _debug domain_env_response "${domain_env_response}"
+
+  if ! _cyon_check_if_2fa_missed "${domain_env_response}"; then return 1; fi
+
+  domain_env_success="$(printf "%s" "${domain_env_response}" | _egrep_o '"authenticated":\w*' | cut -d : -f 2)"
+
+  # Bail if domain environment change fails.
+  if [ "${domain_env_success}" != "true" ]; then
+    _err "    $(printf "%s" "${domain_env_response}" | _cyon_get_response_message)"
+    _err ""
+    return 1
+  fi
+
+  _info "    success"
+  _info ""
+}
+
+_cyon_add_txt() {
+  _info "  - Adding DNS TXT entry..."
+
+  add_txt_url="https://my.cyon.ch/domain/dnseditor/add-record-async"
+  add_txt_data="zone=${fulldomain_idn}.&ttl=900&type=TXT&value=${txtvalue}"
+
+  add_txt_response="$(_post "$add_txt_data" "$add_txt_url")"
+  _debug add_txt_response "${add_txt_response}"
+
+  if ! _cyon_check_if_2fa_missed "${add_txt_response}"; then return 1; fi
+
+  add_txt_message="$(printf "%s" "${add_txt_response}" | _cyon_get_response_message)"
+  add_txt_status="$(printf "%s" "${add_txt_response}" | _cyon_get_response_status)"
+
+  # Bail if adding TXT entry fails.
+  if [ "${add_txt_status}" != "true" ]; then
+    _err "    ${add_txt_message}"
+    _err ""
+    return 1
+  fi
+
+  _info "    success (TXT|${fulldomain_idn}.|${txtvalue})"
+  _info ""
+}
+
+_cyon_delete_txt() {
+  _info "  - Deleting DNS TXT entry..."
+
+  list_txt_url="https://my.cyon.ch/domain/dnseditor/list-async"
+
+  list_txt_response="$(_get "${list_txt_url}" | sed -e 's/data-hash/\\ndata-hash/g')"
+  _debug list_txt_response "${list_txt_response}"
+
+  if ! _cyon_check_if_2fa_missed "${list_txt_response}"; then return 1; fi
+
+  # Find and delete all acme challenge entries for the $fulldomain.
+  _dns_entries="$(printf "%b\n" "${list_txt_response}" | sed -n 's/data-hash=\\"\([^"]*\)\\" data-identifier=\\"\([^"]*\)\\".*/\1 \2/p')"
+
+  printf "%s" "${_dns_entries}" | while read -r _hash _identifier; do
+    dns_type="$(printf "%s" "$_identifier" | cut -d'|' -f1)"
+    dns_domain="$(printf "%s" "$_identifier" | cut -d'|' -f2)"
+
+    if [ "${dns_type}" != "TXT" ] || [ "${dns_domain}" != "${fulldomain_idn}." ]; then
+      continue
+    fi
+
+    hash_encoded="$(printf "%s" "${_hash}" | _url_encode)"
+    identifier_encoded="$(printf "%s" "${_identifier}" | _url_encode)"
+
+    delete_txt_url="https://my.cyon.ch/domain/dnseditor/delete-record-async"
+    delete_txt_data="$(printf "%s" "hash=${hash_encoded}&identifier=${identifier_encoded}")"
+
+    delete_txt_response="$(_post "$delete_txt_data" "$delete_txt_url")"
+    _debug delete_txt_response "${delete_txt_response}"
+
+    if ! _cyon_check_if_2fa_missed "${delete_txt_response}"; then return 1; fi
+
+    delete_txt_message="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_message)"
+    delete_txt_status="$(printf "%s" "${delete_txt_response}" | _cyon_get_response_status)"
+
+    # Skip if deleting TXT entry fails.
+    if [ "${delete_txt_status}" != "true" ]; then
+      _err "    ${delete_txt_message} (${_identifier})"
+    else
+      _info "    success (${_identifier})"
+    fi
+  done
+
+  _info "    done"
+  _info ""
+}
+
+_cyon_get_response_message() {
+  _egrep_o '"message":"[^"]*"' | cut -d : -f 2 | tr -d '"'
+}
+
+_cyon_get_response_status() {
+  _egrep_o '"status":\w*' | cut -d : -f 2
+}
+
+_cyon_get_response_success() {
+  _egrep_o '"onSuccess":"[^"]*"' | cut -d : -f 2 | tr -d '"'
+}
+
+_cyon_check_if_2fa_missed() {
+  # Did we miss the 2FA?
+  if test "${1#*multi_factor_form}" != "${1}"; then
+    _err "    Missed OTP authentication!"
+    _err ""
+    return 1
+  fi
+}

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

+ 1 - 1
dnsapi/dns_gd.sh

@@ -40,7 +40,7 @@ dns_gd_add() {
   if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
     if [ "$response" = "{}" ]; then
       _info "Added, sleeping 10 seconds"
-      sleep 10
+      _sleep 10
       #todo: check if the record takes effect
       return 0
     else

+ 4 - 4
dnsapi/dns_lexicon.sh

@@ -34,7 +34,7 @@ dns_lexicon_add() {
   # shellcheck disable=SC2018,SC2019
   Lx_name=$(echo LEXICON_"${PROVIDER}"_USERNAME | tr 'a-z' 'A-Z')
   Lx_name_v=$(eval echo \$"$Lx_name")
-  _debug "$Lx_name" "$Lx_name_v"
+  _secure_debug "$Lx_name" "$Lx_name_v"
   if [ "$Lx_name_v" ]; then
     _saveaccountconf "$Lx_name" "$Lx_name_v"
     eval export "$Lx_name"
@@ -43,7 +43,7 @@ dns_lexicon_add() {
   # shellcheck disable=SC2018,SC2019
   Lx_token=$(echo LEXICON_"${PROVIDER}"_TOKEN | tr 'a-z' 'A-Z')
   Lx_token_v=$(eval echo \$"$Lx_token")
-  _debug "$Lx_token" "$Lx_token_v"
+  _secure_debug "$Lx_token" "$Lx_token_v"
   if [ "$Lx_token_v" ]; then
     _saveaccountconf "$Lx_token" "$Lx_token_v"
     eval export "$Lx_token"
@@ -52,7 +52,7 @@ dns_lexicon_add() {
   # shellcheck disable=SC2018,SC2019
   Lx_password=$(echo LEXICON_"${PROVIDER}"_PASSWORD | tr 'a-z' 'A-Z')
   Lx_password_v=$(eval echo \$"$Lx_password")
-  _debug "$Lx_password" "$Lx_password_v"
+  _secure_debug "$Lx_password" "$Lx_password_v"
   if [ "$Lx_password_v" ]; then
     _saveaccountconf "$Lx_password" "$Lx_password_v"
     eval export "$Lx_password"
@@ -61,7 +61,7 @@ dns_lexicon_add() {
   # shellcheck disable=SC2018,SC2019
   Lx_domaintoken=$(echo LEXICON_"${PROVIDER}"_DOMAINTOKEN | tr 'a-z' 'A-Z')
   Lx_domaintoken_v=$(eval echo \$"$Lx_domaintoken")
-  _debug "$Lx_domaintoken" "$Lx_domaintoken_v"
+  _secure_debug "$Lx_domaintoken" "$Lx_domaintoken_v"
   if [ "$Lx_domaintoken_v" ]; then
     eval export "$Lx_domaintoken"
     _saveaccountconf "$Lx_domaintoken" "$Lx_domaintoken_v"

+ 2 - 2
dnsapi/dns_ovh.sh

@@ -207,7 +207,7 @@ _ovh_authentication() {
     _err "Unable to get consumerKey"
     return 1
   fi
-  _debug consumerKey "$consumerKey"
+  _secure_debug consumerKey "$consumerKey"
 
   OVH_CK="$consumerKey"
   _saveaccountconf OVH_CK "$OVH_CK"
@@ -269,7 +269,7 @@ _ovh_rest() {
   _ovh_t="$(_ovh_timestamp)"
   _debug2 _ovh_t "$_ovh_t"
   _ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
-  _debug _ovh_p "$_ovh_p"
+  _secure_debug _ovh_p "$_ovh_p"
   _ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
   _debug2 _ovh_hex "$_ovh_hex"
 

Some files were not shown because too many files changed in this diff