Browse Source

Merge branch 'acmesh-official:master' into master

laraveluser 1 năm trước cách đây
mục cha
commit
a6a1de50c8

+ 70 - 19
.github/workflows/DNS.yml

@@ -65,7 +65,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
     - name: Set env file
@@ -113,7 +113,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run:  brew install socat
     - name: Clone acmetest
@@ -164,7 +164,7 @@ jobs:
     - name: Set git to use LF
       run: |
           git config --global core.autocrlf false
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install cygwin base packages with chocolatey
       run: |
           choco config get cacheLocation
@@ -204,7 +204,7 @@ jobs:
 
 
   FreeBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: Windows
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -223,10 +223,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0
+    - uses: vmactions/freebsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: pkg install -y socat curl
@@ -255,7 +255,7 @@ jobs:
 
 
   OpenBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: FreeBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -274,10 +274,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/openbsd-vm@v0
+    - uses: vmactions/openbsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: pkg_add socat curl
@@ -306,7 +306,7 @@ jobs:
 
 
   NetBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: OpenBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -325,10 +325,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/netbsd-vm@v0
+    - uses: vmactions/netbsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
@@ -358,7 +358,7 @@ jobs:
 
 
   DragonFlyBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: NetBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -377,14 +377,14 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/dragonflybsd-vm@v0
+    - uses: vmactions/dragonflybsd-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         prepare: |
-          pkg install -y curl socat
+          pkg install -y curl socat libnghttp2
         usesh: true
         copyback: false
         run: |
@@ -413,7 +413,7 @@ jobs:
 
 
   Solaris:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: DragonFlyBSD
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -433,10 +433,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/solaris-vm@v0
+    - uses: vmactions/solaris-vm@v1
       with:
         envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
         copyback: false
@@ -463,3 +463,54 @@ jobs:
           ./letest.sh
 
 
+  Omnios:
+    runs-on: ubuntu-latest
+    needs: Solaris
+    env:
+      TEST_DNS : ${{ secrets.TEST_DNS }}
+      TestingDomain: ${{ secrets.TestingDomain }}
+      TEST_DNS_NO_WILDCARD: ${{ secrets.TEST_DNS_NO_WILDCARD }}
+      TEST_DNS_NO_SUBDOMAIN: ${{ secrets.TEST_DNS_NO_SUBDOMAIN }}
+      TEST_DNS_SLEEP: ${{ secrets.TEST_DNS_SLEEP }}
+      CASE: le_test_dnsapi
+      TEST_LOCAL: 1
+      DEBUG: ${{ secrets.DEBUG }}
+      http_proxy: ${{ secrets.http_proxy }}
+      https_proxy: ${{ secrets.https_proxy }}
+      HTTPS_INSECURE: 1 # always set to 1 to ignore https error, since Omnios doesn't accept the expired ISRG X1 root
+      TokenName1: ${{ secrets.TokenName1}}
+      TokenName2: ${{ secrets.TokenName2}}
+      TokenName3: ${{ secrets.TokenName3}}
+      TokenName4: ${{ secrets.TokenName4}}
+      TokenName5: ${{ secrets.TokenName5}}
+    steps:
+    - uses: actions/checkout@v4
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/omnios-vm@v1
+      with:
+        envs: 'TEST_DNS TestingDomain TEST_DNS_NO_WILDCARD TEST_DNS_NO_SUBDOMAIN TEST_DNS_SLEEP CASE TEST_LOCAL DEBUG http_proxy https_proxy HTTPS_INSECURE TokenName1 TokenName2 TokenName3 TokenName4 TokenName5 ${{ secrets.TokenName1}} ${{ secrets.TokenName2}} ${{ secrets.TokenName3}} ${{ secrets.TokenName4}} ${{ secrets.TokenName5}}'
+        copyback: false
+        prepare: pkg install socat
+        run: |
+          pkg set-mediator -v -I [email protected] openssl
+          export PATH=/usr/gnu/bin:$PATH
+          if [ "${{ secrets.TokenName1}}" ] ; then
+            export ${{ secrets.TokenName1}}="${{ secrets.TokenValue1}}"
+          fi
+          if [ "${{ secrets.TokenName2}}" ] ; then
+            export ${{ secrets.TokenName2}}="${{ secrets.TokenValue2}}"
+          fi
+          if [ "${{ secrets.TokenName3}}" ] ; then
+            export ${{ secrets.TokenName3}}="${{ secrets.TokenValue3}}"
+          fi
+          if [ "${{ secrets.TokenName4}}" ] ; then
+            export ${{ secrets.TokenName4}}="${{ secrets.TokenValue4}}"
+          fi
+          if [ "${{ secrets.TokenName5}}" ] ; then
+            export ${{ secrets.TokenName5}}="${{ secrets.TokenValue5}}"
+          fi
+          cd ../acmetest
+          ./letest.sh
+
+

+ 71 - 71
.github/workflows/DragonFlyBSD.yml

@@ -1,71 +1,71 @@
-name: DragonFlyBSD
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/DragonFlyBSD.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/DragonFlyBSD.yml'
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-
-
-
-jobs:
-  DragonFlyBSD:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "[email protected]"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-    steps:
-    - uses: actions/checkout@v3
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/dragonflybsd-vm@v0
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
-        copyback: "false"
-        nat: |
-          "8080": "80"
-        prepare: |
-          pkg install -y curl socat libnghttp2
-        usesh: true
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
-
+name: DragonFlyBSD
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/DragonFlyBSD.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/DragonFlyBSD.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+jobs:
+  DragonFlyBSD:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "[email protected]"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/dragonflybsd-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+        nat: |
+          "8080": "80"
+        prepare: |
+          pkg install -y curl socat libnghttp2
+        usesh: true
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

+ 3 - 3
.github/workflows/FreeBSD.yml

@@ -41,7 +41,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "[email protected]"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       with:
@@ -61,7 +61,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/freebsd-vm@v0
+    - uses: vmactions/freebsd-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |

+ 1 - 1
.github/workflows/Linux.yml

@@ -33,7 +33,7 @@ jobs:
       TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
       TEST_ACME_Server: "LetsEncrypt.org_test"
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
       run: |
           cd .. \

+ 1 - 1
.github/workflows/MacOS.yml

@@ -44,7 +44,7 @@ jobs:
       CA_EMAIL: ${{ matrix.CA_EMAIL }}
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run:  brew install socat
     - name: Clone acmetest

+ 71 - 71
.github/workflows/NetBSD.yml

@@ -1,71 +1,71 @@
-name: NetBSD
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/NetBSD.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/NetBSD.yml'
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-
-
-
-jobs:
-  NetBSD:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "[email protected]"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-    steps:
-    - uses: actions/checkout@v3
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/netbsd-vm@v0
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
-        nat: |
-          "8080": "80"
-        prepare: |
-          pkg_add curl socat
-        usesh: true
-        copyback: false
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
-
+name: NetBSD
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/NetBSD.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/NetBSD.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+
+jobs:
+  NetBSD:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "[email protected]"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/netbsd-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN'
+        nat: |
+          "8080": "80"
+        prepare: |
+          /usr/sbin/pkg_add curl socat
+        usesh: true
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

+ 75 - 0
.github/workflows/Omnios.yml

@@ -0,0 +1,75 @@
+name: Omnios
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/Omnios.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/Omnios.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+jobs:
+  Omnios:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+           ACME_USE_WGET: 1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "[email protected]"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/omnios-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+        nat: |
+          "8080": "80"
+        prepare: pkg install socat wget
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

+ 3 - 3
.github/workflows/OpenBSD.yml

@@ -41,7 +41,7 @@ jobs:
          #  CA: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "[email protected]"
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       with:
@@ -61,7 +61,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/openbsd-vm@v0
+    - uses: vmactions/openbsd-vm@v1
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |

+ 2 - 2
.github/workflows/PebbleStrict.yml

@@ -33,7 +33,7 @@ jobs:
       TEST_CA: "Pebble Intermediate CA"
 
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat
     - name: Run Pebble
@@ -58,7 +58,7 @@ jobs:
       TEST_IPCERT: 1
 
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat
     - name: Run Pebble

+ 75 - 74
.github/workflows/Solaris.yml

