Browse Source

Merge branch 'dev' of https://github.com/Neilpang/acme.sh

Boyan Peychev 7 years ago
parent
commit
94b925f5ef
11 changed files with 263 additions and 148 deletions
  1. 16 13
      README.md
  2. 4 0
      acme.sh
  3. 13 1
      dnsapi/README.md
  4. 31 16
      dnsapi/dns_ali.sh
  5. 2 2
      dnsapi/dns_aws.sh
  6. 0 1
      dnsapi/dns_cx.sh
  7. 10 72
      dnsapi/dns_dp.sh
  8. 97 0
      dnsapi/dns_dreamhost.sh
  9. 64 3
      dnsapi/dns_gd.sh
  10. 14 36
      dnsapi/dns_lua.sh
  11. 12 4
      dnsapi/dns_ovh.sh

+ 16 - 13
README.md

@@ -74,7 +74,7 @@ https://github.com/Neilpang/acmetest
 - Webroot mode
 - Webroot mode
 - Standalone mode
 - Standalone mode
 - Apache mode
 - Apache mode
-- Nginx mode ( Beta )
+- Nginx mode
 - DNS mode
 - DNS mode
 - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 - [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode)
 
 
@@ -238,7 +238,7 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 
 
 If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
 If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`.
 
 
-Particularly, if you are running an Apache server, you should use Apache mode instead. This mode doesn't write any files to your web root folder.
+Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder.
 
 
 Just set string "apache" as the second argument and it will force use of apache plugin automatically.
 Just set string "apache" as the second argument and it will force use of apache plugin automatically.
 
 
@@ -246,6 +246,10 @@ Just set string "apache" as the second argument and it will force use of apache
 acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
 acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com
 ```
 ```
 
 
+**This apache mode is only to issue the cert, it will not change your apache config files. 
+You will need to configure your website config files to use the cert by yourself.
+We don't want to mess your apache server, don't worry.**
+
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 
 
 # 7. Use Nginx mode
 # 7. Use Nginx mode
@@ -266,6 +270,10 @@ So, the config is not changed.
 acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
 acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com
 ```
 ```
 
 
+**This apache mode is only to issue the cert, it will not change your apache config files. 
+You will need to configure your website config files to use the cert by yourself.
+We don't want to mess your apache server, don't worry.**
+
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert
 
 
 # 8. Automatic DNS API integration
 # 8. Automatic DNS API integration
@@ -315,20 +323,15 @@ You don't have to do anything manually!
 1. Azure DNS
 1. Azure DNS
 1. selectel.com(selectel.ru) DNS API
 1. selectel.com(selectel.ru) DNS API
 1. zonomi.com DNS API
 1. zonomi.com DNS API
-
-
-
-
-
+1. DreamHost.com API
 
 
 
 
 And: 
 And: 
 
 
-1. lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
-   (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)
+**lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api
+   (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)**
 
 
 
 
-   
 **More APIs coming soon...**
 **More APIs coming soon...**
 
 
 If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
 If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project.
@@ -337,7 +340,7 @@ For more details: [How to use DNS API](dnsapi)
 
 
 # 9. Use DNS manual mode:
 # 9. Use DNS manual mode:
 
 
-If your dns provider doesn't support any api access, you will have to add the txt record by your hand.
+If your dns provider doesn't support any api access, you can add the txt record by your hand.
 
 
 ```bash
 ```bash
 acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
 acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com
@@ -375,7 +378,7 @@ Ok, it's done.
 
 
 And we support them too!
 And we support them too!
 
 
-Just set the `length` parameter with a prefix `ec-`.
+Just set the `keylength` parameter with a prefix `ec-`.
 
 
 For example:
 For example:
 
 
@@ -391,7 +394,7 @@ acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256
 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
 acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256
 ```
 ```
 
 
-Please look at the last parameter above.
+Please look at the `keylength` parameter above.
 
 
 Valid values are:
 Valid values are:
 
 

+ 4 - 0
acme.sh

@@ -3598,6 +3598,10 @@ $_authorizations_map"
       _debug entry "$entry"
       _debug entry "$entry"
       if [ -z "$entry" ]; then
       if [ -z "$entry" ]; then
         _err "Error, can not get domain token entry $d"
         _err "Error, can not get domain token entry $d"
+        _supported_vtypes="$(echo "$response" | _egrep_o "\"challenges\":\[[^]]*]" | tr '{' "\n" | grep type | cut -d '"' -f 4 | tr "\n" ' ')"
+        if [ "$_supported_vtypes" ]; then
+          _err "The supported validation types are: $_supported_vtypes, but you specified: $vtype"
+        fi
         _clearup
         _clearup
         _on_issue_err "$_post_hook"
         _on_issue_err "$_post_hook"
         return 1
         return 1

+ 13 - 1
dnsapi/README.md

@@ -515,7 +515,7 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com
 export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
 export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
 ```
 ```
 
 
-Please note that since DuckDNS uses StartSSL as their cert provider, thus 
+Please note that since DuckDNS uses StartSSL as their cert provider, thus
 --insecure may need to be used when issuing certs:
 --insecure may need to be used when issuing certs:
 ```
 ```
 acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
 acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
@@ -744,6 +744,18 @@ acme.sh --issue --dns dns_zonomi -d example.com -d www.example.com
 
 
 The `ZM_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 The `ZM_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
 
 
+## 40. Use DreamHost DNS API
+
+DNS API keys may be created at https://panel.dreamhost.com/?tree=home.api.
+Ensure the created key has add and remove privelages.
+
+```
+export DH_API_Key="<api key>"
+acme.sh --issue --dns dns_dreamhost -d example.com -d www.example.com
+```
+
+The 'DH_API_KEY' will be saved in `~/.acme.sh/account.conf` and will
+be reused when needed.
 
 
 # Use custom API
 # Use custom API
 
 

+ 31 - 16
dnsapi/dns_ali.sh

@@ -10,6 +10,8 @@ dns_ali_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
 
 
+  Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
+  Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
   if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
   if [ -z "$Ali_Key" ] || [ -z "$Ali_Secret" ]; then
     Ali_Key=""
     Ali_Key=""
     Ali_Secret=""
     Ali_Secret=""
@@ -18,8 +20,8 @@ dns_ali_add() {
   fi
   fi
 
 
   #save the api key and secret to the account conf file.
   #save the api key and secret to the account conf file.
-  _saveaccountconf Ali_Key "$Ali_Key"
-  _saveaccountconf Ali_Secret "$Ali_Secret"
+  _saveaccountconf_mutable Ali_Key "$Ali_Key"
+  _saveaccountconf_mutable Ali_Secret "$Ali_Secret"
 
 
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -32,6 +34,15 @@ dns_ali_add() {
 
 
 dns_ali_rm() {
 dns_ali_rm() {
   fulldomain=$1
   fulldomain=$1
+  txtvalue=$2
+  Ali_Key="${Ali_Key:-$(_readaccountconf_mutable Ali_Key)}"
+  Ali_Secret="${Ali_Secret:-$(_readaccountconf_mutable Ali_Secret)}"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    return 1
+  fi
+
   _clean
   _clean
 }
 }
 
 
@@ -76,16 +87,14 @@ _ali_rest() {
     return 1
     return 1
   fi
   fi
 
 
+  _debug2 response "$response"
   if [ -z "$2" ]; then
   if [ -z "$2" ]; then
-    message="$(printf "%s" "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
-    if [ -n "$message" ]; then
+    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+    if [ "$message" ]; then
       _err "$message"
       _err "$message"
       return 1
       return 1
     fi
     fi
   fi
   fi
-
-  _debug2 response "$response"
-  return 0
 }
 }
 
 
 _ali_urlencode() {
 _ali_urlencode() {
@@ -112,12 +121,14 @@ _ali_nonce() {
 }
 }
 
 
 _check_exist_query() {
 _check_exist_query() {
+  _qdomain="$1"
+  _qsubdomain="$2"
   query=''
   query=''
   query=$query'AccessKeyId='$Ali_Key
   query=$query'AccessKeyId='$Ali_Key
   query=$query'&Action=DescribeDomainRecords'
   query=$query'&Action=DescribeDomainRecords'
-  query=$query'&DomainName='$1
+  query=$query'&DomainName='$_qdomain
   query=$query'&Format=json'
   query=$query'&Format=json'
-  query=$query'&RRKeyWord=_acme-challenge'
+  query=$query'&RRKeyWord='$_qsubdomain
   query=$query'&SignatureMethod=HMAC-SHA1'
   query=$query'&SignatureMethod=HMAC-SHA1'
   query=$query"&SignatureNonce=$(_ali_nonce)"
   query=$query"&SignatureNonce=$(_ali_nonce)"
   query=$query'&SignatureVersion=1.0'
   query=$query'&SignatureVersion=1.0'
@@ -169,17 +180,21 @@ _describe_records_query() {
 }
 }
 
 
 _clean() {
 _clean() {
-  _check_exist_query "$_domain"
+  _check_exist_query "$_domain" "$_sub_domain"
   if ! _ali_rest "Check exist records" "ignore"; then
   if ! _ali_rest "Check exist records" "ignore"; then
     return 1
     return 1
   fi
   fi
 
 
-  records="$(echo "$response" -n | _egrep_o "\"RecordId\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
-  printf "%s" "$records" \
-    | while read -r record_id; do
-      _delete_record_query "$record_id"
-      _ali_rest "Delete record $record_id" "ignore"
-    done
+  record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)"
+  _debug2 record_id "$record_id"
+
+  if [ -z "$record_id" ]; then
+    _debug "record not found, skip"
+  else
+    _delete_record_query "$record_id"
+    _ali_rest "Delete record $record_id" "ignore"
+  fi
+
 }
 }
 
 
 _timestamp() {
 _timestamp() {

+ 2 - 2
dnsapi/dns_aws.sh

@@ -48,7 +48,7 @@ dns_aws_add() {
   fi
   fi
 
 
   if _contains "$response" "<Name>$fulldomain.</Name>"; then
   if _contains "$response" "<Name>$fulldomain.</Name>"; then
-    _resource_record="$(echo "$response" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
+    _resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
     _debug "_resource_record" "$_resource_record"
     _debug "_resource_record" "$_resource_record"
   else
   else
     _debug "single new add"
     _debug "single new add"
@@ -93,7 +93,7 @@ dns_aws_rm() {
   fi
   fi
 
 
   if _contains "$response" "<Name>$fulldomain.</Name>"; then
   if _contains "$response" "<Name>$fulldomain.</Name>"; then
-    _resource_record="$(echo "$response" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
+    _resource_record="$(echo "$response" | sed 's/<ResourceRecordSet>/"/g' | tr '"' "\n" | grep "<Name>$fulldomain.</Name>" | _egrep_o "<ResourceRecords.*</ResourceRecords>" | sed "s/<ResourceRecords>//" | sed "s#</ResourceRecords>##")"
     _debug "_resource_record" "$_resource_record"
     _debug "_resource_record" "$_resource_record"
   else
   else
     _debug "no records exists, skip"
     _debug "no records exists, skip"

+ 0 - 1
dnsapi/dns_cx.sh

@@ -62,7 +62,6 @@ existing_records() {
   _debug "Getting txt records"
   _debug "Getting txt records"
   root=$1
   root=$1
   sub=$2
   sub=$2
-  count=0
   if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
   if ! _rest GET "record/$_domain_id?:domain_id?host_id=0&offset=0&row_num=100"; then
     return 1
     return 1
   fi
   fi

+ 10 - 72
dnsapi/dns_dp.sh

@@ -15,6 +15,8 @@ dns_dp_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
 
 
+  DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
+  DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
   if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
   if [ -z "$DP_Id" ] || [ -z "$DP_Key" ]; then
     DP_Id=""
     DP_Id=""
     DP_Key=""
     DP_Key=""
@@ -24,8 +26,8 @@ dns_dp_add() {
   fi
   fi
 
 
   #save the api key and email to the account conf file.
   #save the api key and email to the account conf file.
-  _saveaccountconf DP_Id "$DP_Id"
-  _saveaccountconf DP_Key "$DP_Key"
+  _saveaccountconf_mutable DP_Id "$DP_Id"
+  _saveaccountconf_mutable DP_Key "$DP_Key"
 
 
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -33,24 +35,18 @@ dns_dp_add() {
     return 1
     return 1
   fi
   fi
 
 
-  existing_records "$_domain" "$_sub_domain"
-  _debug count "$count"
-  if [ "$?" != "0" ]; then
-    _err "Error get existing records."
-    return 1
-  fi
+  add_record "$_domain" "$_sub_domain" "$txtvalue"
 
 
-  if [ "$count" = "0" ]; then
-    add_record "$_domain" "$_sub_domain" "$txtvalue"
-  else
-    update_record "$_domain" "$_sub_domain" "$txtvalue"
-  fi
 }
 }
 
 
 #fulldomain txtvalue
 #fulldomain txtvalue
 dns_dp_rm() {
 dns_dp_rm() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
+
+  DP_Id="${DP_Id:-$(_readaccountconf_mutable DP_Id)}"
+  DP_Key="${DP_Key:-$(_readaccountconf_mutable DP_Key)}"
+
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
     _err "invalid domain"
     _err "invalid domain"
@@ -83,37 +79,6 @@ dns_dp_rm() {
 
 
 }
 }
 
 
-#usage:  root  sub
-#return if the sub record already exists.
-#echos the existing records count.
-# '0' means doesn't exist
-existing_records() {
-  _debug "Getting txt records"
-  root=$1
-  sub=$2
-
-  if ! _rest POST "Record.List" "login_token=$DP_Id,$DP_Key&domain_id=$_domain_id&sub_domain=$_sub_domain"; then
-    return 1
-  fi
-
-  if _contains "$response" 'No records'; then
-    count=0
-    return 0
-  fi
-
-  if _contains "$response" "Action completed successful"; then
-    count=$(printf "%s" "$response" | grep -c '<type>TXT</type>' | tr -d ' ')
-    record_id=$(printf "%s" "$response" | grep '^<id>' | tail -1 | cut -d '>' -f 2 | cut -d '<' -f 1)
-    _debug record_id "$record_id"
-    return 0
-  else
-    _err "get existing records error."
-    return 1
-  fi
-
-  count=0
-}
-
 #add the txt record.
 #add the txt record.
 #usage: root  sub  txtvalue
 #usage: root  sub  txtvalue
 add_record() {
 add_record() {
@@ -128,34 +93,7 @@ add_record() {
     return 1
     return 1
   fi
   fi
 
 
-  if _contains "$response" "Action completed successful"; then
-
-    return 0
-  fi
-
-  return 1 #error
-}
-
-#update the txt record
-#Usage: root sub txtvalue
-update_record() {
-  root=$1
-  sub=$2
-  txtvalue=$3
-  fulldomain="$sub.$root"
-
-  _info "Updating record"
-
-  if ! _rest POST "Record.Modify" "login_token=$DP_Id,$DP_Key&format=json&domain_id=$_domain_id&sub_domain=$_sub_domain&record_type=TXT&value=$txtvalue&record_line=默认&record_id=$record_id"; then
-    return 1
-  fi
-
-  if _contains "$response" "Action completed successful"; then
-
-    return 0
-  fi
-
-  return 1 #error
+  _contains "$response" "Action completed successful" || _contains "$response" "Domain record already exists"
 }
 }
 
 
 ####################  Private functions below ##################################
 ####################  Private functions below ##################################

+ 97 - 0
dnsapi/dns_dreamhost.sh

@@ -0,0 +1,97 @@
+#!/usr/bin/env sh
+
+#Author: RhinoLance
+#Report Bugs here: https://github.com/RhinoLance/acme.sh
+#
+
+#define the api endpoint
+DH_API_ENDPOINT="https://api.dreamhost.com/"
+querystring=""
+
+########  Public functions #####################
+
+#Usage: dns_myapi_add   _acme-challenge.www.domain.com   "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_dreamhost_add() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! validate "$fulldomain" "$txtvalue"; then
+    return 1
+  fi
+
+  querystring="key=$DH_API_KEY&cmd=dns-add_record&record=$fulldomain&type=TXT&value=$txtvalue"
+  if ! submit "$querystring"; then
+    return 1
+  fi
+
+  return 0
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_dreamhost_rm() {
+  fulldomain=$1
+  txtvalue=$2
+
+  if ! validate "$fulldomain" "$txtvalue"; then
+    return 1
+  fi
+
+  querystring="key=$DH_API_KEY&cmd=dns-remove_record&record=$fulldomain&type=TXT&value=$txtvalue"
+  if ! submit "$querystring"; then
+    return 1
+  fi
+
+  return 0
+}
+
+####################  Private functions below ##################################
+
+#send the command to the api endpoint.
+submit() {
+  querystring=$1
+
+  url="$DH_API_ENDPOINT?$querystring"
+
+  _debug url "$url"
+
+  if ! response="$(_get "$url")"; then
+    _err "Error <$1>"
+    return 1
+  fi
+
+  if [ -z "$2" ]; then
+    message="$(echo "$response" | _egrep_o "\"Message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \")"
+    if [ -n "$message" ]; then
+      _err "$message"
+      return 1
+    fi
+  fi
+
+  _debug response "$response"
+
+  return 0
+}
+
+#check that we have a valid API Key
+validate() {
+  fulldomain=$1
+  txtvalue=$2
+
+  _info "Using dreamhost"
+  _debug fulldomain "$fulldomain"
+  _debug txtvalue "$txtvalue"
+
+  #retrieve the API key from the environment variable if it exists, otherwise look for a saved key.
+  DH_API_KEY="${DH_API_KEY:-$(_readaccountconf_mutable DH_API_KEY)}"
+
+  if [ -z "$DH_API_KEY" ]; then
+    DH_API_KEY=""
+    _err "You didn't specify the DreamHost api key yet (export DH_API_KEY=\"<api key>\")"
+    _err "Please login to your control panel, create a key and try again."
+    return 1
+  fi
+
+  #save the api key to the account conf file.
+  _saveaccountconf_mutable DH_API_KEY "$DH_API_KEY"
+}

+ 64 - 3
dnsapi/dns_gd.sh

@@ -15,6 +15,8 @@ dns_gd_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
 
 
+  GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}"
+  GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}"
   if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
   if [ -z "$GD_Key" ] || [ -z "$GD_Secret" ]; then
     GD_Key=""
     GD_Key=""
     GD_Secret=""
     GD_Secret=""
@@ -24,8 +26,8 @@ dns_gd_add() {
   fi
   fi
 
 
   #save the api key and email to the account conf file.
   #save the api key and email to the account conf file.
-  _saveaccountconf GD_Key "$GD_Key"
-  _saveaccountconf GD_Secret "$GD_Secret"
+  _saveaccountconf_mutable GD_Key "$GD_Key"
+  _saveaccountconf_mutable GD_Secret "$GD_Secret"
 
 
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -36,8 +38,27 @@ dns_gd_add() {
   _debug _sub_domain "$_sub_domain"
   _debug _sub_domain "$_sub_domain"
   _debug _domain "$_domain"
   _debug _domain "$_domain"
 
 
+  _debug "Getting existing records"
+  if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
+    return 1
+  fi
+
+  if _contains "$response" "$txtvalue"; then
+    _info "The record is existing, skip"
+    return 0
+  fi
+
+  _add_data="{\"data\":\"$txtvalue\"}"
+  for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
+    _debug2 t "$t"
+    if [ "$t" ]; then
+      _add_data="$_add_data,{\"data\":$t}"
+    fi
+  done
+  _debug2 _add_data "$_add_data"
+
   _info "Adding record"
   _info "Adding record"
-  if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[{\"data\":\"$txtvalue\"}]"; then
+  if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
     if [ "$response" = "{}" ]; then
     if [ "$response" = "{}" ]; then
       _info "Added, sleeping 10 seconds"
       _info "Added, sleeping 10 seconds"
       _sleep 10
       _sleep 10
@@ -56,7 +77,47 @@ dns_gd_add() {
 #fulldomain
 #fulldomain
 dns_gd_rm() {
 dns_gd_rm() {
   fulldomain=$1
   fulldomain=$1
+  txtvalue=$2
+
+  GD_Key="${GD_Key:-$(_readaccountconf_mutable GD_Key)}"
+  GD_Secret="${GD_Secret:-$(_readaccountconf_mutable GD_Secret)}"
+
+  _debug "First detect the root zone"
+  if ! _get_root "$fulldomain"; then
+    _err "invalid domain"
+    return 1
+  fi
+
+  _debug _sub_domain "$_sub_domain"
+  _debug _domain "$_domain"
+
+  _debug "Getting existing records"
+  if ! _gd_rest GET "domains/$_domain/records/TXT/$_sub_domain"; then
+    return 1
+  fi
+
+  if ! _contains "$response" "$txtvalue"; then
+    _info "The record is not existing, skip"
+    return 0
+  fi
+
+  _add_data=""
+  for t in $(echo "$response" | tr '{' "\n" | grep "\"name\":\"$_sub_domain\"" | tr ',' "\n" | grep '"data"' | cut -d : -f 2); do
+    _debug2 t "$t"
+    if [ "$t" ] && [ "$t" != "\"$txtvalue\"" ]; then
+      if [ "$_add_data" ]; then
+        _add_data="$_add_data,{\"data\":$t}"
+      else
+        _add_data="{\"data\":$t}"
+      fi
+    fi
+  done
+  if [ -z "$_add_data" ]; then
+    _add_data="{\"data\":\"\"}"
+  fi
+  _debug2 _add_data "$_add_data"
 
 
+  _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"
 }
 }
 
 
 ####################  Private functions below ##################################
 ####################  Private functions below ##################################

+ 14 - 36
dnsapi/dns_lua.sh

@@ -17,6 +17,8 @@ dns_lua_add() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
 
 
+  LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+  LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
   if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
   if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
     LUA_Key=""
     LUA_Key=""
     LUA_Email=""
     LUA_Email=""
@@ -26,8 +28,8 @@ dns_lua_add() {
   fi
   fi
 
 
   #save the api key and email to the account conf file.
   #save the api key and email to the account conf file.
-  _saveaccountconf LUA_Key "$LUA_Key"
-  _saveaccountconf LUA_Email "$LUA_Email"
+  _saveaccountconf_mutable LUA_Key "$LUA_Key"
+  _saveaccountconf_mutable LUA_Email "$LUA_Email"
 
 
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
@@ -38,50 +40,26 @@ dns_lua_add() {
   _debug _sub_domain "$_sub_domain"
   _debug _sub_domain "$_sub_domain"
   _debug _domain "$_domain"
   _debug _domain "$_domain"
 
 
-  _debug "Getting txt records"
-  _LUA_rest GET "zones/${_domain_id}/records"
-
-  if ! _contains "$response" "\"id\":"; then
-    _err "Error"
-    return 1
-  fi
-
-  count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
-  _debug count "$count"
-  if [ "$count" = "0" ]; then
-    _info "Adding record"
-    if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
-      if _contains "$response" "$fulldomain"; then
-        _info "Added"
-        #todo: check if the record takes effect
-        return 0
-      else
-        _err "Add txt record error."
-        return 1
-      fi
-    fi
-    _err "Add txt record error."
-  else
-    _info "Updating record"
-    record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | _head_n 1 | cut -d: -f2 | cut -d, -f1)
-    _debug "record_id" "$record_id"
-
-    _LUA_rest PUT "zones/$_domain_id/records/$record_id" "{\"id\":$record_id,\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"zone_id\":$_domain_id,\"ttl\":120}"
-    if [ "$?" = "0" ] && _contains "$response" "updated_at"; then
-      _info "Updated!"
+  _info "Adding record"
+  if _LUA_rest POST "zones/$_domain_id/records" "{\"type\":\"TXT\",\"name\":\"$fulldomain.\",\"content\":\"$txtvalue\",\"ttl\":120}"; then
+    if _contains "$response" "$fulldomain"; then
+      _info "Added"
       #todo: check if the record takes effect
       #todo: check if the record takes effect
       return 0
       return 0
+    else
+      _err "Add txt record error."
+      return 1
     fi
     fi
-    _err "Update error"
-    return 1
   fi
   fi
-
 }
 }
 
 
 #fulldomain
 #fulldomain
 dns_lua_rm() {
 dns_lua_rm() {
   fulldomain=$1
   fulldomain=$1
   txtvalue=$2
   txtvalue=$2
+
+  LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+  LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
   _debug "First detect the root zone"
   _debug "First detect the root zone"
   if ! _get_root "$fulldomain"; then
   if ! _get_root "$fulldomain"; then
     _err "invalid domain"
     _err "invalid domain"

+ 12 - 4
dnsapi/dns_ovh.sh

@@ -79,6 +79,9 @@ _ovh_get_api() {
 }
 }
 
 
 _initAuth() {
 _initAuth() {
+  OVH_AK="${OVH_AK:-$(_readaccountconf_mutable OVH_AK)}"
+  OVH_AS="${OVH_AS:-$(_readaccountconf_mutable OVH_AS)}"
+
   if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
   if [ -z "$OVH_AK" ] || [ -z "$OVH_AS" ]; then
     OVH_AK=""
     OVH_AK=""
     OVH_AS=""
     OVH_AS=""
@@ -87,21 +90,26 @@ _initAuth() {
     return 1
     return 1
   fi
   fi
 
 
-  #save the api key and email to the account conf file.
-  _saveaccountconf OVH_AK "$OVH_AK"
-  _saveaccountconf OVH_AS "$OVH_AS"
+  if [ "$OVH_AK" != "$(_readaccountconf OVH_AK)" ]; then
+    _info "It seems that your ovh key is changed, let's clear consumer key first."
+    _clearaccountconf OVH_CK
+  fi
+  _saveaccountconf_mutable OVH_AK "$OVH_AK"
+  _saveaccountconf_mutable OVH_AS "$OVH_AS"
 
 
+  OVH_END_POINT="${OVH_END_POINT:-$(_readaccountconf_mutable OVH_END_POINT)}"
   if [ -z "$OVH_END_POINT" ]; then
   if [ -z "$OVH_END_POINT" ]; then
     OVH_END_POINT="ovh-eu"
     OVH_END_POINT="ovh-eu"
   fi
   fi
   _info "Using OVH endpoint: $OVH_END_POINT"
   _info "Using OVH endpoint: $OVH_END_POINT"
   if [ "$OVH_END_POINT" != "ovh-eu" ]; then
   if [ "$OVH_END_POINT" != "ovh-eu" ]; then
-    _saveaccountconf OVH_END_POINT "$OVH_END_POINT"
+    _saveaccountconf_mutable OVH_END_POINT "$OVH_END_POINT"
   fi
   fi
 
 
   OVH_API="$(_ovh_get_api $OVH_END_POINT)"
   OVH_API="$(_ovh_get_api $OVH_END_POINT)"
   _debug OVH_API "$OVH_API"
   _debug OVH_API "$OVH_API"
 
 
+  OVH_CK="${OVH_CK:-$(_readaccountconf_mutable OVH_CK)}"
   if [ -z "$OVH_CK" ]; then
   if [ -z "$OVH_CK" ]; then
     _info "OVH consumer key is empty, Let's get one:"
     _info "OVH consumer key is empty, Let's get one:"
     if ! _ovh_authentication; then
     if ! _ovh_authentication; then