浏览代码

Merge branch 'acmesh-official:master' into master

laraveluser 1 年之前
父节点
当前提交
a6a1de50c8

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

@@ -65,7 +65,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh acmetest/
     - name: Set env file
     - name: Set env file
@@ -113,7 +113,7 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
     - name: Install tools
       run:  brew install socat
       run:  brew install socat
     - name: Clone acmetest
     - name: Clone acmetest
@@ -164,7 +164,7 @@ jobs:
     - name: Set git to use LF
     - name: Set git to use LF
       run: |
       run: |
           git config --global core.autocrlf false
           git config --global core.autocrlf false
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install cygwin base packages with chocolatey
     - name: Install cygwin base packages with chocolatey
       run: |
       run: |
           choco config get cacheLocation
           choco config get cacheLocation
@@ -204,7 +204,7 @@ jobs:
 
 
 
 
   FreeBSD:
   FreeBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: Windows
     needs: Windows
     env:
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -223,10 +223,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       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}}'
         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
         prepare: pkg install -y socat curl
@@ -255,7 +255,7 @@ jobs:
 
 
 
 
   OpenBSD:
   OpenBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: FreeBSD
     needs: FreeBSD
     env:
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -274,10 +274,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       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}}'
         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
         prepare: pkg_add socat curl
@@ -306,7 +306,7 @@ jobs:
 
 
 
 
   NetBSD:
   NetBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: OpenBSD
     needs: OpenBSD
     env:
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -325,10 +325,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       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}}'
         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: |
         prepare: |
@@ -358,7 +358,7 @@ jobs:
 
 
 
 
   DragonFlyBSD:
   DragonFlyBSD:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: NetBSD
     needs: NetBSD
     env:
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -377,14 +377,14 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       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}}'
         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: |
         prepare: |
-          pkg install -y curl socat
+          pkg install -y curl socat libnghttp2
         usesh: true
         usesh: true
         copyback: false
         copyback: false
         run: |
         run: |
@@ -413,7 +413,7 @@ jobs:
 
 
 
 
   Solaris:
   Solaris:
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     needs: DragonFlyBSD
     needs: DragonFlyBSD
     env:
     env:
       TEST_DNS : ${{ secrets.TEST_DNS }}
       TEST_DNS : ${{ secrets.TEST_DNS }}
@@ -433,10 +433,10 @@ jobs:
       TokenName4: ${{ secrets.TokenName4}}
       TokenName4: ${{ secrets.TokenName4}}
       TokenName5: ${{ secrets.TokenName5}}
       TokenName5: ${{ secrets.TokenName5}}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       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}}'
         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
         copyback: false
@@ -463,3 +463,54 @@ jobs:
           ./letest.sh
           ./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: "ZeroSSL RSA Domain Secure Site CA"
          #  CA_EMAIL: "[email protected]"
          #  CA_EMAIL: "[email protected]"
          #  TEST_PREFERRED_CHAIN: ""
          #  TEST_PREFERRED_CHAIN: ""
-    runs-on: macos-12
+    runs-on: ubuntu-latest
     env:
     env:
       TEST_LOCAL: 1
       TEST_LOCAL: 1
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
       TEST_ACME_Server: ${{ matrix.TEST_ACME_Server }}