@@ -1,74 +1,75 @@
-name: Solaris
-on:
-  push:
-    branches:
-      - '*'
-    paths:
-      - '*.sh'
-      - '.github/workflows/Solaris.yml'
-
-  pull_request:
-    branches:
-      - dev
-    paths:
-      - '*.sh'
-      - '.github/workflows/Solaris.yml'
-
-
-
-concurrency: 
-  group: ${{ github.workflow }}-${{ github.ref }}
-  cancel-in-progress: true
-
-jobs:
-  Solaris:
-    strategy:
-      matrix:
-        include:
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-         - TEST_ACME_Server: "LetsEncrypt.org_test"
-           CA_ECDSA: ""
-           CA: ""
-           CA_EMAIL: ""
-           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
-           ACME_USE_WGET: 1
-         #- TEST_ACME_Server: "ZeroSSL.com"
-         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
-         #  CA: "ZeroSSL RSA Domain Secure Site CA"
-         #  CA_EMAIL: "[email protected]"
-         #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
-    env:
-      TEST_LOCAL: 1
-      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
-      CA_ECDSA: ${{ matrix.CA_ECDSA }}
-      CA: ${{ matrix.CA }}
-      CA_EMAIL: ${{ matrix.CA_EMAIL }}
-      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
-      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
-    steps:
-    - uses: actions/checkout@v3
-    - uses: vmactions/cf-tunnel@v0
-      id: tunnel
-      with:
-        protocol: http
-        port: 8080
-    - name: Set envs
-      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
-    - name: Clone acmetest
-      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
-    - uses: vmactions/solaris-vm@v0
-      with:
-        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
-        copyback: "false"
-        nat: |
-          "8080": "80"
-        prepare: pkgutil -y -i socat curl wget
-        run: |
-          cd ../acmetest \
-          && ./letest.sh
-
+name: Solaris
+on:
+  push:
+    branches:
+      - '*'
+    paths:
+      - '*.sh'
+      - '.github/workflows/Solaris.yml'
+
+  pull_request:
+    branches:
+      - dev
+    paths:
+      - '*.sh'
+      - '.github/workflows/Solaris.yml'
+
+concurrency: 
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+
+
+jobs:
+  Solaris:
+    strategy:
+      matrix:
+        include:
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+         - TEST_ACME_Server: "LetsEncrypt.org_test"
+           CA_ECDSA: ""
+           CA: ""
+           CA_EMAIL: ""
+           TEST_PREFERRED_CHAIN: (STAGING) Pretend Pear X1
+           ACME_USE_WGET: 1
+         #- TEST_ACME_Server: "ZeroSSL.com"
+         #  CA_ECDSA: "ZeroSSL ECC Domain Secure Site CA"
+         #  CA: "ZeroSSL RSA Domain Secure Site CA"
+         #  CA_EMAIL: "[email protected]"
+         #  TEST_PREFERRED_CHAIN: ""
+    runs-on: ubuntu-latest
+    env:
+      TEST_LOCAL: 1
+      TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
+      CA_ECDSA: ${{ matrix.CA_ECDSA }}
+      CA: ${{ matrix.CA }}
+      CA_EMAIL: ${{ matrix.CA_EMAIL }}
+      TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
+      ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: vmactions/cf-tunnel@v0
+      id: tunnel
+      with:
+        protocol: http
+        port: 8080
+    - name: Set envs
+      run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
+    - name: Clone acmetest
+      run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
+    - uses: vmactions/solaris-vm@v1
+      with:
+        envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
+        nat: |
+          "8080": "80"
+        prepare: pkgutil -y -i socat curl wget
+        copyback: false
+        run: |
+          cd ../acmetest \
+          && ./letest.sh
+
+

+ 1 - 1
.github/workflows/Ubuntu.yml

@@ -70,7 +70,7 @@ jobs:
       TestingDomain: ${{ matrix.TestingDomain }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
       run: sudo apt-get install -y socat wget
     - name: Start StepCA

+ 1 - 1
.github/workflows/Windows.yml

@@ -49,7 +49,7 @@ jobs:
     - name: Set git to use LF
       run: |
           git config --global core.autocrlf false
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install cygwin base packages with chocolatey
       run: |
           choco config get cacheLocation

+ 1 - 1
.github/workflows/dockerhub.yml

@@ -41,7 +41,7 @@ jobs:
     if: "contains(needs.CheckToken.outputs.hasToken, 'true')"
     steps:
       - name: checkout code
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Set up QEMU
         uses: docker/setup-qemu-action@v2
       - name: Set up Docker Buildx

+ 2 - 1
.github/workflows/pr_dns.yml

@@ -22,9 +22,10 @@ jobs:
               owner: context.repo.owner,
               repo: context.repo.repo,
               body: `**Welcome**
-                Please make sure you're read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
+                Please make sure you've read our [DNS API Dev Guide](../wiki/DNS-API-Dev-Guide) and [DNS-API-Test](../wiki/DNS-API-Test).
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
+                注意: 必须通过了 [DNS-API-Test](../wiki/DNS-API-Test) 才会被 review. 无论是修改, 还是新加的 dns api, 都必须确保通过这个测试.
                 `
             })
 

+ 1 - 1
.github/workflows/pr_notify.yml

@@ -22,7 +22,7 @@ jobs:
               owner: context.repo.owner,
               repo: context.repo.repo,
               body: `**Welcome**
-                Please make sure you're read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
+                Please make sure you've read our [Code-of-conduct](../wiki/Code-of-conduct) and  add the usage here: [notify](../wiki/notify).
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
                 `

+ 2 - 2
.github/workflows/shellcheck.yml

@@ -22,7 +22,7 @@ jobs:
   ShellCheck:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install Shellcheck
       run: sudo apt-get install -y shellcheck
     - name: DoShellcheck
@@ -31,7 +31,7 @@ jobs:
   shfmt:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install shfmt
       run: curl -sSL https://github.com/mvdan/sh/releases/download/v3.1.2/shfmt_v3.1.2_linux_amd64 -o ~/shfmt && chmod +x ~/shfmt
     - name: shfmt

+ 16 - 19
README.md

