Browse Source

Merge pull request #6505 from acmesh-official/dev

sync
neil 1 tháng trước cách đây
mục cha
commit
03dd318265

+ 2 - 0
.github/workflows/wiki-monitor.yml

@@ -12,6 +12,7 @@ jobs:
         with:
           repository: ${{ github.repository }}.wiki
           path: wiki
+          fetch-depth: 0
 
       - name: Generate wiki change message
         run: |
@@ -58,3 +59,4 @@ jobs:
 
 
 
+

+ 16 - 9
acme.sh

@@ -1811,6 +1811,10 @@ _time() {
 #    2022-04-01 08:10:33   to   1648800633
 #or  2022-04-01T08:10:33Z  to   1648800633
 _date2time() {
+  #Mac/BSD
+  if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
+    return
+  fi
   #Linux
   if date -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
     return
@@ -1820,10 +1824,6 @@ _date2time() {
   if gdate -u -d "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
     return
   fi
-  #Mac/BSD
-  if date -u -j -f "%Y-%m-%d %H:%M:%S" "$(echo "$1" | tr -d "Z" | tr "T" ' ')" +"%s" 2>/dev/null; then
-    return
-  fi
   #Omnios
   if python3 -c "import datetime; print(int(datetime.datetime.strptime(\"$1\", \"%Y-%m-%d %H:%M:%S\").replace(tzinfo=datetime.timezone.utc).timestamp()))" 2>/dev/null; then
     return
@@ -2538,15 +2538,19 @@ _startserver() {
   _NC="socat"
   if [ "$Le_Listen_V6" ]; then
     _NC="$_NC -6"
-  else
+    SOCAT_OPTIONS=TCP6-LISTEN
+  elif [ "$Le_Listen_V4" ]; then
     _NC="$_NC -4"
+    SOCAT_OPTIONS=TCP4-LISTEN
+  else
+    SOCAT_OPTIONS=TCP-LISTEN
   fi
 
   if [ "$DEBUG" ] && [ "$DEBUG" -gt "1" ]; then
     _NC="$_NC -d -d -v"
   fi
 
-  SOCAT_OPTIONS=TCP-LISTEN:$Le_HTTPPort,crlf,reuseaddr,fork
+  SOCAT_OPTIONS=$SOCAT_OPTIONS:$Le_HTTPPort,crlf,reuseaddr,fork
 
   #Adding bind to local-address
   if [ "$ncaddr" ]; then
@@ -3513,7 +3517,7 @@ _on_before_issue() {
   _debug _chk_alt_domains "$_chk_alt_domains"
   #run pre hook
   if [ "$_chk_pre_hook" ]; then
-    _info "Runing pre hook:'$_chk_pre_hook'"
+    _info "Running pre hook:'$_chk_pre_hook'"
     if ! (
       export Le_Domain="$_chk_main_domain"
       export Le_Alt="$_chk_alt_domains"
@@ -4502,6 +4506,7 @@ issue() {
 
   if ! _on_before_issue "$_web_roots" "$_main_domain" "$_alt_domains" "$_pre_hook" "$_local_addr"; then
     _err "_on_before_issue."
+    _on_issue_err "$_post_hook"
     return 1
   fi
 
@@ -4761,7 +4766,8 @@ $_authorizations_map"
         _debug keyauthorization "$keyauthorization"
       fi
 
-      entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
+      # Fix for empty error objects in response which mess up the original code, adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018
+      entry="$(echo "$response" | sed s/'"error":{}'/'"error":null'/ | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
       _debug entry "$entry"
 
       if [ -z "$keyauthorization" -a -z "$entry" ]; then
@@ -6350,7 +6356,8 @@ _deactivate() {
     fi
     _debug "Trigger validation."
     vtype="$(_getIdType "$_d_domain")"
-    entry="$(echo "$response" | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
+    # Fix for empty error objects in response which mess up the original code, adapted from fix suggested here: https://github.com/acmesh-official/acme.sh/issues/4933#issuecomment-1870499018
+    entry="$(echo "$response" | sed s/'"error":{}'/'"error":null'/ | _egrep_o '[^\{]*"type":"'$vtype'"[^\}]*')"
     _debug entry "$entry"
     if [ -z "$entry" ]; then
       _err "$d: Cannot get domain token"

+ 73 - 6
deploy/panos.sh

@@ -7,20 +7,26 @@
 #
 # Firewall admin with superuser and IP address is required.
 #
-# REQURED:
+# REQUIRED:
 #     export PANOS_HOST=""
 #     export PANOS_USER=""    #User *MUST* have Commit and Import Permissions in XML API for Admin Role
 #     export PANOS_PASS=""
 #
 # OPTIONAL
-#    export PANOS_TEMPLATE="" #Template Name of panorama managed devices
+#    export PANOS_TEMPLATE="" # Template Name of panorama managed devices
+#    export PANOS_TEMPLATE_STACK="" # set a Template Stack if certificate should also be pushed automatically
+#    export PANOS_VSYS="Shared"  # name of the vsys to import the certificate
 #
 # The script will automatically generate a new API key if
 # no key is found, or if a saved key has expired or is invalid.
 
+_COMMIT_WAIT_INTERVAL=30   # query commit status every 30 seconds
+_COMMIT_WAIT_ITERATIONS=20 # query commit status 20 times (20*30 = 600 seconds = 10 minutes)
+
 # This function is to parse the XML response from the firewall
 parse_response() {
   type=$2
+  _debug "API Response: $1"
   if [ "$type" = 'keygen' ]; then
     status=$(echo "$1" | sed 's/^.*\(['\'']\)\([a-z]*\)'\''.*/\2/g')
     if [ "$status" = "success" ]; then
@@ -30,6 +36,13 @@ parse_response() {
       message="PAN-OS Key could not be set."
     fi
   else
+    if [ "$type" = 'commit' ]; then
+      job_id=$(echo "$1" | sed 's/^.*\(<job>\)\(.*\)<\/job>.*/\2/g')
+      _commit_job_id=$job_id
+    elif [ "$type" = 'job_status' ]; then
+      job_status=$(echo "$1" | tr -d '\n' | sed 's/^.*<result>\([^<]*\)<\/result>.*/\1/g')
+      _commit_job_status=$job_status
+    fi
     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"
@@ -44,13 +57,13 @@ parse_response() {
 #This function is used to deploy to the firewall
 deployer() {
   content=""
-  type=$1 # Types are keytest, keygen, cert, key, commit
+  type=$1 # Types are keytest, keygen, cert, key, commit, job_status, push
   panos_url="https://$_panos_host/api/"
+  export _H1="Content-Type: application/x-www-form-urlencoded"
 
   #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
@@ -61,7 +74,6 @@ deployer() {
   # Generate API Key
   if [ "$type" = 'keygen' ]; then
     _debug "**** Generating new API Key ****"
-    _H1="Content-Type: application/x-www-form-urlencoded"
     content="type=keygen&user=$_panos_user&password=$_panos_pass"
     # content="$content${nl}--$delim${nl}Content-Disposition: form-data; type=\"keygen\"; user=\"$_panos_user\"; password=\"$_panos_pass\"${nl}Content-Type: application/octet-stream${nl}${nl}"
   fi
@@ -84,6 +96,9 @@ deployer() {
       if [ "$_panos_template" ]; then
         content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
       fi
+      if [ "$_panos_vsys" ]; then
+        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
+      fi
     fi
     if [ "$type" = 'key' ]; then
       panos_url="${panos_url}?type=import"
@@ -96,6 +111,9 @@ deployer() {
       if [ "$_panos_template" ]; then
         content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl\"\r\n\r\n$_panos_template"
       fi
+      if [ "$_panos_vsys" ]; then
+        content="$content${nl}--$delim${nl}Content-Disposition: form-data; name=\"target-tpl-vsys\"\r\n\r\n$_panos_vsys"
+      fi
     fi
     #Close multipart
     content="$content${nl}--$delim--${nl}${nl}"
@@ -106,7 +124,6 @@ deployer() {
   # Commit changes
   if [ "$type" = 'commit' ]; then
     _debug "**** Committing changes ****"
-    export _H1="Content-Type: application/x-www-form-urlencoded"
     #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."
@@ -118,6 +135,20 @@ deployer() {
     content="type=commit&action=partial&key=$_panos_key&cmd=$cmd"
   fi
 
+  # Query job status
+  if [ "$type" = 'job_status' ]; then
+    echo "**** Querying job $_commit_job_id status ****"
+    cmd=$(printf "%s" "<show><jobs><id>$_commit_job_id</id></jobs></show>" | _url_encode)
+    content="type=op&key=$_panos_key&cmd=$cmd"
+  fi
+
+  # Push changes
+  if [ "$type" = 'push' ]; then
+    echo "**** Pushing changes ****"
+    cmd=$(printf "%s" "<commit-all><template-stack><name>$_panos_template_stack</name><admin><member>$_panos_user</member></admin></template-stack></commit-all>" | _url_encode)
+    content="type=commit&action=all&key=$_panos_key&cmd=$cmd"
+  fi
+
   response=$(_post "$content" "$panos_url" "" "POST")
   parse_response "$response" "$type"
   # Saving response to variables
@@ -126,6 +157,8 @@ deployer() {
   if [ "$response_status" = "success" ]; then
     _debug "Successfully deployed $type"
     return 0
+  elif [ "$_commit_job_status" ]; then
+    _debug "Commit Job Status = $_commit_job_status"
   else
     _err "Deploy of type $type failed. Try deploying with --debug to troubleshoot."
     _debug "$message"
@@ -191,11 +224,31 @@ panos_deploy() {
     _getdeployconf PANOS_TEMPLATE
   fi
 
+  # PANOS_TEMPLATE_STACK
+  if [ "$PANOS_TEMPLATE_STACK" ]; then
+    _debug "Detected ENV variable PANOS_TEMPLATE_STACK. Saving to file."
+    _savedeployconf PANOS_TEMPLATE_STACK "$PANOS_TEMPLATE_STACK" 1
+  else
+    _debug "Attempting to load variable PANOS_TEMPLATE_STACK from file."
+    _getdeployconf PANOS_TEMPLATE_STACK
+  fi
+
+  # PANOS_TEMPLATE_STACK
+  if [ "$PANOS_VSYS" ]; then
+    _debug "Detected ENV variable PANOS_VSYS. Saving to file."
+    _savedeployconf PANOS_VSYS "$PANOS_VSYS" 1
+  else
+    _debug "Attempting to load variable PANOS_VSYS from file."
+    _getdeployconf PANOS_VSYS
+  fi
+
   #Store variables
   _panos_host=$PANOS_HOST
   _panos_user=$PANOS_USER
   _panos_pass=$PANOS_PASS
   _panos_template=$PANOS_TEMPLATE
+  _panos_template_stack=$PANOS_TEMPLATE_STACK
+  _panos_vsys=$PANOS_VSYS
 
   #Test API Key if found.  If the key is invalid, the variable _panos_key will be unset.
   if [ "$_panos_host" ] && [ "$_panos_key" ]; then
@@ -229,6 +282,20 @@ panos_deploy() {
       deployer cert
       deployer key
       deployer commit
+      if [ "$_panos_template_stack" ]; then
+        # try to get job status for 20 times in 30 sec interval
+        i=0
+        while [ "$i" -lt $_COMMIT_WAIT_ITERATIONS ]; do
+          deployer job_status
+          if [ "$_commit_job_status" = "OK" ]; then
+            echo "Commit finished!"
+            break
+          fi
+          sleep $_COMMIT_WAIT_INTERVAL
+          i=$((i + 1))
+        done
+        deployer push
+      fi
     fi
   fi
 }

+ 11 - 1
deploy/proxmoxbs.sh

@@ -115,6 +115,16 @@ HEREDOC
   _info "Push certificates to server"
   export HTTPS_INSECURE=1
   export _H1="Authorization: PBSAPIToken=${_proxmoxbs_header_api_token}"
-  _post "$_json_payload" "$_target_url" "" POST "application/json"
+  response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
+  _retval=$?
+  if [ "${_retval}" -eq 0 ]; then
+    _debug3 response "$response"
+    _info "Certificate successfully deployed"
+    return 0
+  else
+    _err "Certificate deployment failed"
+    _debug "Response" "$response"
+    return 1
+  fi
 
 }

+ 11 - 1
deploy/proxmoxve.sh

@@ -127,6 +127,16 @@ HEREDOC
   _info "Push certificates to server"
   export HTTPS_INSECURE=1
   export _H1="Authorization: PVEAPIToken=${_proxmoxve_header_api_token}"
-  _post "$_json_payload" "$_target_url" "" POST "application/json"
+  response=$(_post "$_json_payload" "$_target_url" "" POST "application/json")
+  _retval=$?
+  if [ "${_retval}" -eq 0 ]; then
+    _debug3 response "$response"
+    _info "Certificate successfully deployed"
+    return 0
+  else
+    _err "Certificate deployment failed"
+    _debug "Response" "$response"
+    return 1
+  fi
 
 }

+ 1 - 1
dnsapi/dns_beget.sh

@@ -7,7 +7,7 @@ Options:
  BEGET_User API user
  BEGET_Password API password
 Issues: github.com/acmesh-official/acme.sh/issues/6200
-Author: ARNik [email protected]
+Author: ARNik <[email protected]>
 '
 
 Beget_Api="https://api.beget.com/api"

+ 1 - 1
dnsapi/dns_bookmyname.sh

@@ -7,7 +7,7 @@ Options:
  BOOKMYNAME_USERNAME Username
  BOOKMYNAME_PASSWORD Password
 Issues: github.com/acmesh-official/acme.sh/issues/3209
-Author: Neilpang
+Author: @Neilpang
 '
 
 ########  Public functions #####################

+ 4 - 1
dnsapi/dns_constellix.sh

@@ -117,7 +117,7 @@ dns_constellix_rm() {
 ####################  Private functions below ##################################
 
 _get_root() {
-  domain=$1
+  domain=$(echo "$1" | _lower_case)
   i=2
   p=1
   _debug "Detecting root zone"
@@ -156,6 +156,9 @@ _constellix_rest() {
   data="$3"
   _debug "$ep"
 
+  # Prevent rate limit
+  _sleep 2
+
   rdate=$(date +"%s")"000"
   hmac=$(printf "%s" "$rdate" | _hmac sha1 "$(printf "%s" "$CONSTELLIX_Secret" | _hex_dump | tr -d ' ')" | _base64)
 

+ 1 - 1
dnsapi/dns_ddnss.sh

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_ddnss
 Options:
  DDNSS_Token API Token
 Issues: github.com/acmesh-official/acme.sh/issues/2230
-Author: RaidenII, helbgd, mod242
+Author: @helbgd, @mod242
 '
 
 DDNSS_DNS_API="https://ddnss.de/upd.php"

+ 1 - 1
dnsapi/dns_dnshome.sh

@@ -7,7 +7,7 @@ Options:
  DNSHOME_Subdomain Subdomain
  DNSHOME_SubdomainPassword Subdomain Password
 Issues: github.com/acmesh-official/acme.sh/issues/3819
-Author: dnsHome.de https://github.com/dnsHome-de
+Author: @dnsHome-de
 '
 
 # Usage: add subdomain.ddnsdomain.tld "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"

+ 1 - 1
dnsapi/dns_duckdns.sh

@@ -5,7 +5,7 @@ Site: www.DuckDNS.org
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_duckdns
 Options:
  DuckDNS_Token API Token
-Author: RaidenII
+Author: @RaidenII
 '
 
 DuckDNS_API="https://www.duckdns.org/update"

+ 1 - 1
dnsapi/dns_dyn.sh

@@ -8,7 +8,7 @@ Options:
  DYN_Customer Customer
  DYN_Username API Username
  DYN_Password Secret
-Author: Gerd Naschenweng <https://github.com/magicdude4eva>
+Author: Gerd Naschenweng <@magicdude4eva>
 '
 
 # Dyn Managed DNS API

+ 1 - 1
dnsapi/dns_dynv6.sh

@@ -8,7 +8,7 @@ Options:
 OptionsAlt:
  KEY Path to SSH private key file. E.g. "/root/.ssh/dynv6"
 Issues: github.com/acmesh-official/acme.sh/issues/2702
-Author: StefanAbl
+Author: @StefanAbl
 '
 
 dynv6_api="https://dynv6.com/api/v2"

+ 1 - 1
dnsapi/dns_easydns.sh

@@ -7,7 +7,7 @@ Options:
  EASYDNS_Token API Token
  EASYDNS_Key API Key
 Issues: github.com/acmesh-official/acme.sh/issues/2647
-Author: Neilpang, wurzelpanzer <[email protected]>
+Author: @Neilpang, wurzelpanzer <[email protected]>
 '
 
 # API Documentation: https://sandbox.rest.easydns.net:3001/

+ 1 - 1
dnsapi/dns_fornex.sh

@@ -95,7 +95,7 @@ _get_root() {
       return 1
     fi
 
-    if ! _rest GET "dns/domain/"; then
+    if ! _rest GET "dns/domain/?q=$h"; then
       return 1
     fi
 

+ 1 - 1
dnsapi/dns_freedns.sh

@@ -7,7 +7,7 @@ Options:
  FREEDNS_User Username
  FREEDNS_Password Password
 Issues: github.com/acmesh-official/acme.sh/issues/2305
-Author: David Kerr <https://github.com/dkerr64>
+Author: David Kerr <@dkerr64>
 '
 
 ########  Public functions #####################

+ 1 - 0
dnsapi/dns_he_ddns.sh

@@ -5,6 +5,7 @@ Site: dns.he.net
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_he_ddns
 Options:
  HE_DDNS_KEY The DDNS key
+Issues: https://github.com/acmesh-official/acme.sh/issues/5238
 Author: Markku Leiniö
 '
 

+ 1 - 1
dnsapi/dns_joker.sh

@@ -7,7 +7,7 @@ Options:
  JOKER_USERNAME Username
  JOKER_PASSWORD Password
 Issues: github.com/acmesh-official/acme.sh/issues/2840
-Author: <https://github.com/aattww/>
+Author: @aattww
 '
 
 JOKER_API="https://svc.joker.com/nic/replace"

+ 76 - 20
dnsapi/dns_la.sh

@@ -1,14 +1,17 @@
 #!/usr/bin/env sh
+
+# LA_Id="123"
+# LA_Sk="456"
 # shellcheck disable=SC2034
 dns_la_info='dns.la
 Site: dns.la
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_la
 Options:
- LA_Id API ID
- LA_Key API key
+ LA_Id APIID
+ LA_Sk APISecret
+ LA_Token 用冒号连接 APIID APISecret 再base64生成
 Issues: github.com/acmesh-official/acme.sh/issues/4257
 '
-
 LA_Api="https://api.dns.la/api"
 
 ########  Public functions #####################
@@ -19,18 +22,23 @@ dns_la_add() {
   txtvalue=$2
 
   LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
-  LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
+  LA_Sk="${LA_Sk:-$(_readaccountconf_mutable LA_Sk)}"
+  _log "LA_Id=$LA_Id"
+  _log "LA_Sk=$LA_Sk"
 
-  if [ -z "$LA_Id" ] || [ -z "$LA_Key" ]; then
+  if [ -z "$LA_Id" ] || [ -z "$LA_Sk" ]; then
     LA_Id=""
-    LA_Key=""
+    LA_Sk=""
     _err "You didn't specify a dnsla api id and key yet."
     return 1
   fi
 
   #save the api key and email to the account conf file.
   _saveaccountconf_mutable LA_Id "$LA_Id"
-  _saveaccountconf_mutable LA_Key "$LA_Key"
+  _saveaccountconf_mutable LA_Sk "$LA_Sk"
+
+  # generate dnsla token
+  _la_token
 
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
@@ -42,11 +50,13 @@ dns_la_add() {
   _debug _domain "$_domain"
 
   _info "Adding record"
-  if _la_rest "record.ashx?cmd=create&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue&recordline="; then
-    if _contains "$response" '"resultid":'; then
+
+  # record type is enum in new api, 16 for TXT
+  if _la_post "{\"domainId\":\"$_domain_id\",\"type\":16,\"host\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"ttl\":600}" "record"; then
+    if _contains "$response" '"id":'; then
       _info "Added, OK"
       return 0
-    elif _contains "$response" '"code":532'; then
+    elif _contains "$response" '"msg":"与已有记录冲突"'; then
       _info "Already exists, OK"
       return 0
     else
@@ -54,7 +64,7 @@ dns_la_add() {
       return 1
     fi
   fi
-  _err "Add txt record error."
+  _err "Add txt record failed."
   return 1
 
 }
@@ -65,7 +75,9 @@ dns_la_rm() {
   txtvalue=$2
 
   LA_Id="${LA_Id:-$(_readaccountconf_mutable LA_Id)}"
-  LA_Key="${LA_Key:-$(_readaccountconf_mutable LA_Key)}"
+  LA_Sk="${LA_Sk:-$(_readaccountconf_mutable LA_Sk)}"
+
+  _la_token
 
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
@@ -77,27 +89,29 @@ dns_la_rm() {
   _debug _domain "$_domain"
 
   _debug "Getting txt records"
-  if ! _la_rest "record.ashx?cmd=listn&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&host=$_sub_domain&recordtype=TXT&recorddata=$txtvalue"; then
+  # record type is enum in new api, 16 for TXT
+  if ! _la_get "recordList?pageIndex=1&pageSize=10&domainId=$_domain_id&host=$_sub_domain&type=16&data=$txtvalue"; then
     _err "Error"
     return 1
   fi
 
-  if ! _contains "$response" '"recordid":'; then
+  if ! _contains "$response" '"id":'; then
     _info "Don't need to remove."
     return 0
   fi
 
-  record_id=$(printf "%s" "$response" | grep '"recordid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
+  record_id=$(printf "%s" "$response" | grep '"id":' | _head_n 1 | sed 's/.*"id": *"\([^"]*\)".*/\1/')
   _debug "record_id" "$record_id"
   if [ -z "$record_id" ]; then
     _err "Can not get record id to remove."
     return 1
   fi
-  if ! _la_rest "record.ashx?cmd=remove&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domainid=$_domain_id&domain=$_domain&recordid=$record_id"; then
+  # remove record in new api is RESTful
+  if ! _la_post "" "record?id=$record_id" "DELETE"; then
     _err "Delete record error."
     return 1
   fi
-  _contains "$response" '"code":300'
+  _contains "$response" '"code":200'
 
 }
 
@@ -119,12 +133,13 @@ _get_root() {
       return 1
     fi
 
-    if ! _la_rest "domain.ashx?cmd=get&apiid=$LA_Id&apipass=$LA_Key&rtype=json&domain=$h"; then
+    if ! _la_get "domain?domain=$h"; then
       return 1
     fi
 
-    if _contains "$response" '"domainid":'; then
-      _domain_id=$(printf "%s" "$response" | grep '"domainid":' | cut -d : -f 2 | cut -d , -f 1 | tr -d '\r' | tr -d '\n')
+    if _contains "$response" '"domain":'; then
+      _domain_id=$(echo "$response" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p')
+      _log "_domain_id" "$_domain_id"
       if [ "$_domain_id" ]; then
         _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
         _domain="$h"
@@ -143,6 +158,21 @@ _la_rest() {
   url="$LA_Api/$1"
   _debug "$url"
 
+  if ! response="$(_get "$url" "Authorization: Basic $LA_Token" | tr -d ' ' | tr "}" ",")"; then
+    _err "Error: $url"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+_la_get() {
+  url="$LA_Api/$1"
+  _debug "$url"
+
+  export _H1="Authorization: Basic $LA_Token"
+
   if ! response="$(_get "$url" | tr -d ' ' | tr "}" ",")"; then
     _err "Error: $url"
     return 1
@@ -151,3 +181,29 @@ _la_rest() {
   _debug2 response "$response"
   return 0
 }
+
+# Usage:  _la_post body url [POST|PUT|DELETE]
+_la_post() {
+  body=$1
+  url="$LA_Api/$2"
+  http_method=$3
+  _debug "$body"
+  _debug "$url"
+
+  export _H1="Authorization: Basic $LA_Token"
+
+  if ! response="$(_post "$body" "$url" "" "$http_method")"; then
+    _err "Error: $url"
+    return 1
+  fi
+
+  _debug2 response "$response"
+  return 0
+}
+
+_la_token() {
+  LA_Token=$(printf "%s:%s" "$LA_Id" "$LA_Sk" | _base64)
+  _debug "$LA_Token"
+
+  return 0
+}

+ 4 - 5
dnsapi/dns_mijnhost.sh

@@ -1,16 +1,15 @@
 #!/usr/bin/env sh
 # shellcheck disable=SC2034
 dns_mijnhost_info='mijn.host
-Domains: mijn.host
 Site: mijn.host
-Docs: https://mijn.host/api/doc/
-Issues: https://github.com/acmesh-official/acme.sh/issues/6177
-Author: peterv99
+Docs: https://github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_mijnhost
 Options:
  MIJNHOST_API_KEY API Key
+Issues: github.com/acmesh-official/acme.sh/issues/6177
+Author: @peterv99
 '
 
-########  Public functions ###################### Constants for your mijn-host API
+########  Public functions ######################
 MIJNHOST_API="https://mijn.host/api/v2"
 
 # Add TXT record for domain verification

+ 1 - 1
dnsapi/dns_mydnsjp.sh

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_mydnsjp
 Options:
  MYDNSJP_MasterID Master ID
  MYDNSJP_Password Password
-Author: epgdatacapbon
+Author: @tkmsst
 '
 
 ########  Public functions #####################

+ 1 - 1
dnsapi/dns_namecom.sh

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namecom
 Options:
  Namecom_Username Username
  Namecom_Token API Token
-Author: RaidenII
+Author: @RaidenII
 '
 
 ########  Public functions #####################

+ 1 - 1
dnsapi/dns_namesilo.sh

@@ -5,7 +5,7 @@ Site: NameSilo.com
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_namesilo
 Options:
  Namesilo_Key API Key
-Author: meowthink
+Author: @meowthink
 '
 
 #Utilize API to finish dns-01 verifications.

+ 186 - 0
dnsapi/dns_openprovider_rest.sh

@@ -0,0 +1,186 @@
+#!/usr/bin/env sh
+# shellcheck disable=SC2034
+dns_openprovider_rest_info='OpenProvider (REST)
+Domains: OpenProvider.com
+Site: OpenProvider.eu
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_openprovider_rest
+Options:
+ OPENPROVIDER_REST_USERNAME Openprovider Account Username
+ OPENPROVIDER_REST_PASSWORD Openprovider Account Password
+Issues: github.com/acmesh-official/acme.sh/issues/6122
+Author: Lambiek12
+'
+
+OPENPROVIDER_API_URL="https://api.openprovider.eu/v1beta"
+
+########  Public functions #####################
+
+# Usage: add  _acme-challenge.www.domain.com  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to add txt record
+dns_openprovider_rest_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _openprovider_prepare_credentials || return 1
+
+  _debug "Try fetch OpenProvider DNS zone details"
+  if ! _get_dns_zone "$fulldomain"; then
+    _err "DNS zone not found within configured OpenProvider account."
+    return 1
+  fi
+
+  if [ -n "$_domain_id" ]; then
+    addzonerecordrequestparameters="dns/zones/$_domain_name"
+    addzonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"add\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"$txtvalue\"}]}}"
+
+    if _openprovider_rest PUT "$addzonerecordrequestparameters" "$addzonerecordrequestbody"; then
+      if _contains "$response" "\"success\":true"; then
+        return 0
+      elif _contains "$response" "\"Duplicate record\""; then
+        _debug "Record already existed"
+        return 0
+      else
+        _err "Adding TXT record failed due to errors."
+        return 1
+      fi
+    fi
+  fi
+
+  _err "Adding TXT record failed due to errors."
+  return 1
+}
+
+# Usage: rm  _acme-challenge.www.domain.com  "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+# Used to remove the txt record after validation
+dns_openprovider_rest_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _openprovider_prepare_credentials || return 1
+
+  _debug "Try fetch OpenProvider DNS zone details"
+  if ! _get_dns_zone "$fulldomain"; then
+    _err "DNS zone not found within configured OpenProvider account."
+    return 1
+  fi
+
+  if [ -n "$_domain_id" ]; then
+    removezonerecordrequestparameters="dns/zones/$_domain_name"
+    removezonerecordrequestbody="{\"id\":$_domain_id,\"name\":\"$_domain_name\",\"records\":{\"remove\":[{\"name\":\"$_sub_domain\",\"ttl\":900,\"type\":\"TXT\",\"value\":\"\\\"$txtvalue\\\"\"}]}}"
+
+    if _openprovider_rest PUT "$removezonerecordrequestparameters" "$removezonerecordrequestbody"; then
+      if _contains "$response" "\"success\":true"; then
+        return 0
+      else
+        _err "Removing TXT record failed due to errors."
+        return 1
+      fi
+    fi
+  fi
+
+  _err "Removing TXT record failed due to errors."
+  return 1
+}
+
+####################  OpenProvider API common functions  ####################
+_openprovider_prepare_credentials() {
+  OPENPROVIDER_REST_USERNAME="${OPENPROVIDER_REST_USERNAME:-$(_readaccountconf_mutable OPENPROVIDER_REST_USERNAME)}"
+  OPENPROVIDER_REST_PASSWORD="${OPENPROVIDER_REST_PASSWORD:-$(_readaccountconf_mutable OPENPROVIDER_REST_PASSWORD)}"
+
+  if [ -z "$OPENPROVIDER_REST_USERNAME" ] || [ -z "$OPENPROVIDER_REST_PASSWORD" ]; then
+    OPENPROVIDER_REST_USERNAME=""
+    OPENPROVIDER_REST_PASSWORD=""
+    _err "You didn't specify the Openprovider username or password yet."
+    return 1
+  fi
+
+  #save the credentials to the account conf file.
+  _saveaccountconf_mutable OPENPROVIDER_REST_USERNAME "$OPENPROVIDER_REST_USERNAME"
+  _saveaccountconf_mutable OPENPROVIDER_REST_PASSWORD "$OPENPROVIDER_REST_PASSWORD"
+}
+
+_openprovider_rest() {
+  httpmethod=$1
+  queryparameters=$2
+  requestbody=$3
+
+  _openprovider_rest_login
+  if [ -z "$openproviderauthtoken" ]; then
+    _err "Unable to fetch authentication token from Openprovider API."
+    return 1
+  fi
+
+  export _H1="Content-Type: application/json"
+  export _H2="Accept: application/json"
+  export _H3="Authorization: Bearer $openproviderauthtoken"
+
+  if [ "$httpmethod" != "GET" ]; then
+    response="$(_post "$requestbody" "$OPENPROVIDER_API_URL/$queryparameters" "" "$httpmethod")"
+  else
+    response="$(_get "$OPENPROVIDER_API_URL/$queryparameters")"
+  fi
+
+  if [ "$?" != "0" ]; then
+    _err "No valid parameters supplied for Openprovider API: Error $queryparameters"
+    return 1
+  fi
+
+  _debug2 response "$response"
+
+  return 0
+}
+
+_openprovider_rest_login() {
+  export _H1="Content-Type: application/json"
+  export _H2="Accept: application/json"
+
+  loginrequesturl="$OPENPROVIDER_API_URL/auth/login"
+  loginrequestbody="{\"ip\":\"0.0.0.0\",\"password\":\"$OPENPROVIDER_REST_PASSWORD\",\"username\":\"$OPENPROVIDER_REST_USERNAME\"}"
+  loginresponse="$(_post "$loginrequestbody" "$loginrequesturl" "" "POST")"
+
+  openproviderauthtoken="$(printf "%s\n" "$loginresponse" | _egrep_o '"token" *: *"[^"]*' | _head_n 1 | sed 's#^"token" *: *"##')"
+
+  export openproviderauthtoken
+}
+
+####################  Private functions ##################################
+
+# Usage: _get_dns_zone _acme-challenge.www.domain.com
+# Returns:
+# _domain_id=123456789
+# _domain_name=domain.com
+# _sub_domain=_acme-challenge.www
+_get_dns_zone() {
+  domain=$1
+  i=1
+  p=1
+
+  while true; do
+    h=$(printf "%s" "$domain" | cut -d . -f "$i"-100)
+    if [ -z "$h" ]; then
+      # Empty value not allowed
+      return 1
+    fi
+
+    if ! _openprovider_rest GET "dns/zones/$h" ""; then
+      return 1
+    fi
+
+    if _contains "$response" "\"name\":\"$h\""; then
+      _domain_id="$(printf "%s\n" "$response" | _egrep_o '"id" *: *[^,]*' | _head_n 1 | sed 's#^"id" *: *##')"
+      _debug _domain_id "$_domain_id"
+
+      _domain_name="$h"
+      _debug _domain_name "$_domain_name"
+
+      _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-"$p")
+      _debug _sub_domain "$_sub_domain"
+      return 0
+    fi
+
+    p=$i
+    i=$(_math "$i" + 1)
+  done
+
+  return 1
+}

+ 1 - 1
dnsapi/dns_pleskxml.sh

@@ -8,7 +8,7 @@ Options:
  pleskxml_user Username
  pleskxml_pass Password
 Issues: github.com/acmesh-official/acme.sh/issues/2577
-Author: Stilez, <https://github.com/romanlum>
+Author: @Stilez, @romanlum
 '
 
 ##  Plesk XML API described at:

+ 15 - 3
dnsapi/dns_rage4.sh

@@ -42,6 +42,14 @@ dns_rage4_add() {
   _debug _domain_id "$_domain_id"
 
   _rage4_rest "createrecord/?id=$_domain_id&name=$fulldomain&content=$unquotedtxtvalue&type=TXT&active=true&ttl=1"
+
+  # Response after adding a TXT record should be something like this:
+  # {"status":true,"id":28160443,"error":null}
+  if ! _contains "$response" '"error":null' >/dev/null; then
+    _err "Error while adding TXT record: '$response'"
+    return 1
+  fi
+
   return 0
 }
 
@@ -63,7 +71,12 @@ dns_rage4_rm() {
   _debug "Getting txt records"
   _rage4_rest "getrecords/?id=${_domain_id}"
 
-  _record_id=$(echo "$response" | sed -rn 's/.*"id":([[:digit:]]+)[^\}]*'"$txtvalue"'.*/\1/p')
+  _record_id=$(echo "$response" | tr '{' '\n' | grep '"TXT"' | grep "\"$txtvalue" | sed -rn 's/.*"id":([[:digit:]]+),.*/\1/p')
+  if [ -z "$_record_id" ]; then
+    _err "error retrieving the record_id of the new TXT record in order to delete it, got: '$_record_id'."
+    return 1
+  fi
+
   _rage4_rest "deleterecord/?id=${_record_id}"
   return 0
 }
@@ -105,8 +118,7 @@ _rage4_rest() {
   token_trimmed=$(echo "$RAGE4_TOKEN" | tr -d '"')
   auth=$(printf '%s:%s' "$username_trimmed" "$token_trimmed" | _base64)
 
-  export _H1="Content-Type: application/json"
-  export _H2="Authorization: Basic $auth"
+  export _H1="Authorization: Basic $auth"
 
   response="$(_get "$RAGE4_Api$ep")"
 

+ 1 - 1
dnsapi/dns_schlundtech.sh

@@ -7,7 +7,7 @@ Options:
  SCHLUNDTECH_USER Username
  SCHLUNDTECH_PASSWORD Password
 Issues: github.com/acmesh-official/acme.sh/issues/2246
-Author: <https://github.com/mod242>
+Author: @mod242
 '
 
 SCHLUNDTECH_API="https://gateway.schlundtech.de"

+ 16 - 22
dnsapi/dns_selectel.sh

@@ -1,27 +1,21 @@
 #!/usr/bin/env sh
 # shellcheck disable=SC2034
-
-# dns_selectel_info='Selectel.com
-# Domains: Selectel.ru
-# Site: Selectel.com
-# Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel
-# Options:
-# Variables that must be defined before running
-#   SL_Ver can take one of the values 'v1' or 'v2', default is 'v1'
-#   SL_Ver='v1', when using version API legacy (v1)
-#   SL_Ver='v2', when using version API actual (v2)
-# when using API version v1, i.e. SL_Ver is 'v1' or not defined:
-#   SL_Key - API Key, required
-# when using API version v2:
-#   SL_Ver          - required as 'v2'
-#   SL_Login_ID     - account ID, required
-#   SL_Project_Name - name project, required
-#   SL_Login_Name   - service user name, required
-#   SL_Pswd         - service user password, required
-#   SL_Expire       - token lifetime in minutes (0-1440), default 1400 minutes
-#
-# Issues: github.com/acmesh-official/acme.sh/issues/5126
-#
+dns_selectel_info='Selectel.com
+Domains: Selectel.ru
+Site: Selectel.com
+Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_selectel
+Options: For old API version v1 (deprecated)
+   SL_Ver API version. Use "v1".
+   SL_Key API Key
+OptionsAlt: For the current API version v2
+   SL_Ver API version. Use "v2".
+   SL_Login_ID Account ID
+   SL_Project_Name Project name
+   SL_Login_Name Service user name
+   SL_Pswd Service user password
+   SL_Expire Token lifetime. In minutes (0-1440). Default "1400"
+Issues: github.com/acmesh-official/acme.sh/issues/5126
+'
 
 SL_Api="https://api.selectel.ru/domains"
 auth_uri="https://cloud.api.selcloud.ru/identity/v3/auth/tokens"

+ 4 - 4
dnsapi/dns_spaceship.sh

@@ -4,11 +4,11 @@ dns_spaceship_info='Spaceship.com
 Site: Spaceship.com
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_spaceship
 Options:
- SPACESHIP_API_KEY Spaceship API Key
- SPACESHIP_API_SECRET Spaceship API Secret
- SPACESHIP_ROOT_DOMAIN (Optional) Manually specify the root domain if auto-detection fails
+ SPACESHIP_API_KEY API Key
+ SPACESHIP_API_SECRET API Secret
+ SPACESHIP_ROOT_DOMAIN Root domain. Manually specify the root domain if auto-detection fails. Optional.
 Issues: github.com/acmesh-official/acme.sh/issues/6304
-Author: Meow <https://github.com/Meo597>
+Author: Meow <@Meo597>
 '
 
 # Spaceship API

+ 1 - 1
dnsapi/dns_tele3.sh

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#tele3
 Options:
  TELE3_Key API Key
  TELE3_Secret API Secret
-Author: Roman Blizik <https://github.com/par-pa>
+Author: Roman Blizik <@par-pa>
 '
 
 TELE3_API="https://www.tele3.cz/acme/"

+ 1 - 1
dnsapi/dns_timeweb.sh

@@ -6,7 +6,7 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_timeweb
 Options:
  TW_Token API JWT token. Get it from the control panel at https://timeweb.cloud/my/api-keys
 Issues: github.com/acmesh-official/acme.sh/issues/5140
-Author: Nikolay Pronchev <https://github.com/nikolaypronchev>
+Author: Nikolay Pronchev <@nikolaypronchev>
 '
 
 TW_Api="https://api.timeweb.cloud/api/v1"

+ 1 - 1
dnsapi/dns_udr.sh

@@ -7,7 +7,7 @@ Options:
  UDR_USER Username
  UDR_PASS Password
 Issues: github.com/acmesh-official/acme.sh/issues/3923
-Author: Andreas Scherer <https://github.com/andischerer>
+Author: Andreas Scherer <@andischerer>
 '
 
 UDR_API="https://api.domainreselling.de/api/call.cgi"

+ 1 - 1
dnsapi/dns_variomedia.sh

@@ -74,7 +74,7 @@ dns_variomedia_rm() {
     return 1
   fi
 
-  _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 ' ')"
+  _record_id="$(echo "$response" | sed -E 's/,"tags":\[[^]]*\]//g' | cut -d '[' -f3 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep -i "$_sub_domain" | grep -- "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')"
   _debug _record_id "$_record_id"
   if [ "$_record_id" ]; then
     _info "Successfully retrieved the record id for ACME challenge."

+ 1 - 1
dnsapi/dns_vscale.sh

@@ -5,7 +5,7 @@ Site: vscale.io
 Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_vscale
 Options:
  VSCALE_API_KEY API Key
-Author: Alex Loban <https://github.com/LAV45>
+Author: Alex Loban <@LAV45>
 '
 
 VSCALE_API_URL="https://api.vscale.io/v1"

+ 0 - 1
dnsapi/dns_vultr.sh

@@ -6,7 +6,6 @@ Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi2#dns_vultr
 Options:
  VULTR_API_KEY API Key
 Issues: github.com/acmesh-official/acme.sh/issues/2374
-Author:
 '
 
 VULTR_Api="https://api.vultr.com/v2"

+ 1 - 1
dnsapi/dns_websupport.sh

@@ -7,7 +7,7 @@ Options:
  WS_ApiKey API Key. Called "Identifier" in the WS Admin
  WS_ApiSecret API Secret. Called "Secret key" in the WS Admin
 Issues: github.com/acmesh-official/acme.sh/issues/3486
-Author: trgo.sk <https://github.com/trgosk>, akulumbeg <https://github.com/akulumbeg>
+Author: trgo.sk <@trgosk>, @akulumbeg
 '
 
 # Requirements: API Key and Secret from https://admin.websupport.sk/en/auth/apiKey

+ 1 - 1
dnsapi/dns_world4you.sh

@@ -7,7 +7,7 @@ Options:
  WORLD4YOU_USERNAME Username
  WORLD4YOU_PASSWORD Password
 Issues: github.com/acmesh-official/acme.sh/issues/3269
-Author: Lorenz Stechauner <https://www.github.com/NerLOR>
+Author: Lorenz Stechauner <@NerLOR>
 '
 
 WORLD4YOU_API="https://my.world4you.com/en"

+ 2 - 2
notify/telegram.sh

@@ -34,8 +34,8 @@ telegram_send() {
   fi
   _saveaccountconf_mutable TELEGRAM_BOT_URLBASE "$TELEGRAM_BOT_URLBASE"
 
-  _subject="$(printf "%s" "$_subject" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+--=|{}.!]\)/\\\\\1/g')"
-  _content="$(printf "%s" "$_content" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+--=|{}.!]\)/\\\\\1/g')"
+  _subject="$(printf "%s" "$_subject" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+\-=|{}.!]\)/\\\\\1/g')"
+  _content="$(printf "%s" "$_content" | sed 's/\\/\\\\\\\\/g' | sed 's/\]/\\\\\]/g' | sed 's/\([_*[()~`>#+\-=|{}.!]\)/\\\\\1/g')"
   _content="$(printf "*%s*\n%s" "$_subject" "$_content" | _json_encode)"
   _data="{\"text\": \"$_content\", "
   _data="$_data\"chat_id\": \"$TELEGRAM_BOT_CHATID\", "