@@ -51,7 +51,7 @@ jobs:
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       TEST_PREFERRED_CHAIN: ${{ matrix.TEST_PREFERRED_CHAIN }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: vmactions/cf-tunnel@v0
     - uses: vmactions/cf-tunnel@v0
       id: tunnel
       id: tunnel
       with:
       with:
@@ -61,7 +61,7 @@ jobs:
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
       run: echo "TestingDomain=${{steps.tunnel.outputs.server}}" >> $GITHUB_ENV
     - name: Clone acmetest
     - name: Clone acmetest
       run: cd .. && git clone --depth=1 https://github.com/acmesh-official/acmetest.git  && cp -r acme.sh 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:
       with:
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         envs: 'TEST_LOCAL TestingDomain TEST_ACME_Server CA_ECDSA CA CA_EMAIL TEST_PREFERRED_CHAIN ACME_USE_WGET'
         nat: |
         nat: |

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

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

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

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

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

@@ -33,7 +33,7 @@ jobs:
       TEST_CA: "Pebble Intermediate CA"
       TEST_CA: "Pebble Intermediate CA"
 
 
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
     - name: Install tools
       run: sudo apt-get install -y socat
       run: sudo apt-get install -y socat
     - name: Run Pebble
     - name: Run Pebble
@@ -58,7 +58,7 @@ jobs:
       TEST_IPCERT: 1
       TEST_IPCERT: 1
 
 
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
     - name: Install tools
       run: sudo apt-get install -y socat
       run: sudo apt-get install -y socat
     - name: Run Pebble
     - 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 }}
       TestingDomain: ${{ matrix.TestingDomain }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
       ACME_USE_WGET: ${{ matrix.ACME_USE_WGET }}
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install tools
     - name: Install tools
       run: sudo apt-get install -y socat wget
       run: sudo apt-get install -y socat wget
     - name: Start StepCA
     - name: Start StepCA

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

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

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

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

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

@@ -22,9 +22,10 @@ jobs:
               owner: context.repo.owner,
               owner: context.repo.owner,
               repo: context.repo.repo,
               repo: context.repo.repo,
               body: `**Welcome**
               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.
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
                 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,
               owner: context.repo.owner,
               repo: context.repo.repo,
               repo: context.repo.repo,
               body: `**Welcome**
               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.
                 Then reply on this message, otherwise, your code will not be reviewed or merged.
                 We look forward to reviewing your Pull request shortly ✨
                 We look forward to reviewing your Pull request shortly ✨
                 `
                 `

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

@@ -22,7 +22,7 @@ jobs:
   ShellCheck:
   ShellCheck:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install Shellcheck
     - name: Install Shellcheck
       run: sudo apt-get install -y shellcheck
       run: sudo apt-get install -y shellcheck
     - name: DoShellcheck
     - name: DoShellcheck
@@ -31,7 +31,7 @@ jobs:
   shfmt:
   shfmt:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: Install shfmt
     - 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
       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
     - 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)
 [![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)
 [![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)
 [![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)
 ![Shellcheck](https://github.com/acmesh-official/acme.sh/workflows/Shellcheck/badge.svg)
 ![PebbleStrict](https://github.com/acmesh-official/acme.sh/workflows/PebbleStrict/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
 |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
 |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
 |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):
 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>
 <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
 # 19. License & Others
 
 

+ 27 - 28
acme.sh

@@ -1,6 +1,6 @@
 #!/usr/bin/env sh
 #!/usr/bin/env sh
 
 
-VER=3.0.7
+VER=3.0.8
 
 
 PROJECT_NAME="acme.sh"
 PROJECT_NAME="acme.sh"
 
 
@@ -102,12 +102,12 @@ ECC_SUFFIX="${ECC_SEP}ecc"
 LOG_LEVEL_1=1
 LOG_LEVEL_1=1
 LOG_LEVEL_2=2
 LOG_LEVEL_2=2
 LOG_LEVEL_3=3
 LOG_LEVEL_3=3
-DEFAULT_LOG_LEVEL="$LOG_LEVEL_1"
+DEFAULT_LOG_LEVEL="$LOG_LEVEL_2"
 
 
 DEBUG_LEVEL_1=1
 DEBUG_LEVEL_1=1
 DEBUG_LEVEL_2=2
 DEBUG_LEVEL_2=2
 DEBUG_LEVEL_3=3
 DEBUG_LEVEL_3=3
-DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1
+DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_2
 DEBUG_LEVEL_NONE=0
 DEBUG_LEVEL_NONE=0
 
 
 DOH_CLOUDFLARE=1
 DOH_CLOUDFLARE=1
@@ -931,7 +931,7 @@ fi
 
 
 _egrep_o() {
 _egrep_o() {
   if [ "$__USE_EGREP" ]; then
   if [ "$__USE_EGREP" ]; then
-    egrep -o "$1"
+    egrep -o -- "$1" 2>/dev/null
   else
   else
     sed -n 's/.*\('"$1"'\).*/\1/p'
     sed -n 's/.*\('"$1"'\).*/\1/p'
   fi
   fi
@@ -1561,7 +1561,7 @@ createDomainKey() {
 createCSR() {
 createCSR() {
   _info "Creating csr"
   _info "Creating csr"
   if [ -z "$1" ]; then
   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
     return
   fi
   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
   if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
     return
     return
   fi
   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"
   _err "Can not parse _date2time $1"
   return 1
   return 1
 }
 }
@@ -2118,12 +2122,7 @@ _tail_n() {
 }
 }
 
 
 _tail_c() {
 _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
 # url  payload needbase64  keyfile
@@ -3130,7 +3129,7 @@ _setNginx() {
         _err "nginx command is not found."
         _err "nginx command is not found."
         return 1
         return 1
       fi
       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"
       _debug NGINX_CONF "$NGINX_CONF"
       NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
       NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)"
       _debug NGINX_CONF "$NGINX_CONF"
       _debug NGINX_CONF "$NGINX_CONF"
@@ -5015,9 +5014,9 @@ $_authorizations_map"
         errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
         errordetail="$(echo "$error" | _egrep_o '"detail": *"[^"]*' | cut -d '"' -f 4)"
         _debug2 errordetail "$errordetail"
         _debug2 errordetail "$errordetail"
         if [ "$errordetail" ]; then
         if [ "$errordetail" ]; then
-          _err "$d:Verify error:$errordetail"
+          _err "Invalid status, $d:Verify error detail:$errordetail"
         else
         else
-          _err "$d:Verify error:$error"
+          _err "Invalid status, $d:Verify error:$error"
         fi
         fi
         if [ "$DEBUG" ]; then
         if [ "$DEBUG" ]; then
           if [ "$vtype" = "$VTYPE_HTTP" ]; then
           if [ "$vtype" = "$VTYPE_HTTP" ]; then
@@ -5044,7 +5043,7 @@ $_authorizations_map"
       elif _contains "$status" "processing"; then
       elif _contains "$status" "processing"; then
         _info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
         _info "Processing, The CA is processing your order, please just wait. ($waittimes/$MAX_RETRY_TIMES)"
       else
       else
-        _err "$d:Verify error:$response"
+        _err "Unknown status: $status, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
         _on_issue_err "$_post_hook" "$vlist"
         _on_issue_err "$_post_hook" "$vlist"
@@ -5057,7 +5056,7 @@ $_authorizations_map"
       _send_signed_request "$_authz_url"
       _send_signed_request "$_authz_url"
 
 
       if [ "$?" != "0" ]; then
       if [ "$?" != "0" ]; then
-        _err "$d:Verify error:$response"
+        _err "Invalid code, $d:Verify error:$response"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
         _clearup
         _clearup
         _on_issue_err "$_post_hook" "$vlist"
         _on_issue_err "$_post_hook" "$vlist"
@@ -5993,6 +5992,7 @@ installcronjob() {
   fi
   fi
   _t=$(_time)
   _t=$(_time)
   random_minute=$(_math $_t % 60)
   random_minute=$(_math $_t % 60)
+  random_hour=$(_math $_t / 60 % 24)
 
 
   if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
   if ! _exists "$_CRONTAB" && _exists "fcrontab"; then
     _CRONTAB="fcrontab"
     _CRONTAB="fcrontab"
@@ -6017,16 +6017,14 @@ installcronjob() {
   _info "Installing cron job"
   _info "Installing cron job"
   if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
   if ! $_CRONTAB -l | grep "$PROJECT_ENTRY --cron"; then
     if _exists uname && uname -a | grep SunOS >/dev/null; 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
     else
-      $_CRONTAB -l | {
-        cat
-        echo "$random_minute 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
-      } | $_CRONTAB -
+      _CRONTAB_STDIN="$_CRONTAB -"
     fi
     fi
+    $_CRONTAB -l | {
+      cat
+      echo "$random_minute $random_hour * * * $lesh --cron --home \"$LE_WORKING_DIR\" $_c_entry> /dev/null"
+    } | $_CRONTAB_STDIN
   fi
   fi
   if [ "$?" != "0" ]; then
   if [ "$?" != "0" ]; then
     _err "Install cron job failed. You need to manually renew your certs."
     _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.
   -f, --force                       Force install, force cert renewal or override sudo restrictions.
   --staging, --test                 Use staging server, for testing.
   --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.
   --output-insecure                 Output all the sensitive messages.
                                       By default all the credentials/sensitive messages are hidden from the output/debug/log for security.
                                       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.
   -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.
   -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
   -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 [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.
   --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-kid <eab_key_id>            Key Identifier for External Account Binding.
   --eab-hmac-key <eab_hmac_key>     HMAC key 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:
   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.
   --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.
   --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.
   --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-profile                      Only valid for '--install' command, which means: do not install aliases to user profile.
   --no-color                        Do not output color text.
   --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.
   --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.
   --csr <file>                      Specifies the input csr.
   --pre-hook <command>              Command to be run before obtaining any certificates.
   --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.
   --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
   # Create a temporary PEM file
   _temppem="$(_mktemp)"
   _temppem="$(_mktemp)"
   _debug _temppem "${_temppem}"
   _debug _temppem "${_temppem}"
-  cat "${_ckey}" "${_ccert}" "${_cca}" >"${_temppem}"
+  cat "${_ccert}" "${_cca}" "${_ckey}" >"${_temppem}"
   _ret="$?"
   _ret="$?"
 
 
   # Check that we could create the temporary file
   # 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.
 # 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() {
 parse_response() {
   type=$2
   type=$2
   if [ "$type" = 'keygen' ]; then
   if [ "$type" = 'keygen' ]; then
@@ -23,25 +27,46 @@ parse_response() {
       message="PAN-OS Key could not be set."
       message="PAN-OS Key could not be set."
     fi
     fi
   else
   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
   fi
   return 0
   return 0
 }
 }
 
 
+#This function is used to deploy to the firewall
 deployer() {
 deployer() {
   content=""
   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/"
   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
   if [ "$type" = 'keygen' ]; then
+    _debug "**** Generating new API Key ****"
     _H1="Content-Type: application/x-www-form-urlencoded"
     _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=keygen&user=$_panos_user&password=$_panos_pass"
     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}"
     # 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
   fi
 
 
+  # Deploy Cert or Key
   if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
   if [ "$type" = 'cert' ] || [ "$type" = 'key' ]; then
-    #Generate DEIM
+    _debug "**** Deploying $type ****"
+    #Generate DELIM
     delim="-----MultipartDelimiter$(date "+%s%N")"
     delim="-----MultipartDelimiter$(date "+%s%N")"
     nl="\015\012"
     nl="\015\012"
     #Set Header
     #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=\"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=\"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=\"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
     fi
     #Close multipart
     #Close multipart
     content="$content${nl}--$delim--${nl}${nl}"
     content="$content${nl}--$delim--${nl}${nl}"
@@ -69,16 +94,25 @@ deployer() {
     content=$(printf %b "$content")
     content=$(printf %b "$content")
   fi
   fi
 
 
+  # Commit changes
   if [ "$type" = 'commit' ]; then
   if [ "$type" = 'commit' ]; then
+    _debug "**** Committing changes ****"
     export _H1="Content-Type: application/x-www-form-urlencoded"
     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
   fi
+
   response=$(_post "$content" "$panos_url" "" "POST")
   response=$(_post "$content" "$panos_url" "" "POST")
   parse_response "$response" "$type"
   parse_response "$response" "$type"
   # Saving response to variables
   # Saving response to variables
   response_status=$status
   response_status=$status
-  #DEBUG
   _debug response_status "$response_status"
   _debug response_status "$response_status"
   if [ "$response_status" = "success" ]; then
   if [ "$response_status" = "success" ]; then
     _debug "Successfully deployed $type"
     _debug "Successfully deployed $type"
@@ -92,43 +126,85 @@ deployer() {
 
 
 # This is the main function that will call the other functions to deploy everything.
 # This is the main function that will call the other functions to deploy everything.
 panos_deploy() {
 panos_deploy() {
-  _cdomain="$1"
+  _cdomain=$(echo "$1" | sed 's/*/WILDCARD_/g') #Wildcard Safe Filename
   _ckey="$2"
   _ckey="$2"
   _cfullchain="$5"
   _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
   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
     _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_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
   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
     return 1
   else
   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
     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
       return 1
     else
     else
       deployer cert
       deployer cert

+ 4 - 4
deploy/proxmoxve.sh

@@ -99,11 +99,11 @@ proxmoxve_deploy() {
     _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
     _proxmoxve_api_token_key="$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
     _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
     _savedeployconf DEPLOY_PROXMOXVE_API_TOKEN_KEY "$DEPLOY_PROXMOXVE_API_TOKEN_KEY"
   fi
   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".
   # 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}"
   _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
   # Ugly. I hate putting heredocs inside functions because heredocs don't
   # account for whitespace correctly but it _does_ work and is several times
   # account for whitespace correctly but it _does_ work and is several times
@@ -124,8 +124,8 @@ HEREDOC
   )
   )
   _debug2 Payload "$_json_payload"
   _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}"
   export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
   _post "$_json_payload" "$_target_url" "" POST "application/json"
   _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"
   _debug "Getting API version"
   response=$(_get "$_base_url/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query&query=SYNO.API.Auth")
   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')
   api_version=$(echo "$response" | grep "SYNO.API.Auth" | sed -n 's/.*"maxVersion" *: *\([0-9]*\).*/\1/p')
   _debug3 response "$response"
   _debug3 response "$response"
+  _debug3 api_path "$api_path"
   _debug3 api_version "$api_version"
   _debug3 api_version "$api_version"
 
 
   # Login, get the session ID & SynoToken from JSON
   # Login, get the session ID & SynoToken from JSON
@@ -133,12 +135,15 @@ synology_dsm_deploy() {
       [ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal"
       [ -n "${SYNO_Device_Name}" ] || SYNO_Device_Name="CertRenewal"
     fi
     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"
     _secure_debug2 SYNO_Device_ID "$SYNO_Device_ID"
   else
   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"
     _debug3 response "$response"
   fi
   fi
 
 

+ 2 - 2
dnsapi/dns_1984hosting.sh

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

+ 1 - 1
dnsapi/dns_ali.sh

@@ -117,7 +117,7 @@ _ali_urlencode() {
 _ali_nonce() {
 _ali_nonce() {
   #_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
   #_head_n 1 </dev/urandom | _digest "sha256" hex | cut -c 1-31
   #Not so good...
   #Not so good...
-  date +"%s%N"
+  date +"%s%N" | sed 's/%N//g'
 }
 }
 
 
 _check_exist_query() {
 _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)
   # iterate over names (a.b.c.d -> b.c.d -> c.d -> d)
   while true; do
   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"
     _debug "Checking domain: $h"
     if [ -z "$h" ]; then
     if [ -z "$h" ]; then
       _error "invalid domain"
       _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
 #!/usr/bin/env sh
 
 
 # Gandi LiveDNS v5 API
 # 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
 # currently under beta
 #
 #
 # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
 # Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
@@ -19,13 +20,20 @@ dns_gandi_livedns_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   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
     return 1
   fi
   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"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -157,7 +165,12 @@ _gandi_livedns_rest() {
   _debug "$ep"
   _debug "$ep"
 
 
   export _H1="Content-Type: application/json"
   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
   if [ "$m" = "GET" ]; then
     response="$(_get "$GANDI_LIVEDNS_API/$ep")"
     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 $?
   echo "$rrdatas" | grep -F -v -- "\"$txtvalue\"" | _dns_gcloud_add_rrs || return $?
   _dns_gcloud_execute_tr || return $?
   _dns_gcloud_execute_tr || return $?
 
 
-  _info "$fulldomain record added"
+  _info "$fulldomain record removed"
 }
 }
 
 
 ####################  Private functions below ##################################
 ####################  Private functions below ##################################

+ 2 - 2
dnsapi/dns_gcore.sh

@@ -4,8 +4,8 @@
 #GCORE_Key='773$7b7adaf2a2b32bfb1b83787b4ff32a67eb178e3ada1af733e47b1411f2461f7f4fa7ed7138e2772a46124377bad7384b3bb8d87748f87b3f23db4b8bbe41b2bb'
 #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 #####################
 ########  Public functions #####################
 
 

+ 1 - 1
dnsapi/dns_inwx.sh

@@ -194,7 +194,7 @@ _inwx_login() {
 
 
   response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
   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
   _H1=$INWX_Cookie
   export _H1
   export _H1
   export INWX_Cookie
   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.
 # Also used to test credentials and URI.
 # No params.
 # 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>"
 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.
 # Get all DNS records for a Plesk domain ID.
 # PARAM = Plesk domain id to query
 # PARAM = Plesk domain id to query
@@ -375,16 +379,44 @@ _pleskxml_get_root_domain() {
     return 1
     return 1
   fi
   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,
   # 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.
   # for non-Western character sets.
   # Output will be one line per known domain, containing 2 <name> tages and a single <id> tag
   # 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.
   # 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>')"
   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 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
   # 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
     return 1
   fi
   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"
   _debug _record_id "$_record_id"
   if [ "$_record_id" ]; then
   if [ "$_record_id" ]; then
     _info "Successfully retrieved the record id for ACME challenge."
     _info "Successfully retrieved the record id for ACME challenge."
@@ -93,11 +93,11 @@ dns_variomedia_rm() {
 # _sub_domain=_acme-challenge.www
 # _sub_domain=_acme-challenge.www
 # _domain=domain.com
 # _domain=domain.com
 _get_root() {
 _get_root() {
-  fulldomain=$1
+  domain=$1
   i=1
   i=1
+  p=1
   while true; do
   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
     if [ -z "$h" ]; then
       return 1
       return 1
     fi
     fi
@@ -106,17 +106,14 @@ _get_root() {
       return 1
       return 1
     fi
     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
     fi
+    p=$i
     i=$(_math "$i" + 1)
     i=$(_math "$i" + 1)
   done
   done
-
-  _debug "root domain not found"
   return 1
   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
+}