@@ -8,7 +8,7 @@
 [![Windows](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Windows.yml)
 [![Solaris](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Solaris.yml)
 [![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)
-
+[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)
 
 ![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
 ![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/badge.svg)
@@ -73,20 +73,21 @@ Twitter: [@neilpangxa](https://twitter.com/neilpangxa)
 |7|[![OpenBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/OpenBSD.yml)|OpenBSD
 |8|[![NetBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/NetBSD.yml)|NetBSD
 |9|[![DragonFlyBSD](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/DragonFlyBSD.yml)|DragonFlyBSD
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
-|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
-|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
-|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
-|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
-|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
-|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
-|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
-|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
-|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
-|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
-|11|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
-|22|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
-|23|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
+|10|[![Omnios](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Omnios.yml)|Omnios
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)| Debian
+|12|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|CentOS
+|13|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|openSUSE
+|14|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Alpine Linux (with curl)
+|15|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Archlinux
+|16|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|fedora
+|17|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Kali Linux
+|18|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Oracle Linux
+|19|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Mageia
+|10|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|Gentoo Linux
+|11|[![Linux](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml/badge.svg)](https://github.com/acmesh-official/acme.sh/actions/workflows/Linux.yml)|ClearLinux
+|22|-----| Cloud Linux  https://github.com/acmesh-official/acme.sh/issues/111
+|23|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/acmesh-official/acme.sh/wiki/How-to-run-on-OpenWRT)
+|24|[![](https://acmesh-official.github.io/acmetest/status/proxmox.svg)](https://github.com/acmesh-official/letest#here-are-the-latest-status)| Proxmox: See Proxmox VE Wiki. Version [4.x, 5.0, 5.1](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x,_5.0_and_5.1)#Let.27s_Encrypt_using_acme.sh), version [5.2 and up](https://pve.proxmox.com/wiki/Certificate_Management)
 
 
 Check our [testing project](https://github.com/acmesh-official/acmetest):
@@ -506,10 +507,6 @@ Support this project with your organization. Your logo will show up here with a
 <a href="https://opencollective.com/acmesh/organization/9/website"><img src="https://opencollective.com/acmesh/organization/9/avatar.svg"></a>
 
 
-#### Sponsors
-
-[![quantumca-acmesh-logo](https://user-images.githubusercontent.com/8305679/183255712-634ee1db-bb61-4c03-bca0-bacce99e078c.svg)](https://www.quantumca.com.cn/?__utm_source=acmesh-donation)
-
 
 # 19. License & Others
 

+ 27 - 28
acme.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env sh
 
-VER=3.0.7
+VER=3.0.8
 
 PROJECT_NAME="acme.sh"
 
@@ -102,12 +102,12 @@ ECC_SUFFIX="${ECC_SEP}ecc"
 LOG_LEVEL_1=1
 LOG_LEVEL_2=2
 LOG_LEVEL_3=3
-DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
+DEFAULT_LOG_LEVEL="$LOG_LEVEL_2"
 
 DEBUG_LEVEL_1=1
 DEBUG_LEVEL_2=2
 DEBUG_LEVEL_3=3
-DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
+DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_2
 DEBUG_LEVEL_NONE=0
 
 DOH_CLOUDFLARE=1
@@ -931,7 +931,7 @@ fi
 
 _egrep_o() {
   if [ "$__USE_EGREP" ]; then
-    egrep -o "$1"
+    egrep -o -- "$1" 2>/dev/null
   else
     sed -n 's/.*\('"$1"'\).*/\1/p'
   fi
@@ -1561,7 +1561,7 @@ createDomainKey() {
 createCSR() {
   _info "Creating csr"
   if [ -z "$1" ]; then
-    _usage "Usage: $PROJECT_ENTRY --create-csr --domain <domain.tld> [--domain <domain2.tld> ...]"
+    _usage "Usage: $PROJECT_ENTRY --create-csr --domain <domain.tld> [--domain <domain2.tld> ...] [--ecc]"
     return
   fi
 
@@ -1795,6 +1795,10 @@ _date2time() {
   if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
     return
   fi
+  #Omnios
+  if da="$(echo "$1" | tr -d "Z" | tr "T" ' ')" perl -MTime::Piece -e 'print Time::Piece->strptime($ENV{da}, "%Y-%m-%d %H:%M:%S")->epoch, "\n";' 2>/dev/null; then
+    return
+  fi
   _err "Can not parse _date2time $1"
   return 1
 }
@@ -2118,12 +2122,7 @@ _tail_n() {
 }
 
 _tail_c() {
-  if _is_solaris; then
-    #fix for solaris
-    tail -"$1"c
-  else
-    tail -c "$1"
-  fi
+  tail -c "$1" 2>/dev/null || tail -"$1"c
 }
 
 # url  payload needbase64  keyfile
@@ -3130,7 +3129,7 @@ _setNginx() {
         _err "nginx command is not found."
         return 1
       fi
-      NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "--conf-path=[^ ]* " | tr -d " ")"
+      NGINX_CONF="$(nginx -V 2>&1 | _egrep_o "\-\-conf-path=[^ ]* " | tr -d " ")"
       _debug NGINX_CONF "$NGINX_CONF"
       NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
       _debug NGINX_CONF "$NGINX_CONF"
@@ -5015,9 +5014,9 @@ $_authorizations_map"
         errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
         _debug2 errordetail "$errordetail"
         if [ "$errordetail" ]; then
-          _err "$d:Verify error:$errordetail"
+          _err "Invalid status, $d:Verify error detail:$errordetail"
         else
-          _err "$d:Verify error:$error"
+          _err "Invalid status, $d:Verify error:$error"
         fi
         if [ "$DEBUG" ]; then
           if [ "$vtype" = "$VTYPE_HTTP" ]; then
@@ -5044,7 +5043,7 @@ $_authorizations_map"
       elif _contains "$status" "processing"; then
         _info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
       else
-        _err "$d:Verify error:$response"
+        _err "Unknown status: $status, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _on_issue_err "$_post_hook" "$vlist"
@@ -5057,7 +5056,7 @@ $_authorizations_map"
       _send_signed_request "$_authz_url"
 
       if [ "$?" != "0" ]; then
-        _err "$d:Verify error:$response"
+        _err "Invalid code, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _on_issue_err "$_post_hook" "$vlist"
@@ -5993,6 +5992,7 @@ installcronjob() {
   fi
   _t=$(_time)
   random_minute=$(_math $_t % 60)
+  random_hour=$(_math $_t / 60 % 24)
 
   if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
     _CRONTAB="fcrontab"
@@ -6017,16 +6017,14 @@ installcronjob() {
   _info "Installing cron job"
   if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
     if _exists uname && uname -a | grep SunOS >/dev/null; then
-      $_CRONTAB -l | {
-        cat
-        echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
-      } | $_CRONTAB --
+      _CRONTAB_STDIN="$_CRONTAB --"
     else
-      $_CRONTAB -l | {
-        cat
-        echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
-      } | $_CRONTAB -
+      _CRONTAB_STDIN="$_CRONTAB -"
     fi
+    $_CRONTAB -l | {
+      cat
+      echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+    } | $_CRONTAB_STDIN
   fi
   if [ "$?" != "0" ]; then
     _err "Install cron job failed. You need to manually renew your certs."
@@ -6899,7 +6897,7 @@ Parameters:
 
   -f, --force                       Force install, force cert renewal or override sudo restrictions.
   --staging, --test                 Use staging server, for testing.
-  --debug [0|1|2|3]                 Output debug info. Defaults to 1 if argument is omitted.
+  --debug [0|1|2|3]                 Output debug info. Defaults to $DEBUG_LEVEL_DEFAULT if argument is omitted.
   --output-insecure                 Output all the sensitive messages.
                                       By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
   -w, --webroot <directory>         Specifies the web root folder for web root mode.
@@ -6917,7 +6915,7 @@ Parameters:
   -k, --keylength <bits>            Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384, ec-521.
   -ak, --accountkeylength <bits>    Specifies the account key length: 2048, 3072, 4096
   --log [file]                      Specifies the log file. Defaults to \"$DEFAULT_LOG_FILE\" if argument is omitted.
-  --log-level <1|2>                 Specifies the log level, default is 1.
+  --log-level <1|2>                 Specifies the log level, default is $DEFAULT_LOG_LEVEL.
   --syslog <0|3|6|7>                Syslog level, 0: disable syslog, 3: error, 6: info, 7: debug.
   --eab-kid <eab_key_id>            Key Identifier for External Account Binding.
   --eab-hmac-key <eab_hmac_key>     HMAC key for External Account Binding.
@@ -6925,7 +6923,7 @@ Parameters:
 
   These parameters are to install the cert to nginx/apache or any other server after issue/renew a cert:
 
-  --cert-file <file>                Path to copy the cert file to after issue/renew..
+  --cert-file <file>                Path to copy the cert file to after issue/renew.
   --key-file <file>                 Path to copy the key file to after issue/renew.
   --ca-file <file>                  Path to copy the intermediate cert file to after issue/renew.
   --fullchain-file <file>           Path to copy the fullchain cert file to after issue/renew.
@@ -6955,7 +6953,8 @@ Parameters:
   --no-profile                      Only valid for '--install' command, which means: do not install aliases to user profile.
   --no-color                        Do not output color text.
   --force-color                     Force output of color text. Useful for non-interactive use with the aha tool for HTML E-Mails.
-  --ecc                             Specifies to use the ECC cert. Valid for '--install-cert', '--renew', '--revoke', '--to-pkcs12' and '--create-csr'
+  --ecc                             Specifies use of the ECC cert. Only valid for '--install-cert', '--renew', '--remove ', '--revoke',
+                                      '--deploy', '--to-pkcs8', '--to-pkcs12' and '--create-csr'.
   --csr <file>                      Specifies the input csr.
   --pre-hook <command>              Command to be run before obtaining any certificates.
   --post-hook <command>             Command to be run after attempting to obtain/renew certificates. Runs regardless of whether obtain/renew succeeded or failed.

+ 1 - 1
deploy/haproxy.sh

@@ -147,7 +147,7 @@ haproxy_deploy() {
   # Create a temporary PEM file
   _temppem="$(_mktemp)"
   _debug _temppem "${_temppem}"
-  cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
+  cat "${_ccert}" "${_cca}" "${_ckey}" >"${_temppem}"
   _ret="$?"
 
   # Check that we could create the temporary file

+ 117 - 41
deploy/panos.sh

@@ -7,11 +7,15 @@
 #
 # Firewall admin with superuser and IP address is required.
 #
-# export PANOS_USER=""  # required
-# export PANOS_PASS=""  # required
-# export PANOS_HOST=""  # required
+# REQURED:
+#     export PANOS_HOST=""
+#     export PANOS_USER=""    #User *MUST* have Commit and Import Permissions in XML API for Admin Role
+#     export PANOS_PASS=""
+#
+# The script will automatically generate a new API key if
+# no key is found, or if a saved key has expired or is invalid.
 
-# This function is to parse the XML
+# This function is to parse the XML response from the firewall
 parse_response() {
   type=$2
   if [ "$type" = 'keygen' ]; then
@@ -23,25 +27,46 @@ parse_response() {
       message="PAN-OS Key could not be set."
     fi
   else
-    status=$(echo "$1" | sed 's/^.*"\([a-z]*\)".*/\1/g')
-    message=$(echo "$1" | sed 's/^.*<result>\(.*\)<\/result.*/\1/g')
+    status=$(echo "$1" | tr -d '\n' | sed 's/^.*"\([a-z]*\)".*/\1/g')
+    message=$(echo "$1" | tr -d '\n' | sed 's/.*\(<result>\|<msg>\|<line>\)\([^<]*\).*/\2/g')
+    _debug "Firewall message:  $message"
+    if [ "$type" = 'keytest' ] && [ "$status" != "success" ]; then
+      _debug "****  API Key has EXPIRED or is INVALID ****"
+      unset _panos_key
+    fi
   fi
   return 0
 }
 
+#This function is used to deploy to the firewall
 deployer() {
   content=""
-  type=$1 # Types are keygen, cert, key, commit
-  _debug "**** Deploying $type *****"
+  type=$1 # Types are keytest, keygen, cert, key, commit
   panos_url="https://$_panos_host/api/"
+
+  #Test API Key by performing a lookup
+  if [ "$type" = 'keytest' ]; then
+    _debug "**** Testing saved API Key ****"
+    _H1="Content-Type: application/x-www-form-urlencoded"
+    # Get Version Info to test key
+    content="type=version&key=$_panos_key"
+    ## Exclude all scopes for the empty commit
+    #_exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network><shared-object>exclude</shared-object>"
+    #content="type=commit&action=partial&key=$_panos_key&cmd=<commit><partial>$_exclude_scope<admin><member>acmekeytest</member></admin></partial></commit>"
+  fi
+
+  # Generate API Key
   if [ "$type" = 'keygen' ]; then
+    _debug "**** Generating new API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=keygen&user=$_panos_user&password=$_panos_pass"
     # content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
   fi
 
+  # Deploy Cert or Key
   if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
-    #Generate DEIM
+    _debug "**** Deploying $type ****"
+    #Generate DELIM
     delim="-----MultipartDelimiter$(date "+%s%N")"
     nl="\015\012"
     #Set Header
@@ -61,7 +86,7 @@ deployer() {
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"key\"\r\n\r\n$_panos_key"
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"format\"\r\n\r\npem"
       content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"passphrase\"\r\n\r\n123456"
-      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_ckey")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
+      content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"file\"; filename=\"$(basename "$_cdomain.key")\"${nl}Content-Type: application/octet-stream${nl}${nl}$(cat "$_ckey")"
     fi
     #Close multipart
     content="$content${nl}--$delim--${nl}${nl}"
@@ -69,16 +94,25 @@ deployer() {
     content=$(printf %b "$content")
   fi
 
+  # Commit changes
   if [ "$type" = 'commit' ]; then
+    _debug "**** Committing changes ****"
     export _H1="Content-Type: application/x-www-form-urlencoded"
-    cmd=$(printf "%s" "<commit><partial><$_panos_user></$_panos_user></partial></commit>" | _url_encode)
-    content="type=commit&key=$_panos_key&cmd=$cmd"
+    #Check for force commit - will commit ALL uncommited changes to the firewall. Use with caution!
+    if [ "$FORCE" ]; then
+      _debug "Force switch detected.  Committing ALL changes to the firewall."
+      cmd=$(printf "%s" "<commit><partial><force><admin><member>$_panos_user</member></admin></force></partial></commit>" | _url_encode)
+    else
+      _exclude_scope="<policy-and-objects>exclude</policy-and-objects><device-and-network>exclude</device-and-network>"
+      cmd=$(printf "%s" "<commit><partial>$_exclude_scope<admin><member>$_panos_user</member></admin></partial></commit>" | _url_encode)
+    fi
+    content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
   fi
+
   response=$(_post "$content" "$panos_url" "" "POST")
   parse_response "$response" "$type"
   # Saving response to variables
   response_status=$status
-  #DEBUG
   _debug response_status "$response_status"
   if [ "$response_status" = "success" ]; then
     _debug "Successfully deployed $type"
@@ -92,43 +126,85 @@ deployer() {
 
 # This is the main function that will call the other functions to deploy everything.
 panos_deploy() {
-  _cdomain="$1"
+  _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _cfullchain="$5"
-  # PANOS ENV VAR check
-  if [ -z "$PANOS_USER" ] || [ -z "$PANOS_PASS" ] || [ -z "$PANOS_HOST" ]; then
-    _debug "No ENV variables found lets check for saved variables"
-    _getdeployconf PANOS_USER
-    _getdeployconf PANOS_PASS
-    _getdeployconf PANOS_HOST
-    _panos_user=$PANOS_USER
-    _panos_pass=$PANOS_PASS
-    _panos_host=$PANOS_HOST
-    if [ -z "$_panos_user" ] && [ -z "$_panos_pass" ] && [ -z "$_panos_host" ]; then
-      _err "No host, user and pass found.. If this is the first time deploying please set PANOS_HOST, PANOS_USER and PANOS_PASS in environment variables. Delete them after you have succesfully deployed certs."
-      return 1
-    else
-      _debug "Using saved env variables."
-    fi
+
+  # VALID FILE CHECK
+  if [ ! -f "$_ckey" ] || [ ! -f "$_cfullchain" ]; then
+    _err "Unable to find a valid key and/or cert.  If this is an ECDSA/ECC cert, use the --ecc flag when deploying."
+    return 1
+  fi
+
+  # PANOS_HOST
+  if [ "$PANOS_HOST" ]; then
+    _debug "Detected ENV variable PANOS_HOST. Saving to file."
+    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
   else
-    _debug "Detected ENV variables to be saved to the deploy conf."
-    # Encrypt and save user
+    _debug "Attempting to load variable PANOS_HOST from file."
+    _getdeployconf PANOS_HOST
+  fi
+
+  # PANOS USER
+  if [ "$PANOS_USER" ]; then
+    _debug "Detected ENV variable PANOS_USER. Saving to file."
     _savedeployconf PANOS_USER "$PANOS_USER" 1
+  else
+    _debug "Attempting to load variable PANOS_USER from file."
+    _getdeployconf PANOS_USER
+  fi
+
+  # PANOS_PASS
+  if [ "$PANOS_PASS" ]; then
+    _debug "Detected ENV variable PANOS_PASS. Saving to file."
     _savedeployconf PANOS_PASS "$PANOS_PASS" 1
-    _savedeployconf PANOS_HOST "$PANOS_HOST" 1
-    _panos_user="$PANOS_USER"
-    _panos_pass="$PANOS_PASS"
-    _panos_host="$PANOS_HOST"
+  else
+    _debug "Attempting to load variable PANOS_PASS from file."
+    _getdeployconf PANOS_PASS
   fi
-  _debug "Let's use username and pass to generate token."
-  if [ -z "$_panos_user" ] || [ -z "$_panos_pass" ] || [ -z "$_panos_host" ]; then
-    _err "Please pass username and password and host as env variables PANOS_USER, PANOS_PASS and PANOS_HOST"
+
+  # PANOS_KEY
+  _getdeployconf PANOS_KEY
+  if [ "$PANOS_KEY" ]; then
+    _debug "Detected saved key."
+    _panos_key=$PANOS_KEY
+  else
+    _debug "No key detected"
+    unset _panos_key
+  fi
+
+  #Store variables
+  _panos_host=$PANOS_HOST
+  _panos_user=$PANOS_USER
+  _panos_pass=$PANOS_PASS
+
+  #Test API Key if found.  If the key is invalid, the variable _panos_key will be unset.
+  if [ "$_panos_host" ] && [ "$_panos_key" ]; then
+    _debug "**** Testing API KEY ****"
+    deployer keytest
+  fi
+
+  # Check for valid variables
+  if [ -z "$_panos_host" ]; then
+    _err "No host found. If this is your first time deploying, please set PANOS_HOST in ENV variables. You can delete it after you have successfully deployed the certs."
+    return 1
+  elif [ -z "$_panos_user" ]; then
+    _err "No user found. If this is your first time deploying, please set PANOS_USER in ENV variables. You can delete it after you have successfully deployed the certs."
+    return 1
+  elif [ -z "$_panos_pass" ]; then
+    _err "No password found. If this is your first time deploying, please set PANOS_PASS in ENV variables. You can delete it after you have successfully deployed the certs."
     return 1
   else
-    _debug "Getting PANOS KEY"
-    deployer keygen
+    # Generate a new API key if no valid API key is found
+    if [ -z "$_panos_key" ]; then
+      _debug "**** Generating new PANOS API KEY ****"
+      deployer keygen
+      _savedeployconf PANOS_KEY "$_panos_key" 1
+    fi
+
+    # Confirm that a valid key was generated
     if [ -z "$_panos_key" ]; then
-      _err "Missing apikey."
+      _err "Unable to generate an API key.  The user and pass may be invalid or not authorized to generate a new key.  Please check the PANOS_USER and PANOS_PASS credentials and try again"
       return 1
     else
       deployer cert

+ 4 - 4
deploy/proxmoxve.sh

@@ -99,11 +99,11 @@ proxmoxve_deploy() {
     _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
     _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
   fi
-  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY _proxmoxve_api_token_key
+  _debug2 DEPLOY_PROXMOXVE_API_TOKEN_KEY "$_proxmoxve_api_token_key"
 
   # PVE API Token header value. Used in "Authorization: PVEAPIToken".
   _proxmoxve_header_api_token="${_proxmoxve_user}@${_proxmoxve_user_realm}!${_proxmoxve_api_token_name}=${_proxmoxve_api_token_key}"
-  _debug2 "Auth Header" _proxmoxve_header_api_token
+  _debug2 "Auth Header" "$_proxmoxve_header_api_token"
 
   # Ugly. I hate putting heredocs inside functions because heredocs don't
   # account for whitespace correctly but it _does_ work and is several times
@@ -124,8 +124,8 @@ HEREDOC
   )
   _debug2 Payload "$_json_payload"
 
-  # Push certificates to server.
-  export _HTTPS_INSECURE=1
+  _info "Push certificates to server"
+  export HTTPS_INSECURE=1
   export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
   _post "$_json_payload" "$_target_url" "" POST "application/json"
 

+ 9 - 4
deploy/synology_dsm.sh

@@ -91,8 +91,10 @@ synology_dsm_deploy() {
 
   _debug "Getting API version"
   response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
+  api_path=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"path" *: *"\([^"]*\)".*/\1/p')
   api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
   _debug3 response "$response"
+  _debug3 api_path "$api_path"
   _debug3 api_version "$api_version"
 
   # Login, get the session ID & SynoToken from JSON
@@ -133,12 +135,15 @@ synology_dsm_deploy() {
       [ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal"
     fi
 
-    response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
-    _debug3 response "$response"
-    SYNO_Device_ID=$(echo "$response" | grep "device_id" | sed -n 's/.*"device_id" *: *"\([^"]*\).*/\1/p')
+    response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&otp_code=$otp_code&enable_syno_token=yes&enable_device_token=yes&device_name=$SYNO_Device_Name")
+    _secure_debug3 response "$response"
+
+    id_property='device_id'
+    [ "${api_version}" -gt '6' ] || id_property='did'
+    SYNO_Device_ID=$(echo "$response" | grep "$id_property" | sed -n 's/.*"'$id_property'" *: *"\([^"]*\).*/\1/p')
     _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
   else
-    response=$(_get "$_base_url/webapi/entry.cgi?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")
+    response=$(_get "$_base_url/webapi/$api_path?api=SYNO.API.Auth&version=$api_version&method=login&format=sid&account=$encoded_username&passwd=$encoded_password&enable_syno_token=yes&device_name=$SYNO_Device_Name&device_id=$SYNO_Device_ID")
     _debug3 response "$response"
   fi
 

+ 2 - 2
dnsapi/dns_1984hosting.sh

@@ -128,7 +128,7 @@ _1984hosting_login() {
   _debug "Login to 1984Hosting as user $One984HOSTING_Username."
   username=$(printf '%s' "$One984HOSTING_Username" | _url_encode)
   password=$(printf '%s' "$One984HOSTING_Password" | _url_encode)
-  url="https://1984.hosting/accounts/checkuserauth/"
+  url="https://1984.hosting/api/auth/"
 
   _get "https://1984.hosting/accounts/login/" | grep "csrfmiddlewaretoken"
   csrftoken="$(grep -i '^set-cookie:' "$HTTP_HEADER" | _egrep_o 'csrftoken=[^;]*;' | tr -d ';')"
@@ -185,7 +185,7 @@ _check_cookies() {
     return 1
   fi
 
-  _authget "https://1984.hosting/accounts/loginstatus/"
+  _authget "https://1984.hosting/api/auth/"
   if _contains "$_response" '"ok": true'; then
     _debug "Cached cookies still valid."
     return 0

+ 1 - 1
dnsapi/dns_ali.sh

@@ -117,7 +117,7 @@ _ali_urlencode() {
 _ali_nonce() {
   #_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
   #Not so good...
-  date +"%s%N"
+  date +"%s%N" | sed 's/%N//g'
 }
 
 _check_exist_query() {

+ 180 - 0
dnsapi/dns_artfiles.sh

@@ -0,0 +1,180 @@
+#!/usr/bin/env sh
+
+################################################################################
+# ACME.sh 3rd party DNS API plugin for ArtFiles.de
+################################################################################
+# Author:   Martin Arndt, https://troublezone.net/
+# Released: 2022-02-27
+# Issues:   https://github.com/acmesh-official/acme.sh/issues/4718
+################################################################################
+# Usage:
+# 1. export AF_API_USERNAME='api12345678'
+# 2. export AF_API_PASSWORD='apiPassword'
+# 3. acme.sh --issue -d example.com --dns dns_artfiles
+################################################################################
+
+########## API configuration ###################################################
+
+AF_API_SUCCESS='status":"OK'
+AF_URL_DCP='https://dcp.c.artfiles.de/api/'
+AF_URL_DNS=${AF_URL_DCP}'dns/{*}_dns.html?domain='
+AF_URL_DOMAINS=${AF_URL_DCP}'domain/get_domains.html'
+
+########## Public functions ####################################################
+
+# Adds a new TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_add _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_add() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS addition API…'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  _set_credentials
+  _saveaccountconf_mutable 'AF_API_USERNAME' "$AF_API_USERNAME"
+  _saveaccountconf_mutable 'AF_API_PASSWORD' "$AF_API_PASSWORD"
+
+  _set_headers
+  _get_zone "$domain"
+  _dns 'GET'
+  if ! _contains "$response" 'TXT'; then
+    _err 'Retrieving TXT records failed.'
+
+    return 1
+  fi
+
+  _clean_records
+  _dns 'SET' "$(printf -- '%s\n_acme-challenge "%s"' "$response" "$txtValue")"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Adding ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+# Removes the existing TXT record for given ACME challenge value & domain.
+# Usage: dns_artfiles_rm _acme-challenge.www.example.com "ACME challenge value"
+dns_artfiles_rm() {
+  domain="$1"
+  txtValue="$2"
+  _info 'Using ArtFiles.de DNS removal API…'
+  _debug 'Domain' "$domain"
+  _debug 'txtValue' "$txtValue"
+
+  _set_credentials
+  _set_headers
+  _get_zone "$domain"
+  if ! _dns 'GET'; then
+    return 1
+  fi
+
+  if ! _contains "$response" "$txtValue"; then
+    _err 'Retrieved TXT records are missing given ACME challenge value.'
+
+    return 1
+  fi
+
+  _clean_records
+  response="$(printf -- '%s' "$response" | sed '/_acme-challenge "'"$txtValue"'"/d')"
+  _dns 'SET' "$response"
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err 'Removing ACME challenge value failed.'
+
+    return 1
+  fi
+}
+
+########## Private functions ###################################################
+
+# Cleans awful TXT records response of ArtFiles's API & pretty prints it.
+# Usage: _clean_records
+_clean_records() {
+  _info 'Cleaning TXT records…'
+  # Extract TXT part, strip trailing quote sign (ACME.sh API guidelines forbid
+  # usage of SED's GNU extensions, hence couldn't omit it via regex), strip '\'
+  # from '\"' & turn '\n' into real LF characters.
+  # Yup, awful API to use - but that's all we got to get this working, so… ;)
+  _debug2 'Raw  ' "$response"
+  response="$(printf -- '%s' "$response" | sed 's/^.*TXT":"\([^}]*\).*$/\1/;s/,".*$//;s/.$//;s/\\"/"/g;s/\\n/\n/g')"
+  _debug2 'Clean' "$response"
+}
+
+# Executes an HTTP GET or POST request for getting or setting DNS records,
+# containing given payload upon POST.
+# Usage: _dns [GET | SET] [payload]
+_dns() {
+  _info 'Executing HTTP request…'
+  action="$1"
+  payload="$(printf -- '%s' "$2" | _url_encode)"
+  url="$(printf -- '%s%s' "$AF_URL_DNS" "$domain" | sed 's/{\*}/'"$(printf -- '%s' "$action" | _lower_case)"'/')"
+
+  if [ "$action" = 'SET' ]; then
+    _debug2 'Payload' "$payload"
+    response="$(_post '' "$url&TXT=$payload" '' 'POST' 'application/x-www-form-urlencoded')"
+  else
+    response="$(_get "$url" '' 10)"
+  fi
+
+  if ! _contains "$response" "$AF_API_SUCCESS"; then
+    _err "DNS API error: $response"
+
+    return 1
+  fi
+
+  _debug 'Response' "$response"
+
+  return 0
+}
+
+# Gets the root domain zone for given domain.
+# Usage: _get_zone _acme-challenge.www.example.com
+_get_zone() {
+  fqdn="$1"
+  domains="$(_get "$AF_URL_DOMAINS" '' 10)"
+  _info 'Getting domain zone…'
+  _debug2 'FQDN' "$fqdn"
+  _debug2 'Domains' "$domains"
+
+  while _contains "$fqdn" "."; do
+    if _contains "$domains" "$fqdn"; then
+      domain="$fqdn"
+      _info "Found root domain zone: $domain"
+      break
+    else
+      fqdn="${fqdn#*.}"
+      _debug2 'FQDN' "$fqdn"
+    fi
+  done
+
+  if [ "$domain" = "$fqdn" ]; then
+    return 0
+  fi
+
+  _err 'Couldn'\''t find root domain zone.'
+
+  return 1
+}
+
+# Sets the credentials for accessing ArtFiles's API
+# Usage: _set_credentials
+_set_credentials() {
+  _info 'Setting credentials…'
+  AF_API_USERNAME="${AF_API_USERNAME:-$(_readaccountconf_mutable AF_API_USERNAME)}"
+  AF_API_PASSWORD="${AF_API_PASSWORD:-$(_readaccountconf_mutable AF_API_PASSWORD)}"
+  if [ -z "$AF_API_USERNAME" ] || [ -z "$AF_API_PASSWORD" ]; then
+    _err 'Missing ArtFiles.de username and/or password.'
+    _err 'Please ensure both are set via export command & try again.'
+
+    return 1
+  fi
+}
+
+# Adds the HTTP Authorization & Content-Type headers to a follow-up request.
+# Usage: _set_headers
+_set_headers() {
+  _info 'Setting headers…'
+  encoded="$(printf -- '%s:%s' "$AF_API_USERNAME" "$AF_API_PASSWORD" | _base64)"
+  export _H1="Authorization: Basic $encoded"
+  export _H2='Content-Type: application/json'
+}

+ 1 - 1
dnsapi/dns_aws.sh

@@ -157,7 +157,7 @@ _get_root() {
 
   # iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
   while true; do
-    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100 | sed 's/\./\\./g')
     _debug "Checking domain: $h"
     if [ -z "$h" ]; then
       _error "invalid domain"

+ 185 - 0
dnsapi/dns_dnsexit.sh

@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#use dns-01 at DNSExit.com
+
+#Author: Samuel Jimenez
+#Report Bugs here: https://github.com/acmesh-official/acme.sh
+
+#DNSEXIT_API_KEY=ABCDEFGHIJ0123456789abcdefghij
+#[email protected]
+#DNSEXIT_AUTH_PASS=aStrongPassword
+DNSEXIT_API_URL="https://api.dnsexit.com/dns/"
+DNSEXIT_HOSTS_URL="https://update.dnsexit.com/ipupdate/hosts.jsp"
+
+########  Public functions #####################
+#Usage: dns_dnsexit_add   _acme-challenge.*.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dnsexit_add() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using DNSExit.com"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  _debug 'Load account auth'
+  if ! get_account_info; then
+    return 1
+  fi
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"add\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\",\"ttl\":0,\"overwrite\":false}}"; then
+    _err "$response"
+    return 1
+  fi
+
+  _debug2 _response "$response"
+  return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_dnsexit_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  _info "Using DNSExit.com"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  _debug 'Load account auth'
+  if ! get_account_info; then
+    return 1
+  fi
+
+  _debug 'First detect the root zone'
+  if ! _get_root "$fulldomain"; then
+    _err "$response"
+    return 1
+  fi
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  if ! _dnsexit_rest "{\"domain\":\"$_domain\",\"delete\":{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"content\":\"$txtvalue\"}}"; then
+    _err "$response"
+    return 1
+  fi
+
+  _debug2 _response "$response"
+  return 0
+}
+
+####################  Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+  domain=$1
+  i=1
+  while true; do
+    _domain=$(printf "%s" "$domain" | cut -d . -f $i-100)
+    _debug h "$_domain"
+    if [ -z "$_domain" ]; then
+      return 1
+    fi
+
+    _debug login "$DNSEXIT_AUTH_USER"
+    _debug password "$DNSEXIT_AUTH_PASS"
+    _debug domain "$_domain"
+
+    _dnsexit_http "login=$DNSEXIT_AUTH_USER&password=$DNSEXIT_AUTH_PASS&domain=$_domain"
+
+    if _contains "$response" "0=$_domain"; then
+      _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")"
+      return 0
+    else
+      _debug "Go to next level of $_domain"
+    fi
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}
+
+_dnsexit_rest() {
+  m=POST
+  ep=""
+  data="$1"
+  _debug _dnsexit_rest "$ep"
+  _debug data "$data"
+
+  api_key_trimmed=$(echo "$DNSEXIT_API_KEY" | tr -d '"')
+
+  export _H1="apikey: $api_key_trimmed"
+  export _H2='Content-Type: application/json'
+
+  if [ "$m" != "GET" ]; then
+    _debug data "$data"
+    response="$(_post "$data" "$DNSEXIT_API_URL/$ep" "" "$m")"
+  else
+    response="$(_get "$DNSEXIT_API_URL/$ep")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "Error $ep"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+_dnsexit_http() {
+  m=GET
+  param="$1"
+  _debug param "$param"
+  _debug get "$DNSEXIT_HOSTS_URL?$param"
+
+  response="$(_get "$DNSEXIT_HOSTS_URL?$param")"
+
+  _debug response "$response"
+
+  if [ "$?" != "0" ]; then
+    _err "Error $param"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+get_account_info() {
+
+  DNSEXIT_API_KEY="${DNSEXIT_API_KEY:-$(_readaccountconf_mutable DNSEXIT_API_KEY)}"
+  if test -z "$DNSEXIT_API_KEY"; then
+    DNSEXIT_API_KEY=''
+    _err 'DNSEXIT_API_KEY was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_API_KEY "$DNSEXIT_API_KEY"
+
+  DNSEXIT_AUTH_USER="${DNSEXIT_AUTH_USER:-$(_readaccountconf_mutable DNSEXIT_AUTH_USER)}"
+  if test -z "$DNSEXIT_AUTH_USER"; then
+    DNSEXIT_AUTH_USER=""
+    _err 'DNSEXIT_AUTH_USER was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_AUTH_USER "$DNSEXIT_AUTH_USER"
+
+  DNSEXIT_AUTH_PASS="${DNSEXIT_AUTH_PASS:-$(_readaccountconf_mutable DNSEXIT_AUTH_PASS)}"
+  if test -z "$DNSEXIT_AUTH_PASS"; then
+    DNSEXIT_AUTH_PASS=""
+    _err 'DNSEXIT_AUTH_PASS was not exported'
+    return 1
+  fi
+
+  _saveaccountconf_mutable DNSEXIT_AUTH_PASS "$DNSEXIT_AUTH_PASS"
+
+  return 0
+}

+ 19 - 6
dnsapi/dns_gandi_livedns.sh

@@ -1,7 +1,8 @@
 #!/usr/bin/env sh
 
 # Gandi LiveDNS v5 API
-# https://doc.livedns.gandi.net/
+# https://api.gandi.net/docs/livedns/
+# https://api.gandi.net/docs/authentication/ for token + apikey (deprecated) authentication
 # currently under beta
 #
 # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
@@ -19,13 +20,20 @@ dns_gandi_livedns_add() {
   fulldomain=$1
   txtvalue=$2
 
-  if [ -z "$GANDI_LIVEDNS_KEY" ]; then
-    _err "No API key specified for Gandi LiveDNS."
-    _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+  if [ -z "$GANDI_LIVEDNS_KEY" ] && [ -z "$GANDI_LIVEDNS_TOKEN" ]; then
+    _err "No Token or API key (deprecated) specified for Gandi LiveDNS."
+    _err "Create your token or key and export it as GANDI_LIVEDNS_KEY or GANDI_LIVEDNS_TOKEN respectively"
     return 1
   fi
 
-  _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+  # Keep only one secret in configuration
+  if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+    _saveaccountconf GANDI_LIVEDNS_TOKEN "$GANDI_LIVEDNS_TOKEN"
+    _clearaccountconf GANDI_LIVEDNS_KEY
+  elif [ -n "$GANDI_LIVEDNS_KEY" ]; then
+    _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+    _clearaccountconf GANDI_LIVEDNS_TOKEN
+  fi
 
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
@@ -157,7 +165,12 @@ _gandi_livedns_rest() {
   _debug "$ep"
 
   export _H1="Content-Type: application/json"
-  export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+  if [ -n "$GANDI_LIVEDNS_TOKEN" ]; then
+    export _H2="Authorization: Bearer $GANDI_LIVEDNS_TOKEN"
+  else
+    export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+  fi
 
   if [ "$m" = "GET" ]; then
     response="$(_get "$GANDI_LIVEDNS_API/$ep")"

+ 1 - 1
dnsapi/dns_gcloud.sh

@@ -42,7 +42,7 @@ dns_gcloud_rm() {
   echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
   _dns_gcloud_execute_tr || return $?
 
-  _info "$fulldomain record added"
+  _info "$fulldomain record removed"
 }
 
 ####################  Private functions below ##################################

+ 2 - 2
dnsapi/dns_gcore.sh

@@ -4,8 +4,8 @@
 #GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
 #
 
-GCORE_Api="https://api.gcorelabs.com/dns/v2"
-GCORE_Doc="https://apidocs.gcore.com/dns"
+GCORE_Api="https://api.gcore.com/dns/v2"
+GCORE_Doc="https://api.gcore.com/docs/dns"
 
 ########  Public functions #####################
 

+ 1 - 1
dnsapi/dns_inwx.sh

@@ -194,7 +194,7 @@ _inwx_login() {
 
   response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
 
-  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
+  INWX_Cookie=$(printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep -i "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')")
   _H1=$INWX_Cookie
   export _H1
   export INWX_Cookie

+ 35 - 3
dnsapi/dns_pleskxml.sh

@@ -46,6 +46,10 @@ pleskxml_tplt_get_domains="<packet><webspace><get><filter/><dataset><gen_info/><
 # Also used to test credentials and URI.
 # No params.
 
+pleskxml_tplt_get_additional_domains="<packet><site><get><filter/><dataset><gen_info/></dataset></get></site></packet>"
+# Get a list of additional domains that PLESK can manage, so we can check root domain + host for acme.sh
+# No params.
+
 pleskxml_tplt_get_dns_records="<packet><dns><get_rec><filter><site-id>%s</site-id></filter></get_rec></dns></packet>"
 # Get all DNS records for a Plesk domain ID.
 # PARAM = Plesk domain id to query
@@ -375,16 +379,44 @@ _pleskxml_get_root_domain() {
     return 1
   fi
 
-  # Generate a crude list of domains known to this Plesk account.
+  # Generate a crude list of domains known to this Plesk account based on subscriptions.
   # We convert <ascii-name> tags to <name> so it'll flag on a hit with either <name> or <ascii-name> fields,
   # for non-Western character sets.
   # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
   # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
 
   output="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
+  debug_output="$(printf "%s" "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+
+  _debug 'Domains managed by Plesk server are:'
+  _debug "$debug_output"
+
+  _debug "Querying Plesk server for list of additional managed domains..."
+
+  _call_api "$pleskxml_tplt_get_additional_domains"
+  if [ "$pleskxml_retcode" -ne 0 ]; then
+    return 1
+  fi
+
+  # Generate a crude list of additional domains known to this Plesk account based on sites.
+  # We convert <ascii-name> tags to <name> so it'll flag on a hit with either <name> or <ascii-name> fields,
+  # for non-Western character sets.
+  # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
+  # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned.
+
+  output_additional="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '<status>ok</status>' | sed 's/<ascii-name>/<name>/g;s/<\/ascii-name>/<\/name>/g' | grep '<name>' | grep '<id>')"
+  debug_additional="$(printf "%s" "$output_additional" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
+
+  _debug 'Additional domains managed by Plesk server are:'
+  _debug "$debug_additional"
+
+  # Concate the two outputs together.
+
+  output="$(printf "%s" "$output $NEWLINE $output_additional")"
+  debug_output="$(printf "%s" "$output" | sed -n 's:.*<name>\(.*\)</name>.*:\1:p')"
 
-  _debug 'Domains managed by Plesk server are (ignore the hacked output):'
-  _debug "$output"
+  _debug 'Domains (including additional) managed by Plesk server are:'
+  _debug "$debug_output"
 
   # loop and test if domain, or any parent domain, is managed by Plesk
   # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain

+ 211 - 0
dnsapi/dns_tencent.sh

@@ -0,0 +1,211 @@
+#!/usr/bin/env sh
+Tencent_API="https://dnspod.tencentcloudapi.com"
+
+#Tencent_SecretId="AKIDz81d2cd22cdcdc2dcd1cc1d1A"
+#Tencent_SecretKey="Gu5t9abcabcaabcbabcbbbcbcbbccbbcb"
+
+#Usage: dns_tencent_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_tencent_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+  if [ -z "$Tencent_SecretId" ] || [ -z "$Tencent_SecretKey" ]; then
+    Tencent_SecretId=""
+    Tencent_SecretKey=""
+    _err "You don't specify tencent api SecretId and SecretKey yet."
+    return 1
+  fi
+
+  #save the api SecretId and SecretKey to the account conf file.
+  _saveaccountconf_mutable Tencent_SecretId "$Tencent_SecretId"
+  _saveaccountconf_mutable Tencent_SecretKey "$Tencent_SecretKey"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Add record"
+  _add_record_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "CreateRecord"
+}
+
+dns_tencent_rm() {
+  fulldomain=$1
+  txtvalue=$2
+  Tencent_SecretId="${Tencent_SecretId:-$(_readaccountconf_mutable Tencent_SecretId)}"
+  Tencent_SecretKey="${Tencent_SecretKey:-$(_readaccountconf_mutable Tencent_SecretKey)}"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
+  _debug "Get record list"
+  attempt=1
+  max_attempts=5
+  while [ -z "$record_id" ] && [ "$attempt" -le $max_attempts ]; do
+    _check_exist_query "$_domain" "$_sub_domain" "$txtvalue" && _tencent_rest "DescribeRecordFilterList"
+    record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+    _debug2 record_id "$record_id"
+    if [ -z "$record_id" ]; then
+      _debug "Due to TencentCloud API synchronization delay, record not found, waiting 10 seconds and retrying"
+      _sleep 10
+      attempt=$(_math "$attempt + 1")
+    fi
+  done
+
+  record_id="$(echo "$response" | _egrep_o "\"RecordId\":\s*[0-9]+" | _egrep_o "[0-9]+")"
+  _debug2 record_id "$record_id"
+
+  if [ -z "$record_id" ]; then
+    _debug "record not found after $max_attempts attempts, skip"
+  else
+    _debug "Delete record"
+    _delete_record_query "$record_id" && _tencent_rest "DeleteRecord"
+  fi
+}
+
+####################  Private functions below ##################################
+
+_get_root() {
+  domain=$1
+  i=1
+  p=1
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+    if [ -z "$h" ]; then
+      #not valid
+      return 1
+    fi
+
+    _describe_records_query "$h" "@"
+    if ! _tencent_rest "DescribeRecordList" "ignore"; then
+      return 1
+    fi
+
+    if _contains "$response" "\"TotalCount\":"; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+      _debug _sub_domain "$_sub_domain"
+      _domain="$h"
+      _debug _domain "$_domain"
+      return 0
+    fi
+    p="$i"
+    i=$(_math "$i" + 1)
+  done
+  return 1
+}
+
+_tencent_rest() {
+  action=$1
+  service="dnspod"
+  payload="${query}"
+  timestamp=$(date -u +%s)
+
+  token=$(tencent_signature_v3 $service "$action" "$payload" "$timestamp")
+  version="2021-03-23"
+
+  if ! response="$(tencent_api_request $service $version "$action" "$payload" "$timestamp")"; then
+    _err "Error <$1>"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  if [ -z "$2" ]; then
+    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+    if [ "$message" ]; then
+      _err "$message"
+      return 1
+    fi
+  fi
+}
+
+_add_record_query() {
+  query="{\"Domain\":\"$1\",\"SubDomain\":\"$2\",\"RecordType\":\"TXT\",\"RecordLineId\":\"0\",\"RecordLine\":\"0\",\"Value\":\"$3\",\"TTL\":600}"
+}
+
+_describe_records_query() {
+  query="{\"Domain\":\"$1\",\"Limit\":3000}"
+}
+
+_delete_record_query() {
+  query="{\"Domain\":\"$_domain\",\"RecordId\":$1}"
+}
+
+_check_exist_query() {
+  _domain="$1"
+  _subdomain="$2"
+  _value="$3"
+  query="{\"Domain\":\"$_domain\",\"SubDomain\":\"$_subdomain\",\"RecordValue\":\"$_value\"}"
+}
+
+# shell client for tencent cloud api v3 | @author: rehiy
+
+tencent_sha256() {
+  printf %b "$@" | _digest sha256 hex
+}
+
+tencent_hmac_sha256() {
+  k=$1
+  shift
+  hex_key=$(printf %b "$k" | _hex_dump | tr -d ' ')
+  printf %b "$@" | _hmac sha256 "$hex_key" hex
+}
+
+tencent_hmac_sha256_hexkey() {
+  k=$1
+  shift
+  printf %b "$@" | _hmac sha256 "$k" hex
+}
+
+tencent_signature_v3() {
+  service=$1
+  action=$(echo "$2" | _lower_case)
+  payload=${3:-'{}'}
+  timestamp=${4:-$(date +%s)}
+
+  domain="$service.tencentcloudapi.com"
+  secretId=${Tencent_SecretId:-'tencent-cloud-secret-id'}
+  secretKey=${Tencent_SecretKey:-'tencent-cloud-secret-key'}
+
+  algorithm='TC3-HMAC-SHA256'
+  date=$(date -u -d "@$timestamp" +%Y-%m-%d 2>/dev/null)
+  [ -z "$date" ] && date=$(date -u -r "$timestamp" +%Y-%m-%d)
+
+  canonicalUri='/'
+  canonicalQuery=''
+  canonicalHeaders="content-type:application/json\nhost:$domain\nx-tc-action:$action\n"
+
+  signedHeaders='content-type;host;x-tc-action'
+  canonicalRequest="POST\n$canonicalUri\n$canonicalQuery\n$canonicalHeaders\n$signedHeaders\n$(tencent_sha256 "$payload")"
+
+  credentialScope="$date/$service/tc3_request"
+  stringToSign="$algorithm\n$timestamp\n$credentialScope\n$(tencent_sha256 "$canonicalRequest")"
+
+  secretDate=$(tencent_hmac_sha256 "TC3$secretKey" "$date")
+  secretService=$(tencent_hmac_sha256_hexkey "$secretDate" "$service")
+  secretSigning=$(tencent_hmac_sha256_hexkey "$secretService" 'tc3_request')
+  signature=$(tencent_hmac_sha256_hexkey "$secretSigning" "$stringToSign")
+
+  echo "$algorithm Credential=$secretId/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature"
+}
+
+tencent_api_request() {
+  service=$1
+  version=$2
+  action=$3
+  payload=${4:-'{}'}
+  timestamp=${5:-$(date +%s)}
+
+  token=$(tencent_signature_v3 "$service" "$action" "$payload" "$timestamp")
+
+  _H1="Content-Type: application/json"
+  _H2="Authorization: $token"
+  _H3="X-TC-Version: $version"
+  _H4="X-TC-Timestamp: $timestamp"
+  _H5="X-TC-Action: $action"
+
+  _post "$payload" "$Tencent_API" "" "POST" "application/json"
+}

+ 9 - 12
dnsapi/dns_variomedia.sh

@@ -69,7 +69,7 @@ dns_variomedia_rm() {
     return 1
   fi
 
-  _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
+  _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
   _debug _record_id "$_record_id"
   if [ "$_record_id" ]; then
     _info "Successfully retrieved the record id for ACME challenge."
@@ -93,11 +93,11 @@ dns_variomedia_rm() {
 # _sub_domain=_acme-challenge.www
 # _domain=domain.com
 _get_root() {
-  fulldomain=$1
+  domain=$1
   i=1
+  p=1
   while true; do
-    h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
-    _debug h "$h"
+    h=$(printf "%s" "$domain" | cut -d . -f $i-100)
     if [ -z "$h" ]; then
       return 1
     fi
@@ -106,17 +106,14 @@ _get_root() {
       return 1
     fi
 
-    if _startswith "$response" "\{\"data\":"; then
-      if _contains "$response" "\"id\":\"$h\""; then
-        _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")"
-        _domain=$h
-        return 0
-      fi
+    if _contains "$response" "\"id\":\"$h\""; then
+      _sub_domain=$(printf "%s" "$domain" | cut -d '.' -f 1-$p)
+      _domain="$h"
+      return 0
     fi
+    p=$i
     i=$(_math "$i" + 1)
   done
-
-  _debug "root domain not found"
   return 1
 }
 

+ 105 - 0
dnsapi/dns_west_cn.sh

@@ -0,0 +1,105 @@
+#!/usr/bin/env sh
+
+# West.cn Domain api
+#WEST_Username="username"
+#WEST_Key="sADDsdasdgdsf"
+#Set key at https://www.west.cn/manager/API/APIconfig.asp
+
+REST_API="https://api.west.cn/API/v2"
+
+########  Public functions #####################
+
+#Usage: add  _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_west_cn_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+  WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+  if [ -z "$WEST_Username" ] || [ -z "$WEST_Key" ]; then
+    WEST_Username=""
+    WEST_Key=""
+    _err "You don't specify west api key and username yet."
+    _err "Please set you key and try again."
+    return 1
+  fi
+
+  #save the api key and email to the account conf file.
+  _saveaccountconf_mutable WEST_Username "$WEST_Username"
+  _saveaccountconf_mutable WEST_Key "$WEST_Key"
+
+  add_record "$fulldomain" "$txtvalue"
+}
+
+#Usage: rm _acme-challenge.www.domain.com
+dns_west_cn_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  WEST_Username="${WEST_Username:-$(_readaccountconf_mutable WEST_Username)}"
+  WEST_Key="${WEST_Key:-$(_readaccountconf_mutable WEST_Key)}"
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.list&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT"; then
+    _err "dnsrec.list error."
+    return 1
+  fi
+
+  if _contains "$response" 'no records'; then
+    _info "Don't need to remove."
+    return 0
+  fi
+
+  record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"record_id"' | cut -d : -f 2 | cut -d ',' -f 1)
+  _debug record_id "$record_id"
+  if [ -z "$record_id" ]; then
+    _err "Can not get record id."
+    return 1
+  fi
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.remove&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_id=$record_id"; then
+    _err "dnsrec.remove error."
+    return 1
+  fi
+
+  _contains "$response" "success"
+}
+
+#add the txt record.
+#usage: add fulldomain txtvalue
+add_record() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _info "Adding record"
+
+  if ! _rest POST "domain/dns/" "act=dnsrec.add&username=$WEST_Username&apikey=$WEST_Key&domain=$fulldomain&hostname=$fulldomain&record_type=TXT&record_value=$txtvalue"; then
+    return 1
+  fi
+
+  _contains "$response" "success"
+}
+
+#Usage: method  URI  data
+_rest() {
+  m="$1"
+  ep="$2"
+  data="$3"
+  _debug "$ep"
+  url="$REST_API/$ep"
+
+  _debug url "$url"
+
+  if [ "$m" = "GET" ]; then
+    response="$(_get "$url" | tr -d '\r')"
+  else
+    _debug2 data "$data"
+    response="$(_post "$data" "$url" | tr -d '\r')"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "error $ep"
+    return 1
+  fi
+  _debug2 response "$response"
+  return 0
+}

+ 52 - 0
notify/mattermost.sh

@@ -0,0 +1,52 @@
+#!/usr/bin/env sh
+
+# Support mattermost bots
+
+#MATTERMOST_API_URL=""
+#MATTERMOST_CHANNEL_ID=""
+#MATTERMOST_BOT_TOKEN=""
+
+mattermost_send() {
+  _subject="$1"
+  _content="$2"
+  _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped
+  _debug "_statusCode" "$_statusCode"
+
+  MATTERMOST_API_URL="${MATTERMOST_API_URL:-$(_readaccountconf_mutable MATTERMOST_API_URL)}"
+  if [ -z "$MATTERMOST_API_URL" ]; then
+    _err "You didn't specify a Mattermost API URL MATTERMOST_API_URL yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_API_URL "$MATTERMOST_API_URL"
+
+  MATTERMOST_CHANNEL_ID="${MATTERMOST_CHANNEL_ID:-$(_readaccountconf_mutable MATTERMOST_CHANNEL_ID)}"
+  if [ -z "$MATTERMOST_CHANNEL_ID" ]; then
+    _err "You didn't specify a Mattermost channel id MATTERMOST_CHANNEL_ID yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_CHANNEL_ID "$MATTERMOST_CHANNEL_ID"
+
+  MATTERMOST_BOT_TOKEN="${MATTERMOST_BOT_TOKEN:-$(_readaccountconf_mutable MATTERMOST_BOT_TOKEN)}"
+  if [ -z "$MATTERMOST_BOT_TOKEN" ]; then
+    _err "You didn't specify a Mattermost bot API token MATTERMOST_BOT_TOKEN yet."
+    return 1
+  fi
+  _saveaccountconf_mutable MATTERMOST_BOT_TOKEN "$MATTERMOST_BOT_TOKEN"
+
+  _content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
+  _data="{\"channel_id\": \"$MATTERMOST_CHANNEL_ID\", "
+  _data="$_data\"message\": \"$_content\"}"
+
+  export _H1="Authorization: Bearer $MATTERMOST_BOT_TOKEN"
+  response=""
+  if _post "$_data" "$MATTERMOST_API_URL" "" "POST" "application/json; charset=utf-8"; then
+    MATTERMOST_RESULT_OK=$(echo "$response" | _egrep_o 'create_at')
+    if [ "$?" = "0" ] && [ "$MATTERMOST_RESULT_OK" ]; then
+      _info "mattermost send success."
+      return 0
+    fi
+  fi
+  _err "mattermost send error."
+  _err "$response"
+  return 1
+}