Browse Source

Merge remote-tracking branch 'upstream/master'

nytral 8 years ago
parent
commit
4ab6786163

+ 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:
 If it is a bug report:
 - make sure you are able to repro it on the latest released version. 
 - 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).
 - Refer to the [WIKI](https://wiki.acme.sh).
 - Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
 - Debug info [Debug](https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh).
 
 
-
 -->
 -->
 
 
 Steps to reproduce
 Steps to reproduce
 ------------------
 ------------------
 
 
-
 Debug log
 Debug log
 -----------------
 -----------------
 
 

+ 4 - 5
.travis.yml

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

+ 39 - 14
README.md

@@ -54,7 +54,9 @@ https://github.com/Neilpang/acmetest
 - Webroot mode
 - Webroot mode
 - Standalone mode
 - Standalone mode
 - Apache mode
 - Apache mode
+- Nginx mode ( Beta )
 - DNS mode
 - DNS mode
+- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 
 
 
 
 # 1. How to install
 # 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:
 **Apache** example:
 ```bash
 ```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
 --certpath      /path/to/certfile/in/apache/cert.pem  \
 --certpath      /path/to/certfile/in/apache/cert.pem  \
 --keypath       /path/to/keyfile/in/apache/key.pem  \
 --keypath       /path/to/keyfile/in/apache/key.pem  \
 --fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
 --fullchainpath /path/to/fullchain/certfile/apache/fullchain.pem \
@@ -155,7 +157,7 @@ acme.sh --installcert -d example.com \
 
 
 **Nginx** example:
 **Nginx** example:
 ```bash
 ```bash
-acme.sh --installcert -d example.com \
+acme.sh --install-cert -d example.com \
 --keypath       /path/to/keyfile/in/nginx/key.pem  \
 --keypath       /path/to/keyfile/in/nginx/key.pem  \
 --fullchainpath /path/to/fullchain/nginx/cert.pem \
 --fullchainpath /path/to/fullchain/nginx/cert.pem \
 --reloadcmd     "service nginx force-reload"
 --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
 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.
 Support the `dns-01` challenge.
 
 
@@ -246,7 +267,7 @@ acme.sh --renew -d example.com
 Ok, it's finished.
 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.
 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. Alwaysdata.com API
 1. Linode.com API
 1. Linode.com API
 1. FreeDNS (https://freedns.afraid.org/)
 1. FreeDNS (https://freedns.afraid.org/)
+1. cyon.ch
+1. Domain-Offensive/Resellerinterface/Domainrobot API
+1. Gandi LiveDNS API
 
 
 **More APIs coming soon...**
 **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)
 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.
 `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.)**
 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.
 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.
 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
 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".
 Speak ACME language using shell, directly to "Let's Encrypt".
 
 
 TODO:
 TODO:
 
 
 
 
-# Acknowledgments
+# 15. Acknowledgments
 
 
 1. Acme-tiny: https://github.com/diafygi/acme-tiny
 1. Acme-tiny: https://github.com/diafygi/acme-tiny
 2. ACME protocol: https://github.com/ietf-wg-acme/acme
 2. ACME protocol: https://github.com/ietf-wg-acme/acme
 3. Certbot: https://github.com/certbot/certbot
 3. Certbot: https://github.com/certbot/certbot
 
 
 
 
-# License & Others
+# 16. License & Others
 
 
 License is GPLv3
 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.
 [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)
 [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
 you create under a FreeDNS public domain.  You must own the top level domain in order to automaitcally
 validate with acme.sh at FreeDNS.
 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
 # 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.

+ 3 - 3
dnsapi/dns_ad.sh

@@ -93,7 +93,7 @@ _get_root() {
       fi
       fi
 
 
       if _contains "$response" "<Name>$h.</Name>"; then
       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"
         _debug hostedzone "$hostedzone"
         if [ -z "$hostedzone" ]; then
         if [ -z "$hostedzone" ]; then
           _err "Error, can not get hostedzone."
           _err "Error, can not get hostedzone."
@@ -181,10 +181,10 @@ aws_rest() {
 
 
   #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
   #kSecret="wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" ############################
 
 
-  _debug2 kSecret "$kSecret"
+  _secure_debug2 kSecret "$kSecret"
 
 
   kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
   kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")"
-  _debug2 kSecretH "$kSecretH"
+  _secure_debug2 kSecretH "$kSecretH"
 
 
   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
   kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)"
   _debug2 kDateH "$kDateH"
   _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 _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
     if [ "$response" = "{}" ]; then
     if [ "$response" = "{}" ]; then
       _info "Added, sleeping 10 seconds"
       _info "Added, sleeping 10 seconds"
-      sleep 10
+      _sleep 10
       #todo: check if the record takes effect
       #todo: check if the record takes effect
       return 0
       return 0
     else
     else

+ 4 - 4
dnsapi/dns_lexicon.sh

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

+ 2 - 2
dnsapi/dns_ovh.sh

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

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