/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")"
+ _debug3 "subdomain_csv: $subdomain_csv"
+
+ # The above beauty ends with striping out rows that do not have an
+ # href to edit.php and do not have the domain name we are looking for.
+ # So all we should be left with is CSV of table of subdomains we are
+ # interested in.
+
+ # Now we have to read through this table and extract the data we need
+ lines="$(echo "$subdomain_csv" | wc -l)"
+ i=0
+ found=0
+ DNSdataid=""
+ while [ "$i" -lt "$lines" ]; do
+ i="$(_math "$i" + 1)"
+ line="$(echo "$subdomain_csv" | sed -n "${i}p")"
+ _debug3 "line: $line"
+ DNSname="$(echo "$line" | _egrep_o 'edit.php.*' | cut -d '>' -f 2 | cut -d '<' -f 1)"
+ _debug2 "DNSname: $DNSname"
+ if [ "$DNSname" = "$fulldomain" ]; then
+ DNStype="$(echo "$line" | sed 's/' -f 2 | cut -d '<' -f 1)"
+ _debug2 "DNStype: $DNStype"
+ if [ "$DNStype" = "TXT" ]; then
+ DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)"
+ _debug2 "DNSdataid: $DNSdataid"
+ DNSvalue="$(echo "$line" | sed 's/ | ' -f 2 | cut -d '<' -f 1)"
+ if _startswith "$DNSvalue" """; then
+ # remove the quotation from the start
+ DNSvalue="$(echo "$DNSvalue" | cut -c 7-)"
+ fi
+ if _endswith "$DNSvalue" "..."; then
+ # value was truncated, remove the dot dot dot from the end
+ DNSvalue="$(echo "$DNSvalue" | sed 's/...$//')"
+ elif _endswith "$DNSvalue" """; then
+ # else remove the closing quotation from the end
+ DNSvalue="$(echo "$DNSvalue" | sed 's/......$//')"
+ fi
+ _debug2 "DNSvalue: $DNSvalue"
+
+ if [ -n "$DNSdataid" ] && _startswith "$txtvalue" "$DNSvalue"; then
+ # Found a match. But note... Website is truncating the
+ # value field so we are only testing that part that is not
+ # truncated. This should be accurate enough.
+ _debug "Deleting TXT record for $fulldomain, $txtvalue"
+ _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
+ return $?
+ fi
+
+ fi
+ fi
+ done
+ done
+
+ # If we get this far we did not find a match (after two attempts)
+ # Not necessarily an error, but log anyway.
+ _debug3 "$subdomain_csv"
+ _info "Cannot delete TXT record for $fulldomain, $txtvalue. Does not exist at FreeDNS"
+ return 0
+}
+
+#################### Private functions below ##################################
+
+# usage: _freedns_login username password
+# print string "cookie=value" etc.
+# returns 0 success
+_freedns_login() {
+ export _H1="Accept-Language:en-US"
+ username="$1"
+ password="$2"
+ url="https://freedns.afraid.org/zc.php?step=2"
+
+ _debug "Login to FreeDNS as user $username"
+
+ htmlpage="$(_post "username=$(printf '%s' "$username" | _url_encode)&password=$(printf '%s' "$password" | _url_encode)&submit=Login&action=auth" "$url")"
+
+ if [ "$?" != "0" ]; then
+ _err "FreeDNS login failed for user $username bad RC from _post"
+ return 1
+ fi
+
+ cookies="$(grep -i '^Set-Cookie.*dns_cookie.*$' "$HTTP_HEADER" | _head_n 1 | tr -d "\r\n" | cut -d " " -f 2)"
+
+ # if cookies is not empty then logon successful
+ if [ -z "$cookies" ]; then
+ _debug3 "htmlpage: $htmlpage"
+ _err "FreeDNS login failed for user $username. Check $HTTP_HEADER file"
+ return 1
+ fi
+
+ printf "%s" "$cookies"
+ return 0
+}
+
+# usage _freedns_retrieve_subdomain_page login_cookies
+# echo page retrieved (html)
+# returns 0 success
+_freedns_retrieve_subdomain_page() {
+ export _H1="Cookie:$1"
+ export _H2="Accept-Language:en-US"
+ url="https://freedns.afraid.org/subdomain/"
+
+ _debug "Retrieve subdomain page from FreeDNS"
+
+ htmlpage="$(_get "$url")"
+
+ if [ "$?" != "0" ]; then
+ _err "FreeDNS retrieve subdomains failed bad RC from _get"
+ return 1
+ elif [ -z "$htmlpage" ]; then
+ _err "FreeDNS returned empty subdomain page"
+ return 1
+ fi
+
+ _debug3 "htmlpage: $htmlpage"
+
+ printf "%s" "$htmlpage"
+ return 0
+}
+
+# usage _freedns_add_txt_record login_cookies domain_id subdomain value
+# returns 0 success
+_freedns_add_txt_record() {
+ export _H1="Cookie:$1"
+ export _H2="Accept-Language:en-US"
+ domain_id="$2"
+ subdomain="$3"
+ value="$(printf '%s' "$4" | _url_encode)"
+ url="http://freedns.afraid.org/subdomain/save.php?step=2"
+
+ htmlpage="$(_post "type=TXT&domain_id=$domain_id&subdomain=$subdomain&address=%22$value%22&send=Save%21" "$url")"
+
+ if [ "$?" != "0" ]; then
+ _err "FreeDNS failed to add TXT record for $subdomain bad RC from _post"
+ return 1
+ elif ! grep "200 OK" "$HTTP_HEADER" >/dev/null; then
+ _debug3 "htmlpage: $htmlpage"
+ _err "FreeDNS failed to add TXT record for $subdomain. Check $HTTP_HEADER file"
+ return 1
+ elif _contains "$htmlpage" "security code was incorrect"; then
+ _debug3 "htmlpage: $htmlpage"
+ _err "FreeDNS failed to add TXT record for $subdomain as FreeDNS requested security code"
+ _err "Note that you cannot use automatic DNS validation for FreeDNS public domains"
+ return 1
+ fi
+
+ _debug3 "htmlpage: $htmlpage"
+ _info "Added acme challenge TXT record for $fulldomain at FreeDNS"
+ return 0
+}
+
+# usage _freedns_delete_txt_record login_cookies data_id
+# returns 0 success
+_freedns_delete_txt_record() {
+ export _H1="Cookie:$1"
+ export _H2="Accept-Language:en-US"
+ data_id="$2"
+ url="https://freedns.afraid.org/subdomain/delete2.php"
+
+ htmlheader="$(_get "$url?data_id%5B%5D=$data_id&submit=delete+selected" "onlyheader")"
+
+ if [ "$?" != "0" ]; then
+ _err "FreeDNS failed to delete TXT record for $data_id bad RC from _get"
+ return 1
+ elif ! _contains "$htmlheader" "200 OK"; then
+ _debug2 "htmlheader: $htmlheader"
+ _err "FreeDNS failed to delete TXT record $data_id"
+ return 1
+ fi
+
+ _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS"
+ return 0
+}
diff --git a/dnsapi/dns_gandi_livedns.sh b/dnsapi/dns_gandi_livedns.sh
new file mode 100755
index 00000000..7a21aba6
--- /dev/null
+++ b/dnsapi/dns_gandi_livedns.sh
@@ -0,0 +1,123 @@
+#!/usr/bin/env sh
+
+# Gandi LiveDNS v5 API
+# http://doc.livedns.gandi.net/
+# currently under beta
+#
+# Requires GANDI API KEY set in GANDI_LIVEDNS_KEY set as environment variable
+#
+#Author: Frédéric Crozat
+#Report Bugs here: https://github.com/fcrozat/acme.sh
+#
+######## Public functions #####################
+
+GANDI_LIVEDNS_API="https://dns.api.gandi.net/api/v5"
+
+#Usage: dns_gandi_livedns_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_gandi_livedns_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$GANDI_LIVEDNS_KEY" ]; then
+ _err "No API key specified for Gandi LiveDNS."
+ _err "Create your key and export it as GANDI_LIVEDNS_KEY"
+ return 1
+ fi
+
+ _saveaccountconf GANDI_LIVEDNS_KEY "$GANDI_LIVEDNS_KEY"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _gandi_livedns_rest PUT "domains/$_domain/records/$_sub_domain/TXT" "{\"rrset_ttl\": 300, \"rrset_values\":[\"$txtvalue\"]}" \
+ && _contains "$response" '{"message": "DNS Record Created"}' \
+ && _info "Add $(__green "success")"
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_gandi_livedns_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+
+ _debug fulldomain "$fulldomain"
+ _debug domain "$_domain"
+ _debug sub_domain "$_sub_domain"
+
+ _gandi_livedns_rest DELETE "domains/$_domain/records/$_sub_domain/TXT" ""
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _gandi_livedns_rest GET "domains/$h"; then
+ return 1
+ fi
+
+ if _contains "$response" '"code": 401'; then
+ _err "$response"
+ return 1
+ elif _contains "$response" '"code": 404'; then
+ _debug "$h not found"
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_gandi_livedns_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Content-Type: application/json"
+ export _H2="X-Api-Key: $GANDI_LIVEDNS_KEY"
+
+ if [ "$m" = "GET" ]; then
+ response="$(_get "$GANDI_LIVEDNS_API/$ep")"
+ else
+ _debug data "$data"
+ response="$(_post "$data" "$GANDI_LIVEDNS_API/$ep" "" "$m")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_gd.sh b/dnsapi/dns_gd.sh
new file mode 100755
index 00000000..5fb1b174
--- /dev/null
+++ b/dnsapi/dns_gd.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env sh
+
+#Godaddy domain api
+#
+#GD_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#GD_Secret="asdfsdfsfsdfsdfdfsdf"
+
+GD_Api="https://api.godaddy.com/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_gd_add() {
+ fulldomain=$1
+ 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
+ GD_Key=""
+ GD_Secret=""
+ _err "You don't specify godaddy api key and secret yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable GD_Key "$GD_Key"
+ _saveaccountconf_mutable GD_Secret "$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 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"
+ if _gd_rest PUT "domains/$_domain/records/TXT/$_sub_domain" "[$_add_data]"; then
+ if [ "$response" = "{}" ]; then
+ _info "Added, sleeping 10 seconds"
+ _sleep 10
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ _err "$response"
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+
+}
+
+#fulldomain
+dns_gd_rm() {
+ 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 ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _gd_rest GET "domains/$h"; then
+ return 1
+ fi
+
+ if _contains "$response" '"code":"NOT_FOUND"'; then
+ _debug "$h not found"
+ else
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p="$i"
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_gd_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Authorization: sso-key $GD_Key:$GD_Secret"
+ export _H2="Content-Type: application/json"
+
+ if [ "$data" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$GD_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$GD_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh
new file mode 100755
index 00000000..f42d56af
--- /dev/null
+++ b/dnsapi/dns_he.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env sh
+
+########################################################################
+# Hurricane Electric hook script for acme.sh
+#
+# Environment variables:
+#
+# - $HE_Username (your dns.he.net username)
+# - $HE_Password (your dns.he.net password)
+#
+# Author: Ondrej Simek
+# Git repo: https://github.com/angel333/acme.sh
+
+#-- dns_he_add() - Add TXT record --------------------------------------
+# Usage: dns_he_add _acme-challenge.subdomain.domain.com "XyZ123..."
+
+dns_he_add() {
+ _full_domain=$1
+ _txt_value=$2
+ _info "Using DNS-01 Hurricane Electric hook"
+
+ HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
+ HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
+ if [ -z "$HE_Username" ] || [ -z "$HE_Password" ]; then
+ HE_Username=
+ HE_Password=
+ _err "No auth details provided. Please set user credentials using the \$HE_Username and \$HE_Password envoronment variables."
+ return 1
+ fi
+ _saveaccountconf_mutable HE_Username "$HE_Username"
+ _saveaccountconf_mutable HE_Password "$HE_Password"
+
+ # Fills in the $_zone_id
+ _find_zone "$_full_domain" || return 1
+ _debug "Zone id \"$_zone_id\" will be used."
+
+ body="email=${HE_Username}&pass=${HE_Password}"
+ body="$body&account="
+ body="$body&menu=edit_zone"
+ body="$body&Type=TXT"
+ body="$body&hosted_dns_zoneid=$_zone_id"
+ body="$body&hosted_dns_recordid="
+ body="$body&hosted_dns_editzone=1"
+ body="$body&Priority="
+ body="$body&Name=$_full_domain"
+ body="$body&Content=$_txt_value"
+ body="$body&TTL=300"
+ body="$body&hosted_dns_editrecord=Submit"
+ response="$(_post "$body" "https://dns.he.net/")"
+ exit_code="$?"
+ if [ "$exit_code" -eq 0 ]; then
+ _info "TXT record added successfully."
+ else
+ _err "Couldn't add the TXT record."
+ fi
+ _debug2 response "$response"
+ return "$exit_code"
+}
+
+#-- dns_he_rm() - Remove TXT record ------------------------------------
+# Usage: dns_he_rm _acme-challenge.subdomain.domain.com "XyZ123..."
+
+dns_he_rm() {
+ _full_domain=$1
+ _txt_value=$2
+ _info "Cleaning up after DNS-01 Hurricane Electric hook"
+ HE_Username="${HE_Username:-$(_readaccountconf_mutable HE_Username)}"
+ HE_Password="${HE_Password:-$(_readaccountconf_mutable HE_Password)}"
+ # fills in the $_zone_id
+ _find_zone "$_full_domain" || return 1
+ _debug "Zone id \"$_zone_id\" will be used."
+
+ # Find the record id to clean
+ body="email=${HE_Username}&pass=${HE_Password}"
+ body="$body&hosted_dns_zoneid=$_zone_id"
+ body="$body&menu=edit_zone"
+ body="$body&hosted_dns_editzone="
+
+ response="$(_post "$body" "https://dns.he.net/")"
+ _debug2 "response" "$response"
+ if ! _contains "$response" "$_txt_value"; then
+ _debug "The txt record is not found, just skip"
+ return 0
+ fi
+ _record_id="$(echo "$response" | tr -d "#" | sed "s/Successfully removed record.' \
+ >/dev/null
+ exit_code="$?"
+ if [ "$exit_code" -eq 0 ]; then
+ _info "Record removed successfully."
+ else
+ _err "Could not clean (remove) up the record. Please go to HE administration interface and clean it by hand."
+ return "$exit_code"
+ fi
+}
+
+########################## PRIVATE FUNCTIONS ###########################
+
+_find_zone() {
+ _domain="$1"
+ body="email=${HE_Username}&pass=${HE_Password}"
+ response="$(_post "$body" "https://dns.he.net/")"
+ _debug2 response "$response"
+ _table="$(echo "$response" | tr -d "#" | sed "s/
+
+ nameserver.info
+
+
+
+
+
+ domain
+
+ %s
+
+
+
+ type
+
+ TXT
+
+
+
+ name
+
+ %s
+
+
+
+
+
+
+ ' "$_domain" "$_sub_domain")
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! _contains "$response" "Command completed successfully"; then
+ _err "Error could not get txt records"
+ return 1
+ fi
+
+ if ! printf "%s" "$response" | grep "count" >/dev/null; then
+ _info "Do not need to delete record"
+ else
+ _record_id=$(printf '%s' "$response" | _egrep_o '.*(record){1}(.*)([0-9]+){1}' | _egrep_o 'id<\/name>[0-9]+' | _egrep_o '[0-9]+')
+ _info "Deleting record"
+ _inwx_delete_record "$_record_id"
+ fi
+
+}
+
+#################### Private functions below ##################################
+
+_inwx_login() {
+
+ xml_content=$(printf '
+
+ account.login
+
+
+
+
+
+ user
+
+ %s
+
+
+
+ pass
+
+ %s
+
+
+
+
+
+
+ ' $INWX_User $INWX_Password)
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ printf "Cookie: %s" "$(grep "domrobot=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'domrobot=[^;]*;' | tr -d ';')"
+
+}
+
+_get_root() {
+ domain=$1
+ _debug "get root"
+
+ domain=$1
+ i=2
+ p=1
+
+ _H1=$(_inwx_login)
+ export _H1
+ xml_content='
+
+ nameserver.list
+ '
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "$h"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+
+}
+
+_inwx_delete_record() {
+ record_id=$1
+ xml_content=$(printf '
+
+ nameserver.deleteRecord
+
+
+
+
+
+ id
+
+ %s
+
+
+
+
+
+
+ ' "$record_id")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+
+}
+
+_inwx_update_record() {
+ record_id=$1
+ txtval=$2
+ xml_content=$(printf '
+
+ nameserver.updateRecord
+
+
+
+
+
+ content
+
+ %s
+
+
+
+ id
+
+ %s
+
+
+
+
+
+
+ ' "$txtval" "$record_id")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+
+}
+
+_inwx_add_record() {
+
+ domain=$1
+ sub_domain=$2
+ txtval=$3
+
+ xml_content=$(printf '
+
+ nameserver.createRecord
+
+
+
+
+
+ domain
+
+ %s
+
+
+
+ type
+
+ TXT
+
+
+
+ content
+
+ %s
+
+
+
+ name
+
+ %s
+
+
+
+
+
+
+ ' "$domain" "$txtval" "$sub_domain")
+
+ response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
+
+ if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+ return 0
+}
diff --git a/dnsapi/dns_ispconfig.sh b/dnsapi/dns_ispconfig.sh
new file mode 100755
index 00000000..1e500ad6
--- /dev/null
+++ b/dnsapi/dns_ispconfig.sh
@@ -0,0 +1,176 @@
+#!/usr/bin/env sh
+
+# ISPConfig 3.1 API
+# User must provide login data and URL to the ISPConfig installation incl. port. The remote user in ISPConfig must have access to:
+# - DNS txt Functions
+
+# Report bugs to https://github.com/sjau/acme.sh
+
+# Values to export:
+# export ISPC_User="remoteUser"
+# export ISPC_Password="remotePassword"
+# export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
+# export ISPC_Api_Insecure=1 # Set 1 for insecure and 0 for secure -> difference is whether ssl cert is checked for validity (0) or whether it is just accepted (1)
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_ispconfig_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+ _debug "Calling: dns_ispconfig_add() '${fulldomain}' '${txtvalue}'"
+ _ISPC_credentials && _ISPC_login && _ISPC_getZoneInfo && _ISPC_addTxt
+}
+
+#Usage: dns_myapi_rm _acme-challenge.www.domain.com
+dns_ispconfig_rm() {
+ fulldomain="${1}"
+ _debug "Calling: dns_ispconfig_rm() '${fulldomain}'"
+ _ISPC_credentials && _ISPC_login && _ISPC_rmTxt
+}
+
+#################### Private functions below ##################################
+
+_ISPC_credentials() {
+ if [ -z "${ISPC_User}" ] || [ -z "$ISPC_Password" ] || [ -z "${ISPC_Api}" ] || [ -z "${ISPC_Api_Insecure}" ]; then
+ ISPC_User=""
+ ISPC_Password=""
+ ISPC_Api=""
+ ISPC_Api_Insecure=""
+ _err "You haven't specified the ISPConfig Login data, URL and whether you want check the ISPC SSL cert. Please try again."
+ return 1
+ else
+ _saveaccountconf ISPC_User "${ISPC_User}"
+ _saveaccountconf ISPC_Password "${ISPC_Password}"
+ _saveaccountconf ISPC_Api "${ISPC_Api}"
+ _saveaccountconf ISPC_Api_Insecure "${ISPC_Api_Insecure}"
+ # Set whether curl should use secure or insecure mode
+ export HTTPS_INSECURE="${ISPC_Api_Insecure}"
+ fi
+}
+
+_ISPC_login() {
+ _info "Getting Session ID"
+ curData="{\"username\":\"${ISPC_User}\",\"password\":\"${ISPC_Password}\",\"client_login\":false}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?login")"
+ _debug "Calling _ISPC_login: '${curData}' '${ISPC_Api}?login'"
+ _debug "Result of _ISPC_login: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ sessionID=$(echo "${curResult}" | _egrep_o "response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _info "Retrieved Session ID."
+ _debug "Session ID: '${sessionID}'"
+ else
+ _err "Couldn't retrieve the Session ID."
+ return 1
+ fi
+}
+
+_ISPC_getZoneInfo() {
+ _info "Getting Zoneinfo"
+ zoneEnd=false
+ curZone="${fulldomain}"
+ while [ "${zoneEnd}" = false ]; do
+ # we can strip the first part of the fulldomain, since it's just the _acme-challenge string
+ curZone="${curZone#*.}"
+ # suffix . needed for zone -> domain.tld.
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"origin\":\"${curZone}.\"}}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_zone_get")"
+ _debug "Calling _ISPC_getZoneInfo: '${curData}' '${ISPC_Api}?login'"
+ _debug "Result of _ISPC_getZoneInfo: '$curResult'"
+ if _contains "${curResult}" '"id":"'; then
+ zoneFound=true
+ zoneEnd=true
+ _info "Retrieved zone data."
+ _debug "Zone data: '${curResult}'"
+ fi
+ if [ "${curZone#*.}" != "$curZone" ]; then
+ _debug2 "$curZone still contains a '.' - so we can check next higher level"
+ else
+ zoneEnd=true
+ _err "Couldn't retrieve zone data."
+ return 1
+ fi
+ done
+ if [ "${zoneFound}" ]; then
+ server_id=$(echo "${curResult}" | _egrep_o "server_id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Server ID: '${server_id}'"
+ case "${server_id}" in
+ '' | *[!0-9]*)
+ _err "Server ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Server ID" ;;
+ esac
+ zone=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Zone: '${zone}'"
+ case "${zone}" in
+ '' | *[!0-9]*)
+ _err "Zone ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Zone ID" ;;
+ esac
+ client_id=$(echo "${curResult}" | _egrep_o "sys_userid.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Client ID: '${client_id}'"
+ case "${client_id}" in
+ '' | *[!0-9]*)
+ _err "Client ID is not numeric."
+ return 1
+ ;;
+ *) _info "Retrieved Client ID." ;;
+ esac
+ zoneFound=""
+ zoneEnd=""
+ fi
+}
+
+_ISPC_addTxt() {
+ curSerial="$(date +%s)"
+ curStamp="$(date +'%F %T')"
+ params="\"server_id\":\"${server_id}\",\"zone\":\"${zone}\",\"name\":\"${fulldomain}.\",\"type\":\"txt\",\"data\":\"${txtvalue}\",\"aux\":\"0\",\"ttl\":\"3600\",\"active\":\"y\",\"stamp\":\"${curStamp}\",\"serial\":\"${curSerial}\""
+ curData="{\"session_id\":\"${sessionID}\",\"client_id\":\"${client_id}\",\"params\":{${params}}}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_add")"
+ _debug "Calling _ISPC_addTxt: '${curData}' '${ISPC_Api}?dns_txt_add'"
+ _debug "Result of _ISPC_addTxt: '$curResult'"
+ record_id=$(echo "${curResult}" | _egrep_o "\"response.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Record ID: '${record_id}'"
+ case "${record_id}" in
+ '' | *[!0-9]*)
+ _err "Couldn't add ACME Challenge TXT record to zone."
+ return 1
+ ;;
+ *) _info "Added ACME Challenge TXT record to zone." ;;
+ esac
+}
+
+_ISPC_rmTxt() {
+ # Need to get the record ID.
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":{\"name\":\"${fulldomain}.\",\"type\":\"TXT\"}}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_get")"
+ _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_get'"
+ _debug "Result of _ISPC_rmTxt: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ record_id=$(echo "${curResult}" | _egrep_o "\"id.*" | cut -d ':' -f 2 | cut -d '"' -f 2)
+ _debug "Record ID: '${record_id}'"
+ case "${record_id}" in
+ '' | *[!0-9]*)
+ _err "Record ID is not numeric."
+ return 1
+ ;;
+ *)
+ unset IFS
+ _info "Retrieved Record ID."
+ curData="{\"session_id\":\"${sessionID}\",\"primary_id\":\"${record_id}\"}"
+ curResult="$(_post "${curData}" "${ISPC_Api}?dns_txt_delete")"
+ _debug "Calling _ISPC_rmTxt: '${curData}' '${ISPC_Api}?dns_txt_delete'"
+ _debug "Result of _ISPC_rmTxt: '$curResult'"
+ if _contains "${curResult}" '"code":"ok"'; then
+ _info "Removed ACME Challenge TXT record from zone."
+ else
+ _err "Couldn't remove ACME Challenge TXT record from zone."
+ return 1
+ fi
+ ;;
+ esac
+ fi
+}
diff --git a/dnsapi/dns_knot.sh b/dnsapi/dns_knot.sh
new file mode 100644
index 00000000..094a6981
--- /dev/null
+++ b/dnsapi/dns_knot.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env sh
+
+######## Public functions #####################
+
+#Usage: dns_knot_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_knot_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _checkKey || return 1
+ [ -n "${KNOT_SERVER}" ] || KNOT_SERVER="localhost"
+ # save the dns server and key to the account.conf file.
+ _saveaccountconf KNOT_SERVER "${KNOT_SERVER}"
+ _saveaccountconf KNOT_KEY "${KNOT_KEY}"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+
+ _info "Adding ${fulldomain}. 60 TXT \"${txtvalue}\""
+
+ knsupdate -y "${KNOT_KEY}" <
+
+LINODE_API_URL="https://api.linode.com/?api_key=$LINODE_API_KEY&api_action="
+
+######## Public functions #####################
+
+#Usage: dns_linode_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_linode_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_add() '${fulldomain}' '${txtvalue}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id&Type=TXT&Name=$_sub_domain&Target=$txtvalue"
+
+ if _rest GET "domain.resource.create" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error adding the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully added."
+ return 0
+ fi
+
+ return 1
+}
+
+#Usage: dns_linode_rm _acme-challenge.www.domain.com
+dns_linode_rm() {
+ fulldomain="${1}"
+
+ if ! _Linode_API; then
+ return 1
+ fi
+
+ _info "Using Linode"
+ _debug "Calling: dns_linode_rm() '${fulldomain}'"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "Domain does not exist."
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _parameters="&DomainID=$_domain_id"
+
+ if _rest GET "domain.resource.list" "$_parameters" && [ -n "$response" ]; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+
+ resource="$(echo "$response" | _egrep_o "{.*\"NAME\":\s*\"$_sub_domain\".*}")"
+ if [ "$resource" ]; then
+ _resource_id=$(printf "%s\n" "$resource" | _egrep_o "\"RESOURCEID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_resource_id" ]; then
+ _debug _resource_id "$_resource_id"
+
+ _parameters="&DomainID=$_domain_id&ResourceID=$_resource_id"
+
+ if _rest GET "domain.resource.delete" "$_parameters" && [ -n "$response" ]; then
+ _resource_id=$(printf "%s\n" "$response" | _egrep_o "\"ResourceID\":\s*[0-9]+" | cut -d : -f 2 | tr -d " " | _head_n 1)
+ _debug _resource_id "$_resource_id"
+
+ if [ -z "$_resource_id" ]; then
+ _err "Error deleting the domain resource."
+ return 1
+ fi
+
+ _info "Domain resource successfully deleted."
+ return 0
+ fi
+ fi
+
+ return 1
+ fi
+
+ return 0
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+
+_Linode_API() {
+ if [ -z "$LINODE_API_KEY" ]; then
+ LINODE_API_KEY=""
+
+ _err "You didn't specify the Linode API key yet."
+ _err "Please create your key and try again."
+
+ return 1
+ fi
+
+ _saveaccountconf LINODE_API_KEY "$LINODE_API_KEY"
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _rest GET "domain.list"; then
+ response="$(echo "$response" | tr -d "\n" | tr '{' "|" | sed 's/|/&{/g' | tr "|" "\n")"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "{.*\"DOMAIN\":\s*\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"DOMAINID\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method method action data
+_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$LINODE_API_URL$ep" "" "$mtd")"
+ else
+ response="$(_get "$LINODE_API_URL$ep$data")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_lua.sh b/dnsapi/dns_lua.sh
new file mode 100755
index 00000000..30c15579
--- /dev/null
+++ b/dnsapi/dns_lua.sh
@@ -0,0 +1,154 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+#
+#LUA_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#LUA_Email="user@luadns.net"
+
+LUA_Api="https://api.luadns.com/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_lua_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+ LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
+ LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
+
+ if [ -z "$LUA_Key" ] || [ -z "$LUA_Email" ]; then
+ LUA_Key=""
+ LUA_Email=""
+ _err "You don't specify luadns api key and email yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable LUA_Key "$LUA_Key"
+ _saveaccountconf_mutable LUA_Email "$LUA_Email"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _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
+}
+
+#fulldomain
+dns_lua_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ LUA_Key="${LUA_Key:-$(_readaccountconf_mutable LUA_Key)}"
+ LUA_Email="${LUA_Email:-$(_readaccountconf_mutable LUA_Email)}"
+ LUA_auth=$(printf "%s" "$LUA_Email:$LUA_Key" | _base64)
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _LUA_rest GET "zones/${_domain_id}/records"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"name\":\"$fulldomain.\",\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ 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"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _LUA_rest DELETE "/zones/$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" "$record_id"
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ if ! _LUA_rest GET "zones"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*,\"name\":\"$h\"" | cut -d : -f 2 | cut -d , -f 1)
+ _debug _domain_id "$_domain_id"
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_LUA_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Authorization: Basic $LUA_auth"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$LUA_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$LUA_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh
new file mode 100644
index 00000000..382eeedd
--- /dev/null
+++ b/dnsapi/dns_me.sh
@@ -0,0 +1,157 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+# ME_Key=qmlkdjflmkqdjf
+# ME_Secret=qmsdlkqmlksdvnnpae
+
+ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_me_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$ME_Key" ] || [ -z "$ME_Secret" ]; then
+ ME_Key=""
+ ME_Secret=""
+ _err "You didn't specify DNSMadeEasy api key and secret yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf ME_Key "$ME_Key"
+ _saveaccountconf ME_Secret "$ME_Secret"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
+
+ if ! _contains "$response" "\"totalRecords\":"; then
+ _err "Error"
+ return 1
+ fi
+
+ _info "Adding record"
+ if _me_rest POST "$_domain_id/records/" "{\"type\":\"TXT\",\"name\":\"$_sub_domain\",\"value\":\"$txtvalue\",\"gtdLocation\":\"DEFAULT\",\"ttl\":120}"; then
+ if printf -- "%s" "$response" | grep \"id\": >/dev/null; then
+ _info "Added"
+ #todo: check if the record takes effect
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+
+}
+
+#fulldomain
+dns_me_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _me_rest GET "${_domain_id}/records?recordName=$_sub_domain&type=TXT"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"totalRecords\":[^,]*" | cut -d : -f 2)
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ record_id=$(printf "%s\n" "$response" | _egrep_o ",\"value\":\"..$txtvalue..\",\"id\":[^,]*" | cut -d : -f 3 | head -n 1)
+ _debug "record_id" "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if ! _me_rest DELETE "$_domain_id/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" ''
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _me_rest GET "name?domainname=$h"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"name\":\"$h\""; then
+ _domain_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[^,]*" | head -n 1 | cut -d : -f 2 | tr -d '}')
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_me_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ cdate=$(LANG=C date -u +"%a, %d %b %Y %T %Z")
+ hmac=$(printf "%s" "$cdate" | _hmac sha1 "$(printf "%s" "$ME_Secret" | _hex_dump | tr -d " ")" hex)
+
+ export _H1="x-dnsme-apiKey: $ME_Key"
+ export _H2="x-dnsme-requestDate: $cdate"
+ export _H3="x-dnsme-hmac: $hmac"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$ME_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$ME_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_myapi.sh b/dnsapi/dns_myapi.sh
new file mode 100755
index 00000000..6bf62508
--- /dev/null
+++ b/dnsapi/dns_myapi.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env sh
+
+#Here is a sample custom api script.
+#This file name is "dns_myapi.sh"
+#So, here must be a method dns_myapi_add()
+#Which will be called by acme.sh to add the txt record to your api system.
+#returns 0 means success, otherwise error.
+#
+#Author: Neilpang
+#Report Bugs here: https://github.com/Neilpang/acme.sh
+#
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_myapi_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using myapi"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+ _err "Not implemented!"
+ return 1
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_myapi_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _info "Using myapi"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+}
+
+#################### Private functions below ##################################
diff --git a/dnsapi/dns_namecom.sh b/dnsapi/dns_namecom.sh
new file mode 100755
index 00000000..b712fa94
--- /dev/null
+++ b/dnsapi/dns_namecom.sh
@@ -0,0 +1,166 @@
+#!/usr/bin/env sh
+
+#Author: RaidenII
+#Created 06/28/2017
+#Updated 03/01/2018, rewrote to support name.com API v4
+#Utilize name.com API to finish dns-01 verifications.
+######## Public functions #####################
+
+Namecom_API="https://api.name.com/v4"
+
+#Usage: dns_namecom_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_namecom_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ # First we need name.com credentials.
+ if [ -z "$Namecom_Username" ]; then
+ Namecom_Username=""
+ _err "Username for name.com is missing."
+ _err "Please specify that in your environment variable."
+ return 1
+ fi
+
+ if [ -z "$Namecom_Token" ]; then
+ Namecom_Token=""
+ _err "API token for name.com is missing."
+ _err "Please specify that in your environment variable."
+ return 1
+ fi
+
+ # Save them in configuration.
+ _saveaccountconf Namecom_Username "$Namecom_Username"
+ _saveaccountconf Namecom_Token "$Namecom_Token"
+
+ # Login in using API
+ if ! _namecom_login; then
+ return 1
+ fi
+
+ # Find domain in domain list.
+ if ! _namecom_get_root "$fulldomain"; then
+ _err "Unable to find domain specified."
+ return 1
+ fi
+
+ # Add TXT record.
+ _namecom_addtxt_json="{\"host\":\"$_sub_domain\",\"type\":\"TXT\",\"answer\":\"$txtvalue\",\"ttl\":\"300\"}"
+ if _namecom_rest POST "domains/$_domain/records" "$_namecom_addtxt_json"; then
+ _retvalue=$(printf "%s\n" "$response" | _egrep_o "\"$_sub_domain\"")
+ if [ "$_retvalue" ]; then
+ _info "Successfully added TXT record, ready for validation."
+ return 0
+ else
+ _err "Unable to add the DNS record."
+ return 1
+ fi
+ fi
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_namecom_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _namecom_login; then
+ return 1
+ fi
+
+ # Find domain in domain list.
+ if ! _namecom_get_root "$fulldomain"; then
+ _err "Unable to find domain specified."
+ return 1
+ fi
+
+ # Get the record id.
+ if _namecom_rest GET "domains/$_domain/records"; then
+ _record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":[0-9]+,\"domainName\":\"$_domain\",\"host\":\"$_sub_domain\",\"fqdn\":\"$fulldomain.\",\"type\":\"TXT\",\"answer\":\"$txtvalue\"" | cut -d \" -f 3 | _egrep_o [0-9]+)
+ _debug record_id "$_record_id"
+ if [ "$_record_id" ]; then
+ _info "Successfully retrieved the record id for ACME challenge."
+ else
+ _err "Unable to retrieve the record id."
+ return 1
+ fi
+ fi
+
+ # Remove the DNS record using record id.
+ if _namecom_rest DELETE "domains/$_domain/records/$_record_id"; then
+ _info "Successfully removed the TXT record."
+ return 0
+ else
+ _err "Unable to delete record id."
+ return 1
+ fi
+}
+
+#################### Private functions below ##################################
+_namecom_rest() {
+ method=$1
+ param=$2
+ data=$3
+
+ export _H1="Authorization: Basic $_namecom_auth"
+ export _H2="Content-Type: application/json"
+
+ if [ "$method" != "GET" ]; then
+ response="$(_post "$data" "$Namecom_API/$param" "" "$method")"
+ else
+ response="$(_get "$Namecom_API/$param")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $param"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
+
+_namecom_login() {
+ # Auth string
+ # Name.com API v4 uses http basic auth to authenticate
+ # need to convert the token for http auth
+ _namecom_auth=$(printf "%s:%s" "$Namecom_Username" "$Namecom_Token" | base64)
+
+ if _namecom_rest GET "hello"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "\"username\"\:\"$Namecom_Username\"")
+ if [ "$retcode" ]; then
+ _info "Successfully logged in."
+ else
+ _err "Logging in failed."
+ return 1
+ fi
+ fi
+}
+
+_namecom_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if ! _namecom_rest GET "domains"; then
+ return 1
+ fi
+
+ # Need to exclude the last field (tld)
+ numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
+ while [ $i -le "$numfields" ]; do
+ host=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug host "$host"
+ if [ -z "$host" ]; then
+ return 1
+ fi
+
+ if _contains "$response" "$host"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$host"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh
new file mode 100755
index 00000000..dc1a4fda
--- /dev/null
+++ b/dnsapi/dns_namesilo.sh
@@ -0,0 +1,137 @@
+#!/usr/bin/env sh
+
+#Author: meowthink
+#Created 01/14/2017
+#Utilize namesilo.com API to finish dns-01 verifications.
+
+Namesilo_API="https://www.namesilo.com/api"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_namesilo_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$Namesilo_Key" ]; then
+ Namesilo_Key=""
+ _err "API token for namesilo.com is missing."
+ _err "Please specify that in your environment variable."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf Namesilo_Key "$Namesilo_Key"
+
+ if ! _get_root "$fulldomain"; then
+ _err "Unable to find domain specified."
+ return 1
+ fi
+
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug txtvalue "$txtvalue"
+ if _namesilo_rest GET "dnsAddRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrtype=TXT&rrhost=$_sub_domain&rrvalue=$txtvalue"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "300")
+ if [ "$retcode" ]; then
+ _info "Successfully added TXT record, ready for validation."
+ return 0
+ else
+ _err "Unable to add the DNS record."
+ return 1
+ fi
+ fi
+}
+
+#Usage: fulldomain txtvalue
+#Remove the txt record after validation.
+dns_namesilo_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if ! _get_root "$fulldomain"; then
+ _err "Unable to find domain specified."
+ return 1
+ fi
+
+ # Get the record id.
+ if _namesilo_rest GET "dnsListRecords?version=1&type=xml&key=$Namesilo_Key&domain=$_domain"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "300")
+ if [ "$retcode" ]; then
+ _record_id=$(printf "%s\n" "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1)
+ _debug record_id "$_record_id"
+ _info "Successfully retrieved the record id for ACME challenge."
+ else
+ _err "Unable to retrieve the record id."
+ return 1
+ fi
+ fi
+
+ # Remove the DNS record using record id.
+ if _namesilo_rest GET "dnsDeleteRecord?version=1&type=xml&key=$Namesilo_Key&domain=$_domain&rrid=$_record_id"; then
+ retcode=$(printf "%s\n" "$response" | _egrep_o "300")
+ if [ "$retcode" ]; then
+ _info "Successfully removed the TXT record."
+ return 0
+ else
+ _err "Unable to remove the DNS record."
+ return 1
+ fi
+ fi
+}
+
+#################### Private functions below ##################################
+
+# _acme-challenge.www.domain.com
+# returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if ! _namesilo_rest GET "listDomains?version=1&type=xml&key=$Namesilo_Key"; then
+ return 1
+ fi
+
+ # Need to exclude the last field (tld)
+ numfields=$(echo "$domain" | _egrep_o "\." | wc -l)
+ while [ $i -le "$numfields" ]; do
+ host=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug host "$host"
+ if [ -z "$host" ]; then
+ return 1
+ fi
+
+ if _contains "$response" "$host"; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$host"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_namesilo_rest() {
+ method=$1
+ param=$2
+ data=$3
+
+ if [ "$method" != "GET" ]; then
+ response="$(_post "$data" "$Namesilo_API/$param" "" "$method")"
+ else
+ response="$(_get "$Namesilo_API/$param")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $param"
+ return 1
+ fi
+
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_nsone.sh b/dnsapi/dns_nsone.sh
new file mode 100644
index 00000000..00e186d2
--- /dev/null
+++ b/dnsapi/dns_nsone.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env sh
+
+# bug reports to dev@1e.ca
+
+#
+#NS1_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+NS1_Api="https://api.nsone.net/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nsone_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$NS1_Key" ]; then
+ NS1_Key=""
+ _err "You didn't specify nsone dns api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf NS1_Key "$NS1_Key"
+
+ _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 txt records"
+ _nsone_rest GET "zones/${_domain}"
+
+ if ! _contains "$response" "\"records\":"; then
+ _err "Error"
+ return 1
+ fi
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",[^{]*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Adding record"
+
+ if _nsone_rest PUT "zones/$_domain/$fulldomain/TXT" "{\"answers\":[{\"answer\":[\"$txtvalue\"]}],\"type\":\"TXT\",\"domain\":\"$fulldomain\",\"zone\":\"$_domain\"}"; 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"
+ prev_txt=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",\"short_answers\":\[\"[^,]*\]" | _head_n 1 | cut -d: -f3 | cut -d, -f1)
+ _debug "prev_txt" "$prev_txt"
+
+ _nsone_rest POST "zones/$_domain/$fulldomain/TXT" "{\"answers\": [{\"answer\": [\"$txtvalue\"]},{\"answer\": $prev_txt}],\"type\": \"TXT\",\"domain\":\"$fulldomain\",\"zone\": \"$_domain\"}"
+ if [ "$?" = "0" ] && _contains "$response" "$fulldomain"; then
+ _info "Updated!"
+ #todo: check if the record takes effect
+ return 0
+ fi
+ _err "Update error"
+ return 1
+ fi
+
+}
+
+#fulldomain
+dns_nsone_rm() {
+ fulldomain=$1
+ txtvalue=$2
+ _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 txt records"
+ _nsone_rest GET "zones/${_domain}/$fulldomain/TXT"
+
+ count=$(printf "%s\n" "$response" | _egrep_o "\"domain\":\"$fulldomain\",.*\"type\":\"TXT\"" | wc -l | tr -d " ")
+ _debug count "$count"
+ if [ "$count" = "0" ]; then
+ _info "Don't need to remove."
+ else
+ if ! _nsone_rest DELETE "zones/${_domain}/$fulldomain/TXT"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" ""
+ fi
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ if ! _nsone_rest GET "zones"; then
+ return 1
+ fi
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"zone\":\"$h\""; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_nsone_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="X-NSONE-Key: $NS1_Key"
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$NS1_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$NS1_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh
new file mode 100755
index 00000000..7acb2ef7
--- /dev/null
+++ b/dnsapi/dns_nsupdate.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env sh
+
+######## Public functions #####################
+
+#Usage: dns_nsupdate_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_nsupdate_add() {
+ fulldomain=$1
+ txtvalue=$2
+ _checkKeyFile || return 1
+ [ -n "${NSUPDATE_SERVER}" ] || NSUPDATE_SERVER="localhost"
+ # save the dns server and key to the account conf file.
+ _saveaccountconf NSUPDATE_SERVER "${NSUPDATE_SERVER}"
+ _saveaccountconf NSUPDATE_KEY "${NSUPDATE_KEY}"
+ _info "adding ${fulldomain}. 60 in txt \"${txtvalue}\""
+ nsupdate -k "${NSUPDATE_KEY}" </dev/null && ! _contains "$response" "NOT_GRANTED_CALL" >/dev/null; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain="$h"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_ovh_timestamp() {
+ _H1=""
+ _H2=""
+ _H3=""
+ _H4=""
+ _H5=""
+ _get "$OVH_API/auth/time" "" 30
+}
+
+_ovh_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ _ovh_url="$OVH_API/$ep"
+ _debug2 _ovh_url "$_ovh_url"
+ _ovh_t="$(_ovh_timestamp)"
+ _debug2 _ovh_t "$_ovh_t"
+ _ovh_p="$OVH_AS+$OVH_CK+$m+$_ovh_url+$data+$_ovh_t"
+ _secure_debug _ovh_p "$_ovh_p"
+ _ovh_hex="$(printf "%s" "$_ovh_p" | _digest sha1 hex)"
+ _debug2 _ovh_hex "$_ovh_hex"
+
+ export _H1="X-Ovh-Application: $OVH_AK"
+ export _H2="X-Ovh-Signature: \$1\$$_ovh_hex"
+ _debug2 _H2 "$_H2"
+ export _H3="X-Ovh-Timestamp: $_ovh_t"
+ export _H4="X-Ovh-Consumer: $OVH_CK"
+ export _H5="Content-Type: application/json;charset=utf-8"
+ if [ "$data" ] || [ "$m" = "POST" ] || [ "$m" = "PUT" ] || [ "$m" = "DELETE" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$_ovh_url" "" "$m")"
+ else
+ response="$(_get "$_ovh_url")"
+ fi
+
+ if [ "$?" != "0" ] || _contains "$response" "INVALID_CREDENTIAL"; then
+ _err "error $response"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_pdns.sh b/dnsapi/dns_pdns.sh
new file mode 100755
index 00000000..7d807c81
--- /dev/null
+++ b/dnsapi/dns_pdns.sh
@@ -0,0 +1,184 @@
+#!/usr/bin/env sh
+
+#PowerDNS Embedded API
+#https://doc.powerdns.com/md/httpapi/api_spec/
+#
+#PDNS_Url="http://ns.example.com:8081"
+#PDNS_ServerId="localhost"
+#PDNS_Token="0123456789ABCDEF"
+#PDNS_Ttl=60
+
+DEFAULT_PDNS_TTL=60
+
+######## Public functions #####################
+#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000"
+#fulldomain
+#txtvalue
+dns_pdns_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$PDNS_Url" ]; then
+ PDNS_Url=""
+ _err "You don't specify PowerDNS address."
+ _err "Please set PDNS_Url and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_ServerId" ]; then
+ PDNS_ServerId=""
+ _err "You don't specify PowerDNS server id."
+ _err "Please set you PDNS_ServerId and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_Token" ]; then
+ PDNS_Token=""
+ _err "You don't specify PowerDNS token."
+ _err "Please create you PDNS_Token and try again."
+ return 1
+ fi
+
+ if [ -z "$PDNS_Ttl" ]; then
+ PDNS_Ttl="$DEFAULT_PDNS_TTL"
+ fi
+
+ #save the api addr and key to the account conf file.
+ _saveaccountconf PDNS_Url "$PDNS_Url"
+ _saveaccountconf PDNS_ServerId "$PDNS_ServerId"
+ _saveaccountconf PDNS_Token "$PDNS_Token"
+
+ if [ "$PDNS_Ttl" != "$DEFAULT_PDNS_TTL" ]; then
+ _saveaccountconf PDNS_Ttl "$PDNS_Ttl"
+ fi
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ if ! set_record "$_domain" "$fulldomain" "$txtvalue"; then
+ return 1
+ fi
+
+ return 0
+}
+
+#fulldomain
+dns_pdns_rm() {
+ fulldomain=$1
+
+ _debug "Detect root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain "$_domain"
+
+ if ! rm_record "$_domain" "$fulldomain"; then
+ return 1
+ fi
+
+ return 0
+}
+
+set_record() {
+ _info "Adding record"
+ root=$1
+ full=$2
+ txtvalue=$3
+
+ if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"REPLACE\", \"name\": \"$full.\", \"type\": \"TXT\", \"ttl\": $PDNS_Ttl, \"records\": [{\"name\": \"$full.\", \"type\": \"TXT\", \"content\": \"\\\"$txtvalue\\\"\", \"disabled\": false, \"ttl\": $PDNS_Ttl}]}]}"; then
+ _err "Set txt record error."
+ return 1
+ fi
+
+ if ! notify_slaves "$root"; then
+ return 1
+ fi
+
+ return 0
+}
+
+rm_record() {
+ _info "Remove record"
+ root=$1
+ full=$2
+
+ if ! _pdns_rest "PATCH" "/api/v1/servers/$PDNS_ServerId/zones/$root." "{\"rrsets\": [{\"changetype\": \"DELETE\", \"name\": \"$full.\", \"type\": \"TXT\"}]}"; then
+ _err "Delete txt record error."
+ return 1
+ fi
+
+ if ! notify_slaves "$root"; then
+ return 1
+ fi
+
+ return 0
+}
+
+notify_slaves() {
+ root=$1
+
+ if ! _pdns_rest "PUT" "/api/v1/servers/$PDNS_ServerId/zones/$root./notify"; then
+ _err "Notify slaves error."
+ return 1
+ fi
+
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _domain=domain.com
+_get_root() {
+ domain=$1
+ i=1
+
+ if _pdns_rest "GET" "/api/v1/servers/$PDNS_ServerId/zones"; then
+ _zones_response="$response"
+ fi
+
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ if [ -z "$h" ]; then
+ return 1
+ fi
+
+ if _contains "$_zones_response" "\"name\": \"$h.\""; then
+ _domain="$h"
+ return 0
+ fi
+
+ i=$(_math $i + 1)
+ done
+ _debug "$domain not found"
+
+ return 1
+}
+
+_pdns_rest() {
+ method=$1
+ ep=$2
+ data=$3
+
+ export _H1="X-API-Key: $PDNS_Token"
+
+ if [ ! "$method" = "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$PDNS_Url$ep" "" "$method")"
+ else
+ response="$(_get "$PDNS_Url$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+
+ return 0
+}
diff --git a/dnsapi/dns_selectel.sh b/dnsapi/dns_selectel.sh
new file mode 100644
index 00000000..94252d81
--- /dev/null
+++ b/dnsapi/dns_selectel.sh
@@ -0,0 +1,161 @@
+#!/usr/bin/env sh
+
+#
+#SL_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+
+SL_Api="https://api.selectel.ru/domains/v1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_selectel_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify selectel.ru api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable SL_Key "$SL_Key"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _info "Adding record"
+ if _sl_rest POST "/$_domain_id/records/" "{\"type\": \"TXT\", \"ttl\": 60, \"name\": \"$fulldomain\", \"content\": \"$txtvalue\"}"; then
+ if _contains "$response" "$txtvalue" || _contains "$response" "record_already_exists"; then
+ _info "Added, OK"
+ return 0
+ fi
+ fi
+ _err "Add txt record error."
+ return 1
+}
+
+#fulldomain txtvalue
+dns_selectel_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ SL_Key="${SL_Key:-$(_readaccountconf_mutable SL_Key)}"
+
+ if [ -z "$SL_Key" ]; then
+ SL_Key=""
+ _err "You don't specify slectel api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _sl_rest GET "/${_domain_id}/records/"
+
+ if ! _contains "$response" "$txtvalue"; then
+ _err "Txt record not found"
+ return 1
+ fi
+
+ _record_seg="$(echo "$response" | _egrep_o "\"content\" *: *\"$txtvalue\"[^}]*}")"
+ _debug2 "_record_seg" "$_record_seg"
+ if [ -z "$_record_seg" ]; then
+ _err "can not find _record_seg"
+ return 1
+ fi
+
+ _record_id="$(echo "$_record_seg" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\"" | cut -d : -f 2)"
+ _debug2 "_record_id" "$_record_id"
+ if [ -z "$_record_id" ]; then
+ _err "can not find _record_id"
+ return 1
+ fi
+
+ if ! _sl_rest DELETE "/$_domain_id/records/$_record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ return 0
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+
+ if ! _sl_rest GET "/"; then
+ return 1
+ fi
+
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if _contains "$response" "\"name\": \"$h\","; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ _debug "Getting domain id for $h"
+ if ! _sl_rest GET "/$h"; then
+ return 1
+ fi
+ _domain_id="$(echo "$response" | tr "," "\n" | tr "}" "\n" | tr -d " " | grep "\"id\":" | cut -d : -f 2)"
+ return 0
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_sl_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="X-Token: $SL_Key"
+ export _H2="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$SL_Api/$ep" "" "$m")"
+ else
+ response="$(_get "$SL_Api/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_servercow.sh b/dnsapi/dns_servercow.sh
new file mode 100755
index 00000000..be4e59da
--- /dev/null
+++ b/dnsapi/dns_servercow.sh
@@ -0,0 +1,170 @@
+#!/usr/bin/env sh
+
+##########
+# Custom servercow.de DNS API v1 for use with [acme.sh](https://github.com/Neilpang/acme.sh)
+#
+# Usage:
+# export SERVERCOW_API_Username=username
+# export SERVERCOW_API_Password=password
+# acme.sh --issue -d example.com --dns dns_servercow
+#
+# Issues:
+# Any issues / questions / suggestions can be posted here:
+# https://github.com/jhartlep/servercow-dns-api/issues
+#
+# Author: Jens Hartlep
+##########
+
+SERVERCOW_API="https://api.servercow.de/dns/v1/domains"
+
+# Usage dns_servercow_add _acme-challenge.www.domain.com "abcdefghijklmnopqrstuvwxyz"
+dns_servercow_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using servercow"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$txtvalue"
+
+ SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
+ SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
+ if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
+ SERVERCOW_API_Username=""
+ SERVERCOW_API_Password=""
+ _err "You don't specify servercow api username and password yet."
+ _err "Please create your username and password and try again."
+ return 1
+ fi
+
+ # save the credentials to the account conf file
+ _saveaccountconf_mutable SERVERCOW_API_Username "$SERVERCOW_API_Username"
+ _saveaccountconf_mutable SERVERCOW_API_Password "$SERVERCOW_API_Password"
+
+ _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"
+
+ if _servercow_api POST "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":20}"; then
+ if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "add txt record error."
+ return 1
+ fi
+ fi
+ _err "add txt record error."
+
+ return 1
+}
+
+# Usage fulldomain txtvalue
+# Remove the txt record after validation
+dns_servercow_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _info "Using servercow"
+ _debug fulldomain "$fulldomain"
+ _debug txtvalue "$fulldomain"
+
+ SERVERCOW_API_Username="${SERVERCOW_API_Username:-$(_readaccountconf_mutable SERVERCOW_API_Username)}"
+ SERVERCOW_API_Password="${SERVERCOW_API_Password:-$(_readaccountconf_mutable SERVERCOW_API_Password)}"
+ if [ -z "$SERVERCOW_API_Username" ] || [ -z "$SERVERCOW_API_Password" ]; then
+ SERVERCOW_API_Username=""
+ SERVERCOW_API_Password=""
+ _err "You don't specify servercow api username and password yet."
+ _err "Please create your username and password and try again."
+ return 1
+ fi
+
+ _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"
+
+ if _servercow_api DELETE "$_domain" "{\"type\":\"TXT\",\"name\":\"$fulldomain\"}"; then
+ if printf -- "%s" "$response" | grep "ok" >/dev/null; then
+ _info "Deleted, OK"
+ _contains "$response" '"message":"ok"'
+ else
+ _err "delete txt record error."
+ return 1
+ fi
+ fi
+
+}
+
+#################### Private functions below ##################################
+
+# _acme-challenge.www.domain.com
+# returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+_get_root() {
+ fulldomain=$1
+ i=2
+ p=1
+
+ while true; do
+ _domain=$(printf "%s" "$fulldomain" | cut -d . -f $i-100)
+
+ _debug _domain "$_domain"
+ if [ -z "$_domain" ]; then
+ # not valid
+ return 1
+ fi
+
+ if ! _servercow_api GET "$_domain"; then
+ return 1
+ fi
+
+ if ! _contains "$response" '"error":"no such domain in user context"' >/dev/null; then
+ _sub_domain=$(printf "%s" "$fulldomain" | cut -d . -f 1-$p)
+ if [ -z "$_sub_domain" ]; then
+ # not valid
+ return 1
+ fi
+
+ return 0
+ fi
+
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+
+ return 1
+}
+
+_servercow_api() {
+ method=$1
+ domain=$2
+ data="$3"
+
+ export _H1="Content-Type: application/json"
+ export _H2="X-Auth-Username: $SERVERCOW_API_Username"
+ export _H3="X-Auth-Password: $SERVERCOW_API_Password"
+
+ if [ "$method" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$SERVERCOW_API/$domain" "" "$method")"
+ else
+ response="$(_get "$SERVERCOW_API/$domain")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $domain"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_unoeuro.sh b/dnsapi/dns_unoeuro.sh
new file mode 100644
index 00000000..a3803a21
--- /dev/null
+++ b/dnsapi/dns_unoeuro.sh
@@ -0,0 +1,202 @@
+#!/usr/bin/env sh
+
+#
+#UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#UNO_User="UExxxxxx"
+
+Uno_Api="https://api.unoeuro.com/1"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_unoeuro_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
+ UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
+ if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
+ UNO_Key=""
+ UNO_User=""
+ _err "You haven't specified a UnoEuro api key and account yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ if ! _contains "$UNO_User" "UE"; then
+ _err "It seems that the UNO_User=$UNO_User is not a valid username."
+ _err "Please check and retry."
+ return 1
+ fi
+
+ #save the api key and email to the account conf file.
+ _saveaccountconf_mutable UNO_Key "$UNO_Key"
+ _saveaccountconf_mutable UNO_User "$UNO_User"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _uno_rest GET "my/products/$h/dns/records"
+
+ if ! _contains "$response" "\"status\": 200" >/dev/null; then
+ _err "Error"
+ return 1
+ fi
+
+ if ! _contains "$response" "$_sub_domain" >/dev/null; then
+ _info "Adding record"
+
+ if _uno_rest POST "my/products/$h/dns/records" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"; then
+ if _contains "$response" "\"status\": 200" >/dev/null; then
+ _info "Added, OK"
+ return 0
+ else
+ _err "Add txt record error."
+ return 1
+ fi
+ fi
+ _err "Add txt record error."
+ else
+ _info "Updating record"
+ record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1)
+ record_line_number=$(_math "$record_line_number" - 1)
+ record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}")
+ _debug "record_id" "$record_id"
+
+ _uno_rest PUT "my/products/$h/dns/records/$record_id" "{\"name\":\"$fulldomain\",\"type\":\"TXT\",\"data\":\"$txtvalue\",\"ttl\":120}"
+ if _contains "$response" "\"status\": 200" >/dev/null; then
+ _info "Updated, OK"
+ return 0
+ fi
+ _err "Update error"
+ return 1
+ fi
+}
+
+#fulldomain txtvalue
+dns_unoeuro_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ UNO_Key="${UNO_Key:-$(_readaccountconf_mutable UNO_Key)}"
+ UNO_User="${UNO_User:-$(_readaccountconf_mutable UNO_User)}"
+ if [ -z "$UNO_Key" ] || [ -z "$UNO_User" ]; then
+ UNO_Key=""
+ UNO_User=""
+ _err "You haven't specified a UnoEuro api key and account yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ if ! _contains "$UNO_User" "UE"; then
+ _err "It seems that the UNO_User=$UNO_User is not a valid username."
+ _err "Please check and retry."
+ return 1
+ fi
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _uno_rest GET "my/products/$h/dns/records"
+
+ if ! _contains "$response" "\"status\": 200"; then
+ _err "Error"
+ return 1
+ fi
+
+ if ! _contains "$response" "$_sub_domain"; then
+ _info "Don't need to remove."
+ else
+ record_line_number=$(echo "$response" | grep -n "$_sub_domain" | cut -d : -f 1)
+ record_line_number=$(_math "$record_line_number" - 1)
+ record_id=$(echo "$response" | _head_n "$record_line_number" | _tail_n 1 1 | _egrep_o "[0-9]{1,}")
+ _debug "record_id" "$record_id"
+
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+
+ if ! _uno_rest DELETE "my/products/$h/dns/records/$record_id"; then
+ _err "Delete record error."
+ return 1
+ fi
+ _contains "$response" "\"status\": 200"
+ fi
+
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=sdjkglgdfewsdfg
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ if ! _uno_rest GET "my/products/$h/dns/records"; then
+ return 1
+ fi
+
+ if _contains "$response" "\"status\": 200"; then
+ _domain_id=$h
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ return 1
+}
+
+_uno_rest() {
+ m=$1
+ ep="$2"
+ data="$3"
+ _debug "$ep"
+
+ export _H1="Content-Type: application/json"
+
+ if [ "$m" != "GET" ]; then
+ _debug data "$data"
+ response="$(_post "$data" "$Uno_Api/$UNO_User/$UNO_Key/$ep" "" "$m")"
+ else
+ response="$(_get "$Uno_Api/$UNO_User/$UNO_Key/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh
new file mode 100755
index 00000000..e50b7d8b
--- /dev/null
+++ b/dnsapi/dns_vscale.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env sh
+
+#This is the vscale.io api wrapper for acme.sh
+#
+#Author: Alex Loban
+#Report Bugs here: https://github.com/LAV45/acme.sh
+
+#VSCALE_API_KEY="sdfsdfsdfljlbjkljlkjsdfoiwje"
+VSCALE_API_URL="https://api.vscale.io/v1"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_vscale_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ if [ -z "$VSCALE_API_KEY" ]; then
+ VSCALE_API_KEY=""
+ _err "You didn't specify the VSCALE api key yet."
+ _err "Please create you key and try again."
+ return 1
+ fi
+
+ _saveaccountconf VSCALE_API_KEY "$VSCALE_API_KEY"
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _vscale_tmpl_json="{\"type\":\"TXT\",\"name\":\"$_sub_domain.$_domain\",\"content\":\"$txtvalue\"}"
+
+ if _vscale_rest POST "domains/$_domain_id/records/" "$_vscale_tmpl_json"; then
+ response=$(printf "%s\n" "$response" | _egrep_o "{\"error\": \".+\"" | cut -d : -f 2)
+ if [ -z "$response" ]; then
+ _info "txt record updated success."
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+#fulldomain txtvalue
+dns_vscale_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ _debug "First detect the root zone"
+ if ! _get_root "$fulldomain"; then
+ _err "invalid domain"
+ return 1
+ fi
+ _debug _domain_id "$_domain_id"
+ _debug _sub_domain "$_sub_domain"
+ _debug _domain "$_domain"
+
+ _debug "Getting txt records"
+ _vscale_rest GET "domains/$_domain_id/records/"
+
+ if [ -n "$response" ]; then
+ record_id=$(printf "%s\n" "$response" | _egrep_o "\"TXT\", \"id\": [0-9]+, \"name\": \"$_sub_domain.$_domain\"" | cut -d : -f 2 | tr -d ", \"name\"")
+ _debug record_id "$record_id"
+ if [ -z "$record_id" ]; then
+ _err "Can not get record id to remove."
+ return 1
+ fi
+ if _vscale_rest DELETE "domains/$_domain_id/records/$record_id" && [ -z "$response" ]; then
+ _info "txt record deleted success."
+ return 0
+ fi
+ _debug response "$response"
+ return 1
+ fi
+
+ return 1
+}
+
+#################### Private functions below ##################################
+#_acme-challenge.www.domain.com
+#returns
+# _sub_domain=_acme-challenge.www
+# _domain=domain.com
+# _domain_id=12345
+_get_root() {
+ domain=$1
+ i=2
+ p=1
+
+ if _vscale_rest GET "domains/"; then
+ response="$(echo "$response" | tr -d "\n" | sed 's/{/\n&/g')"
+ while true; do
+ h=$(printf "%s" "$domain" | cut -d . -f $i-100)
+ _debug h "$h"
+ if [ -z "$h" ]; then
+ #not valid
+ return 1
+ fi
+
+ hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")"
+ if [ "$hostedzone" ]; then
+ _domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "\"id\":\s*[0-9]+" | _head_n 1 | cut -d : -f 2 | tr -d \ )
+ if [ "$_domain_id" ]; then
+ _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
+ _domain=$h
+ return 0
+ fi
+ return 1
+ fi
+ p=$i
+ i=$(_math "$i" + 1)
+ done
+ fi
+ return 1
+}
+
+#method uri qstr data
+_vscale_rest() {
+ mtd="$1"
+ ep="$2"
+ data="$3"
+
+ _debug mtd "$mtd"
+ _debug ep "$ep"
+
+ export _H1="Accept: application/json"
+ export _H2="Content-Type: application/json"
+ export _H3="X-Token: ${VSCALE_API_KEY}"
+
+ if [ "$mtd" != "GET" ]; then
+ # both POST and DELETE.
+ _debug data "$data"
+ response="$(_post "$data" "$VSCALE_API_URL/$ep" "" "$mtd")"
+ else
+ response="$(_get "$VSCALE_API_URL/$ep")"
+ fi
+
+ if [ "$?" != "0" ]; then
+ _err "error $ep"
+ return 1
+ fi
+ _debug2 response "$response"
+ return 0
+}
diff --git a/dnsapi/dns_yandex.sh b/dnsapi/dns_yandex.sh
new file mode 100755
index 00000000..5fbb09d8
--- /dev/null
+++ b/dnsapi/dns_yandex.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/env sh
+# Author: non7top@gmail.com
+# 07 Jul 2017
+# report bugs at https://github.com/non7top/acme.sh
+
+# Values to export:
+# export PDD_Token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+
+######## Public functions #####################
+
+#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_yandex_add() {
+ fulldomain="${1}"
+ txtvalue="${2}"
+ _debug "Calling: dns_yandex_add() '${fulldomain}' '${txtvalue}'"
+ _PDD_credentials || return 1
+ export _H1="PddToken: $PDD_Token"
+
+ _PDD_get_domain "$fulldomain"
+ _debug "Found suitable domain in pdd: $curDomain"
+ curData="domain=${curDomain}&type=TXT&subdomain=${curSubdomain}&ttl=360&content=${txtvalue}"
+ curUri="https://pddimp.yandex.ru/api2/admin/dns/add"
+ curResult="$(_post "${curData}" "${curUri}")"
+ _debug "Result: $curResult"
+}
+
+#Usage: dns_myapi_rm _acme-challenge.www.domain.com
+dns_yandex_rm() {
+ fulldomain="${1}"
+ _debug "Calling: dns_yandex_rm() '${fulldomain}'"
+ _PDD_credentials || return 1
+ export _H1="PddToken: $PDD_Token"
+ record_id=$(pdd_get_record_id "${fulldomain}")
+ _debug "Result: $record_id"
+
+ _PDD_get_domain "$fulldomain"
+ _debug "Found suitable domain in pdd: $curDomain"
+
+ curUri="https://pddimp.yandex.ru/api2/admin/dns/del"
+ curData="domain=${curDomain}&record_id=${record_id}"
+ curResult="$(_post "${curData}" "${curUri}")"
+ _debug "Result: $curResult"
+}
+
+#################### Private functions below ##################################
+
+_PDD_get_domain() {
+ fulldomain="${1}"
+ __page=1
+ __last=0
+ while [ $__last -eq 0 ]; do
+ uri1="https://pddimp.yandex.ru/api2/admin/domain/domains?page=${__page}&on_page=20"
+ res1=$(_get "$uri1" | _normalizeJson)
+ #_debug "$res1"
+ __found=$(echo "$res1" | sed -n -e 's#.* "found": \([^,]*\),.*#\1#p')
+ _debug "found: $__found results on page"
+ if [ "$__found" -lt 20 ]; then
+ _debug "last page: $__page"
+ __last=1
+ fi
+
+ __all_domains="$__all_domains $(echo "$res1" | tr "," "\n" | grep '"name"' | cut -d: -f2 | sed -e 's@"@@g')"
+
+ __page=$(_math $__page + 1)
+ done
+
+ k=2
+ while [ $k -lt 10 ]; do
+ __t=$(echo "$fulldomain" | cut -d . -f $k-100)
+ _debug "finding zone for domain $__t"
+ for d in $__all_domains; do
+ if [ "$d" = "$__t" ]; then
+ p=$(_math $k - 1)
+ curSubdomain="$(echo "$fulldomain" | cut -d . -f "1-$p")"
+ curDomain="$__t"
+ return 0
+ fi
+ done
+ k=$(_math $k + 1)
+ done
+ _err "No suitable domain found in your account"
+ return 1
+}
+
+_PDD_credentials() {
+ if [ -z "${PDD_Token}" ]; then
+ PDD_Token=""
+ _err "You need to export PDD_Token=xxxxxxxxxxxxxxxxx"
+ _err "You can get it at https://pddimp.yandex.ru/api2/admin/get_token"
+ return 1
+ else
+ _saveaccountconf PDD_Token "${PDD_Token}"
+ fi
+}
+
+pdd_get_record_id() {
+ fulldomain="${1}"
+
+ _PDD_get_domain "$fulldomain"
+ _debug "Found suitable domain in pdd: $curDomain"
+
+ curUri="https://pddimp.yandex.ru/api2/admin/dns/list?domain=${curDomain}"
+ curResult="$(_get "${curUri}" | _normalizeJson)"
+ _debug "Result: $curResult"
+ echo "$curResult" | _egrep_o "{[^{]*\"content\":[^{]*\"subdomain\":\"${curSubdomain}\"" | sed -n -e 's#.* "record_id": \(.*\),[^,]*#\1#p'
+}
diff --git a/dnsapi/dns_zonomi.sh b/dnsapi/dns_zonomi.sh
new file mode 100644
index 00000000..52a889ea
--- /dev/null
+++ b/dnsapi/dns_zonomi.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env sh
+
+#
+#ZM_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
+#
+#https://zonomi.com dns api
+
+ZM_Api="https://zonomi.com/app/dns/dyndns.jsp"
+
+######## Public functions #####################
+
+#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
+dns_zonomi_add() {
+ fulldomain=$1
+ txtvalue=$2
+
+ ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}"
+
+ if [ -z "$ZM_Key" ]; then
+ ZM_Key=""
+ _err "You don't specify zonomi api key yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ #save the api key to the account conf file.
+ _saveaccountconf_mutable ZM_Key "$ZM_Key"
+
+ _info "Get existing txt records for $fulldomain"
+ if ! _zm_request "action=QUERY&name=$fulldomain"; then
+ _err "error"
+ return 1
+ fi
+
+ if _contains "$response" "' | tr "<" "\n" | grep record | grep 'type="TXT"' | cut -d '"' -f 6); do
+ _debug2 t "$t"
+ _qstr="$_qstr&action[$_qindex]=SET&type[$_qindex]=TXT&name[$_qindex]=$fulldomain&value[$_qindex]=$t"
+ _qindex="$(_math "$_qindex" + 1)"
+ done
+ _zm_request "$_qstr"
+ else
+ _debug "Just add record"
+ _zm_request "action=SET&type=TXT&name=$fulldomain&value=$txtvalue"
+ fi
+
+}
+
+#fulldomain txtvalue
+dns_zonomi_rm() {
+ fulldomain=$1
+ txtvalue=$2
+
+ ZM_Key="${ZM_Key:-$(_readaccountconf_mutable ZM_Key)}"
+ if [ -z "$ZM_Key" ]; then
+ ZM_Key=""
+ _err "You don't specify zonomi api key yet."
+ _err "Please create your key and try again."
+ return 1
+ fi
+
+ _zm_request "action=DELETE&type=TXT&name=$fulldomain"
+
+}
+
+#################### Private functions below ##################################
+#qstr
+_zm_request() {
+ qstr="$1"
+
+ _debug2 "qstr" "$qstr"
+
+ _zm_url="$ZM_Api?api_key=$ZM_Key&$qstr"
+ _debug2 "_zm_url" "$_zm_url"
+ response="$(_get "$_zm_url")"
+
+ if [ "$?" != "0" ]; then
+ return 1
+ fi
+ _debug2 response "$response"
+ _contains "$response" "OK:"
+}
diff --git a/le.sh b/le.sh
deleted file mode 100755
index bb99dfd6..00000000
--- a/le.sh
+++ /dev/null
@@ -1,1288 +0,0 @@
-#!/usr/bin/env bash
-VER=1.1.8
-PROJECT="https://github.com/Neilpang/le"
-
-DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
-DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
-
-STAGE_CA="https://acme-staging.api.letsencrypt.org"
-
-VTYPE_HTTP="http-01"
-VTYPE_DNS="dns-01"
-
-if [ -z "$AGREEMENT" ] ; then
- AGREEMENT="$DEFAULT_AGREEMENT"
-fi
-
-_debug() {
-
- if [ -z "$DEBUG" ] ; then
- return
- fi
-
- if [ -z "$2" ] ; then
- echo $1
- else
- echo "$1"="$2"
- fi
-}
-
-_info() {
- if [ -z "$2" ] ; then
- echo "$1"
- else
- echo "$1"="$2"
- fi
-}
-
-_err() {
- if [ -z "$2" ] ; then
- echo "$1" >&2
- else
- echo "$1"="$2" >&2
- fi
- return 1
-}
-
-_h2b() {
- hex=$(cat)
- i=1
- j=2
- while [ '1' ] ; do
- h=$(printf $hex | cut -c $i-$j)
- if [ -z "$h" ] ; then
- break;
- fi
- printf "\x$h"
- let "i+=2"
- let "j+=2"
- done
-}
-
-_base64() {
- openssl base64 -e | tr -d '\n'
-}
-
-#domain [2048]
-createAccountKey() {
- _info "Creating account key"
- if [ -z "$1" ] ; then
- echo Usage: createAccountKey account-domain [2048]
- return
- fi
-
- account=$1
- length=$2
-
- if [[ "$length" == "ec-"* ]] ; then
- length=2048
- fi
-
- if [ -z "$2" ] ; then
- _info "Use default length 2048"
- length=2048
- fi
- _initpath
-
- if [ -f "$ACCOUNT_KEY_PATH" ] ; then
- _info "Account key exists, skip"
- return
- else
- #generate account key
- openssl genrsa $length > "$ACCOUNT_KEY_PATH"
- fi
-
-}
-
-#domain length
-createDomainKey() {
- _info "Creating domain key"
- if [ -z "$1" ] ; then
- echo Usage: createDomainKey domain [2048]
- return
- fi
-
- domain=$1
- length=$2
- isec=""
- if [[ "$length" == "ec-"* ]] ; then
- isec="1"
- length=$(printf $length | cut -d '-' -f 2-100)
- eccname="$length"
- fi
-
- if [ -z "$length" ] ; then
- if [ "$isec" ] ; then
- length=256
- else
- length=2048
- fi
- fi
- _info "Use length $length"
-
- if [ "$isec" ] ; then
- if [ "$length" == "256" ] ; then
- eccname="prime256v1"
- fi
- if [ "$length" == "384" ] ; then
- eccname="secp384r1"
- fi
- if [ "$length" == "521" ] ; then
- eccname="secp521r1"
- fi
- _info "Using ec name: $eccname"
- fi
-
- _initpath $domain
-
- if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
- #generate account key
- if [ "$isec" ] ; then
- openssl ecparam -name $eccname -genkey 2>/dev/null > "$CERT_KEY_PATH"
- else
- openssl genrsa $length 2>/dev/null > "$CERT_KEY_PATH"
- fi
- else
- if [ "$IS_RENEW" ] ; then
- _info "Domain key exists, skip"
- return 0
- else
- _err "Domain key exists, do you want to overwrite the key?"
- _err "Set FORCE=1, and try again."
- return 1
- fi
- fi
-
-}
-
-# domain domainlist
-createCSR() {
- _info "Creating csr"
- if [ -z "$1" ] ; then
- echo Usage: $0 domain [domainlist]
- return
- fi
- domain=$1
- _initpath $domain
-
- domainlist=$2
-
- if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && ! [ "$FORCE" ]; then
- _info "CSR exists, skip"
- return
- fi
-
- if [ -z "$domainlist" ] ; then
- #single domain
- _info "Single domain" $domain
- openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" > "$CSR_PATH"
- else
- alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
- #multi
- _info "Multi domain" "$alt"
- printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n[SAN]\nsubjectAltName=$alt" > "$DOMAIN_SSL_CONF"
- openssl req -new -sha256 -key "$CERT_KEY_PATH" -subj "/CN=$domain" -reqexts SAN -config "$DOMAIN_SSL_CONF" -out "$CSR_PATH"
- fi
-
-}
-
-_b64() {
- __n=$(cat)
- echo $__n | tr '/+' '_-' | tr -d '= '
-}
-
-_time2str() {
- #BSD
- if date -u -d@$1 2>/dev/null ; then
- return
- fi
-
- #Linux
- if date -u -r $1 2>/dev/null ; then
- return
- fi
-
-}
-
-_send_signed_request() {
- url=$1
- payload=$2
- needbase64=$3
-
- _debug url $url
- _debug payload "$payload"
-
- CURL_HEADER="$LE_WORKING_DIR/curl.header"
- dp="$LE_WORKING_DIR/curl.dump"
- CURL="curl --silent --dump-header $CURL_HEADER "
- if [ "$DEBUG" ] ; then
- CURL="$CURL --trace-ascii $dp "
- fi
- payload64=$(echo -n $payload | _base64 | _b64)
- _debug payload64 $payload64
-
- nonceurl="$API/directory"
- nonce="$($CURL -I $nonceurl | grep -o "^Replay-Nonce:.*$" | tr -d "\r\n" | cut -d ' ' -f 2)"
-
- _debug nonce "$nonce"
-
- protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
- _debug protected "$protected"
-
- protected64="$(printf "$protected" | _base64 | _b64)"
- _debug protected64 "$protected64"
-
- sig=$(echo -n "$protected64.$payload64" | openssl dgst -sha256 -sign $ACCOUNT_KEY_PATH | _base64 | _b64)
- _debug sig "$sig"
-
- body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
- _debug body "$body"
-
- if [ "$needbase64" ] ; then
- response="$($CURL -X POST --data "$body" $url | _base64)"
- else
- response="$($CURL -X POST --data "$body" $url)"
- fi
-
- responseHeaders="$(cat $CURL_HEADER)"
-
- _debug responseHeaders "$responseHeaders"
- _debug response "$response"
- code="$(grep ^HTTP $CURL_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
- _debug code $code
-
-}
-
-_get() {
- url="$1"
- _debug url $url
- response="$(curl --silent $url)"
- ret=$?
- _debug response "$response"
- code="$(echo $response | grep -o '"status":[0-9]\+' | cut -d : -f 2)"
- _debug code $code
- return $ret
-}
-
-#setopt "file" "opt" "=" "value" [";"]
-_setopt() {
- __conf="$1"
- __opt="$2"
- __sep="$3"
- __val="$4"
- __end="$5"
- if [ -z "$__opt" ] ; then
- echo usage: $0 '"file" "opt" "=" "value" [";"]'
- return
- fi
- if [ ! -f "$__conf" ] ; then
- touch "$__conf"
- fi
- if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
- _debug OK
- if [[ "$__val" == *"&"* ]] ; then
- __val="$(echo $__val | sed 's/&/\\&/g')"
- fi
- text="$(cat $__conf)"
- printf "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
- else
- _debug APP
- echo "$__opt$__sep$__val$__end" >> "$__conf"
- fi
- _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
-}
-
-#_savedomainconf key value
-#save to domain.conf
-_savedomainconf() {
- key="$1"
- value="$2"
- if [ "$DOMAIN_CONF" ] ; then
- _setopt $DOMAIN_CONF "$key" "=" "$value"
- else
- _err "DOMAIN_CONF is empty, can not save $key=$value"
- fi
-}
-
-#_saveaccountconf key value
-_saveaccountconf() {
- key="$1"
- value="$2"
- if [ "$ACCOUNT_CONF_PATH" ] ; then
- _setopt $ACCOUNT_CONF_PATH "$key" "=" "$value"
- else
- _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
- fi
-}
-
-_startserver() {
- content="$1"
- _NC="nc -q 1"
- if nc -h 2>&1 | grep "nmap.org/ncat" >/dev/null ; then
- _NC="nc"
- fi
-# while true ; do
- if [ "$DEBUG" ] ; then
- echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort -vv
- else
- echo -e -n "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -l -p $Le_HTTPPort > /dev/null
- fi
-# done
-}
-
-_stopserver() {
- pid="$1"
-
-}
-
-_initpath() {
-
- if [ -z "$LE_WORKING_DIR" ]; then
- LE_WORKING_DIR=$HOME/.le
- fi
-
- if [ -z "$ACCOUNT_CONF_PATH" ] ; then
- ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
- fi
-
- if [ -f "$ACCOUNT_CONF_PATH" ] ; then
- source "$ACCOUNT_CONF_PATH"
- fi
-
- if [ -z "$API" ] ; then
- if [ -z "$STAGE" ] ; then
- API="$DEFAULT_CA"
- else
- API="$STAGE_CA"
- _info "Using stage api:$API"
- fi
- fi
-
- if [ -z "$ACME_DIR" ] ; then
- ACME_DIR="/home/.acme"
- fi
-
- if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
- APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR/"
- fi
-
- domain="$1"
- mkdir -p "$LE_WORKING_DIR"
-
- if [ -z "$ACCOUNT_KEY_PATH" ] ; then
- ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
- fi
-
- if [ -z "$domain" ] ; then
- return 0
- fi
-
- domainhome="$LE_WORKING_DIR/$domain"
- mkdir -p "$domainhome"
-
- if [ -z "$DOMAIN_CONF" ] ; then
- DOMAIN_CONF="$domainhome/$Le_Domain.conf"
- fi
-
- if [ -z "$DOMAIN_SSL_CONF" ] ; then
- DOMAIN_SSL_CONF="$domainhome/$Le_Domain.ssl.conf"
- fi
-
- if [ -z "$CSR_PATH" ] ; then
- CSR_PATH="$domainhome/$domain.csr"
- fi
- if [ -z "$CERT_KEY_PATH" ] ; then
- CERT_KEY_PATH="$domainhome/$domain.key"
- fi
- if [ -z "$CERT_PATH" ] ; then
- CERT_PATH="$domainhome/$domain.cer"
- fi
- if [ -z "$CA_CERT_PATH" ] ; then
- CA_CERT_PATH="$domainhome/ca.cer"
- fi
-
-}
-
-
-_apachePath() {
- httpdroot="$(apachectl -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
- httpdconfname="$(apachectl -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
- httpdconf="$httpdroot/$httpdconfname"
- if [ ! -f $httpdconf ] ; then
- _err "Apache Config file not found" $httpdconf
- return 1
- fi
- return 0
-}
-
-_restoreApache() {
- if [ -z "$usingApache" ] ; then
- return 0
- fi
- _initpath
- if ! _apachePath ; then
- return 1
- fi
-
- if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
- _debug "No config file to restore."
- return 0
- fi
-
- cp -p "$APACHE_CONF_BACKUP_DIR/$httpdconfname" "$httpdconf"
- if ! apachectl -t ; then
- _err "Sorry, restore apache config error, please contact me."
- return 1;
- fi
- rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
- return 0
-}
-
-_setApache() {
- _initpath
- if ! _apachePath ; then
- return 1
- fi
-
- #backup the conf
- _debug "Backup apache config file" $httpdconf
- cp -p $httpdconf $APACHE_CONF_BACKUP_DIR/
- _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
- _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
- _info "The backup file will be deleted on sucess, just forget it."
-
- #add alias
- echo "
-Alias /.well-known/acme-challenge $ACME_DIR
-
-
-Require all granted
-
- " >> $httpdconf
-
- if ! apachectl -t ; then
- _err "Sorry, apache config error, please contact me."
- _restoreApache
- return 1;
- fi
-
- if [ ! -d "$ACME_DIR" ] ; then
- mkdir -p "$ACME_DIR"
- chmod 755 "$ACME_DIR"
- fi
-
- if ! apachectl graceful ; then
- _err "Sorry, apachectl graceful error, please contact me."
- _restoreApache
- return 1;
- fi
- usingApache="1"
- return 0
-}
-
-_clearup () {
- _stopserver $serverproc
- serverproc=""
- _restoreApache
-}
-
-# webroot removelevel tokenfile
-_clearupwebbroot() {
- __webroot="$1"
- if [ -z "$__webroot" ] ; then
- _debug "no webroot specified, skip"
- return 0
- fi
-
- if [ "$2" == '1' ] ; then
- _debug "remove $__webroot/.well-known"
- rm -rf "$__webroot/.well-known"
- elif [ "$2" == '2' ] ; then
- _debug "remove $__webroot/.well-known/acme-challenge"
- rm -rf "$__webroot/.well-known/acme-challenge"
- elif [ "$2" == '3' ] ; then
- _debug "remove $__webroot/.well-known/acme-challenge/$3"
- rm -rf "$__webroot/.well-known/acme-challenge/$3"
- else
- _info "Skip for removelevel:$2"
- fi
-
- return 0
-
-}
-
-issue() {
- if [ -z "$2" ] ; then
- _err "Usage: le issue webroot|no|apache|dns a.com [www.a.com,b.com,c.com]|no [key-length]|no"
- return 1
- fi
- Le_Webroot="$1"
- Le_Domain="$2"
- Le_Alt="$3"
- Le_Keylength="$4"
- Le_RealCertPath="$5"
- Le_RealKeyPath="$6"
- Le_RealCACertPath="$7"
- Le_ReloadCmd="$8"
-
-
- _initpath $Le_Domain
-
- if [ -f "$DOMAIN_CONF" ] ; then
- Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2)
- if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
- _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
- return 2
- fi
- fi
-
- if [ "$Le_Alt" == "no" ] ; then
- Le_Alt=""
- fi
- if [ "$Le_Keylength" == "no" ] ; then
- Le_Keylength=""
- fi
- if [ "$Le_RealCertPath" == "no" ] ; then
- Le_RealCertPath=""
- fi
- if [ "$Le_RealKeyPath" == "no" ] ; then
- Le_RealKeyPath=""
- fi
- if [ "$Le_RealCACertPath" == "no" ] ; then
- Le_RealCACertPath=""
- fi
- if [ "$Le_ReloadCmd" == "no" ] ; then
- Le_ReloadCmd=""
- fi
-
- _setopt "$DOMAIN_CONF" "Le_Domain" "=" "$Le_Domain"
- _setopt "$DOMAIN_CONF" "Le_Alt" "=" "$Le_Alt"
- _setopt "$DOMAIN_CONF" "Le_Webroot" "=" "$Le_Webroot"
- _setopt "$DOMAIN_CONF" "Le_Keylength" "=" "$Le_Keylength"
- _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
- _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
- _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
- _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
-
- if [ "$Le_Webroot" == "no" ] ; then
- _info "Standalone mode."
- if ! command -v "nc" > /dev/null ; then
- _err "Please install netcat(nc) tools first."
- return 1
- fi
-
- if [ -z "$Le_HTTPPort" ] ; then
- Le_HTTPPort=80
- fi
- _setopt "$DOMAIN_CONF" "Le_HTTPPort" "=" "$Le_HTTPPort"
-
- netprc="$(ss -ntpl | grep :$Le_HTTPPort" ")"
- if [ "$netprc" ] ; then
- _err "$netprc"
- _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
- _err "Please stop it first"
- return 1
- fi
- fi
-
- if [ "$Le_Webroot" == "apache" ] ; then
- if ! _setApache ; then
- _err "set up apache error. Report error to me."
- return 1
- fi
- wellknown_path="$ACME_DIR"
- else
- usingApache=""
- fi
-
- createAccountKey $Le_Domain $Le_Keylength
-
- if ! createDomainKey $Le_Domain $Le_Keylength ; then
- _err "Create domain key error."
- return 1
- fi
-
- if ! createCSR $Le_Domain $Le_Alt ; then
- _err "Create CSR error."
- return 1
- fi
-
- pub_exp=$(openssl rsa -in $ACCOUNT_KEY_PATH -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
- if [ "${#pub_exp}" == "5" ] ; then
- pub_exp=0$pub_exp
- fi
- _debug pub_exp "$pub_exp"
-
- e=$(echo $pub_exp | _h2b | _base64)
- _debug e "$e"
-
- modulus=$(openssl rsa -in $ACCOUNT_KEY_PATH -modulus -noout | cut -d '=' -f 2 )
- n=$(echo $modulus| _h2b | _base64 | _b64 )
-
- jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
-
- HEADER='{"alg": "RS256", "jwk": '$jwk'}'
- HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
- _debug HEADER "$HEADER"
-
- accountkey_json=$(echo -n "$jwk" | tr -d ' ' )
- thumbprint=$(echo -n "$accountkey_json" | openssl dgst -sha256 -binary | _base64 | _b64)
-
-
- _info "Registering account"
- regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
- if [ "$ACCOUNT_EMAIL" ] ; then
- regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
- fi
- _send_signed_request "$API/acme/new-reg" "$regjson"
-
- if [ "$code" == "" ] || [ "$code" == '201' ] ; then
- _info "Registered"
- echo $response > $LE_WORKING_DIR/account.json
- elif [ "$code" == '409' ] ; then
- _info "Already registered"
- else
- _err "Register account Error."
- _clearup
- return 1
- fi
-
- vtype="$VTYPE_HTTP"
- if [[ "$Le_Webroot" == "dns"* ]] ; then
- vtype="$VTYPE_DNS"
- fi
-
- vlist="$Le_Vlist"
- # verify each domain
- _info "Verify each domain"
- sep='#'
- if [ -z "$vlist" ] ; then
- alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
- for d in $alldomains
- do
- _info "Geting token for domain" $d
- _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}"
- if [ ! -z "$code" ] && [ ! "$code" == '201' ] ; then
- _err "new-authz error: $response"
- _clearup
- return 1
- fi
-
- entry="$(printf $response | egrep -o '{[^{]*"type":"'$vtype'"[^}]*')"
- _debug entry "$entry"
-
- token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
- _debug token $token
-
- uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
- _debug uri $uri
-
- keyauthorization="$token.$thumbprint"
- _debug keyauthorization "$keyauthorization"
-
- dvlist="$d$sep$keyauthorization$sep$uri"
- _debug dvlist "$dvlist"
-
- vlist="$vlist$dvlist,"
-
- done
-
- #add entry
- dnsadded=""
- ventries=$(echo "$vlist" | tr ',' ' ' )
- for ventry in $ventries
- do
- d=$(echo $ventry | cut -d $sep -f 1)
- keyauthorization=$(echo $ventry | cut -d $sep -f 2)
-
- if [ "$vtype" == "$VTYPE_DNS" ] ; then
- dnsadded='0'
- txtdomain="_acme-challenge.$d"
- _debug txtdomain "$txtdomain"
- txt="$(echo -e -n $keyauthorization | openssl dgst -sha256 -binary | _base64 | _b64)"
- _debug txt "$txt"
- #dns
- #1. check use api
- d_api=""
- if [ -f "$LE_WORKING_DIR/$d/$Le_Webroot" ] ; then
- d_api="$LE_WORKING_DIR/$d/$Le_Webroot"
- elif [ -f "$LE_WORKING_DIR/$d/$Le_Webroot.sh" ] ; then
- d_api="$LE_WORKING_DIR/$d/$Le_Webroot.sh"
- elif [ -f "$LE_WORKING_DIR/$Le_Webroot" ] ; then
- d_api="$LE_WORKING_DIR/$Le_Webroot"
- elif [ -f "$LE_WORKING_DIR/$Le_Webroot.sh" ] ; then
- d_api="$LE_WORKING_DIR/$Le_Webroot.sh"
- elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot" ] ; then
- d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot"
- elif [ -f "$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh" ] ; then
- d_api="$LE_WORKING_DIR/dnsapi/$Le_Webroot.sh"
- fi
- _debug d_api "$d_api"
-
- if [ "$d_api" ]; then
- _info "Found domain api file: $d_api"
- else
- _err "Add the following TXT record:"
- _err "Domain: $txtdomain"
- _err "TXT value: $txt"
- _err "Please be aware that you prepend _acme-challenge. before your domain"
- _err "so the resulting subdomain will be: $txtdomain"
- continue
- fi
-
- if ! source $d_api ; then
- _err "Load file $d_api error. Please check your api file and try again."
- return 1
- fi
-
- addcommand="$Le_Webroot-add"
- if ! command -v $addcommand ; then
- _err "It seems that your api file is not correct, it must have a function named: $Le_Webroot"
- return 1
- fi
-
- if ! $addcommand $txtdomain $txt ; then
- _err "Error add txt for domain:$txtdomain"
- return 1
- fi
- dnsadded='1'
- fi
- done
-
- if [ "$dnsadded" == '0' ] ; then
- _setopt "$DOMAIN_CONF" "Le_Vlist" "=" "\"$vlist\""
- _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
- _err "Please add the TXT records to the domains, and retry again."
- return 1
- fi
-
- fi
-
- if [ "$dnsadded" == '1' ] ; then
- _info "Sleep 60 seconds for the txt records to take effect"
- sleep 60
- fi
-
- _debug "ok, let's start to verify"
- ventries=$(echo "$vlist" | tr ',' ' ' )
- for ventry in $ventries
- do
- d=$(echo $ventry | cut -d $sep -f 1)
- keyauthorization=$(echo $ventry | cut -d $sep -f 2)
- uri=$(echo $ventry | cut -d $sep -f 3)
- _info "Verifying:$d"
- _debug "d" "$d"
- _debug "keyauthorization" "$keyauthorization"
- _debug "uri" "$uri"
- removelevel=""
- token=""
- if [ "$vtype" == "$VTYPE_HTTP" ] ; then
- if [ "$Le_Webroot" == "no" ] ; then
- _info "Standalone mode server"
- _startserver "$keyauthorization" &
- serverproc="$!"
- sleep 2
- _debug serverproc $serverproc
- else
- if [ -z "$wellknown_path" ] ; then
- wellknown_path="$Le_Webroot/.well-known/acme-challenge"
- fi
- _debug wellknown_path "$wellknown_path"
-
- if [ ! -d "$Le_Webroot/.well-known" ] ; then
- removelevel='1'
- elif [ ! -d "$Le_Webroot/.well-known/acme-challenge" ] ; then
- removelevel='2'
- else
- removelevel='3'
- fi
-
- token="$(echo -e -n "$keyauthorization" | cut -d '.' -f 1)"
- _debug "writing token:$token to $wellknown_path/$token"
-
- mkdir -p "$wellknown_path"
- echo -n "$keyauthorization" > "$wellknown_path/$token"
-
- webroot_owner=$(stat -c '%U:%G' $Le_Webroot)
- _debug "Changing owner/group of .well-known to $webroot_owner"
- chown -R $webroot_owner "$Le_Webroot/.well-known"
-
- fi
- fi
-
- _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}"
-
- if [ ! -z "$code" ] && [ ! "$code" == '202' ] ; then
- _err "$d:Challenge error: $resource"
- _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
- _clearup
- return 1
- fi
-
- while [ "1" ] ; do
- _debug "sleep 5 secs to verify"
- sleep 5
- _debug "checking"
-
- if ! _get $uri ; then
- _err "$d:Verify error:$resource"
- _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
- _clearup
- return 1
- fi
-
- status=$(echo $response | egrep -o '"status":"[^"]+"' | cut -d : -f 2 | tr -d '"')
- if [ "$status" == "valid" ] ; then
- _info "Success"
- _stopserver $serverproc
- serverproc=""
- _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
- break;
- fi
-
- if [ "$status" == "invalid" ] ; then
- error=$(echo $response | egrep -o '"error":{[^}]*}' | grep -o '"detail":"[^"]*"' | cut -d '"' -f 4)
- _err "$d:Verify error:$error"
- _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
- _clearup
- return 1;
- fi
-
- if [ "$status" == "pending" ] ; then
- _info "Pending"
- else
- _err "$d:Verify error:$response"
- _clearupwebbroot "$Le_Webroot" "$removelevel" "$token"
- _clearup
- return 1
- fi
-
- done
-
- done
-
- _clearup
- _info "Verify finished, start to sign."
- der="$(openssl req -in $CSR_PATH -outform DER | _base64 | _b64)"
- _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64"
-
-
- Le_LinkCert="$(grep -i -o '^Location.*$' $CURL_HEADER | tr -d "\r\n" | cut -d " " -f 2)"
- _setopt "$DOMAIN_CONF" "Le_LinkCert" "=" "$Le_LinkCert"
-
- if [ "$Le_LinkCert" ] ; then
- echo -----BEGIN CERTIFICATE----- > "$CERT_PATH"
- curl --silent "$Le_LinkCert" | openssl base64 -e >> "$CERT_PATH"
- echo -----END CERTIFICATE----- >> "$CERT_PATH"
- _info "Cert success."
- cat "$CERT_PATH"
-
- _info "Your cert is in $CERT_PATH"
- fi
-
-
- if [ -z "$Le_LinkCert" ] ; then
- response="$(echo $response | openssl base64 -d -A)"
- _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
- return 1
- fi
-
- _setopt "$DOMAIN_CONF" 'Le_Vlist' '=' "\"\""
-
- Le_LinkIssuer=$(grep -i '^Link' $CURL_HEADER | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
- _setopt "$DOMAIN_CONF" "Le_LinkIssuer" "=" "$Le_LinkIssuer"
-
- if [ "$Le_LinkIssuer" ] ; then
- echo -----BEGIN CERTIFICATE----- > "$CA_CERT_PATH"
- curl --silent "$Le_LinkIssuer" | openssl base64 -e >> "$CA_CERT_PATH"
- echo -----END CERTIFICATE----- >> "$CA_CERT_PATH"
- _info "The intermediate CA cert is in $CA_CERT_PATH"
- fi
-
- Le_CertCreateTime=$(date -u "+%s")
- _setopt "$DOMAIN_CONF" "Le_CertCreateTime" "=" "$Le_CertCreateTime"
-
- Le_CertCreateTimeStr=$(date -u )
- _setopt "$DOMAIN_CONF" "Le_CertCreateTimeStr" "=" "\"$Le_CertCreateTimeStr\""
-
- if [ ! "$Le_RenewalDays" ] ; then
- Le_RenewalDays=80
- fi
-
- _setopt "$DOMAIN_CONF" "Le_RenewalDays" "=" "$Le_RenewalDays"
-
- let "Le_NextRenewTime=Le_CertCreateTime+Le_RenewalDays*24*60*60"
- _setopt "$DOMAIN_CONF" "Le_NextRenewTime" "=" "$Le_NextRenewTime"
-
- Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
- _setopt "$DOMAIN_CONF" "Le_NextRenewTimeStr" "=" "\"$Le_NextRenewTimeStr\""
-
-
- installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
-
-}
-
-renew() {
- Le_Domain="$1"
- if [ -z "$Le_Domain" ] ; then
- _err "Usage: $0 domain.com"
- return 1
- fi
-
- _initpath $Le_Domain
-
- if [ ! -f "$DOMAIN_CONF" ] ; then
- _info "$Le_Domain is not a issued domain, skip."
- return 0;
- fi
-
- source "$DOMAIN_CONF"
- if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
- _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
- return 2
- fi
-
- IS_RENEW="1"
- issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd"
- local res=$?
- IS_RENEW=""
-
- return $res
-}
-
-renewAll() {
- _initpath
- _info "renewAll"
-
- for d in $(ls -F $LE_WORKING_DIR | grep [^.].*[.].*/$ ) ; do
- d=$(echo $d | cut -d '/' -f 1)
- _info "renew $d"
-
- Le_LinkCert=""
- Le_Domain=""
- Le_Alt=""
- Le_Webroot=""
- Le_Keylength=""
- Le_LinkIssuer=""
-
- Le_CertCreateTime=""
- Le_CertCreateTimeStr=""
- Le_RenewalDays=""
- Le_NextRenewTime=""
- Le_NextRenewTimeStr=""
-
- Le_RealCertPath=""
- Le_RealKeyPath=""
-
- Le_RealCACertPath=""
-
- Le_ReloadCmd=""
-
- DOMAIN_CONF=""
- DOMAIN_SSL_CONF=""
- CSR_PATH=""
- CERT_KEY_PATH=""
- CERT_PATH=""
- CA_CERT_PATH=""
- ACCOUNT_KEY_PATH=""
-
- wellknown_path=""
-
- renew "$d"
- done
-
-}
-
-installcert() {
- Le_Domain="$1"
- if [ -z "$Le_Domain" ] ; then
- _err "Usage: $0 domain.com [cert-file-path]|no [key-file-path]|no [ca-cert-file-path]|no [reloadCmd]|no"
- return 1
- fi
-
- Le_RealCertPath="$2"
- Le_RealKeyPath="$3"
- Le_RealCACertPath="$4"
- Le_ReloadCmd="$5"
-
- _initpath $Le_Domain
-
- _setopt "$DOMAIN_CONF" "Le_RealCertPath" "=" "\"$Le_RealCertPath\""
- _setopt "$DOMAIN_CONF" "Le_RealCACertPath" "=" "\"$Le_RealCACertPath\""
- _setopt "$DOMAIN_CONF" "Le_RealKeyPath" "=" "\"$Le_RealKeyPath\""
- _setopt "$DOMAIN_CONF" "Le_ReloadCmd" "=" "\"$Le_ReloadCmd\""
-
- if [ "$Le_RealCertPath" ] ; then
- if [ -f "$Le_RealCertPath" ] ; then
- cp -p "$Le_RealCertPath" "$Le_RealCertPath".bak
- fi
- cat "$CERT_PATH" > "$Le_RealCertPath"
- fi
-
- if [ "$Le_RealCACertPath" ] ; then
- if [ -f "$Le_RealCACertPath" ] ; then
- cp -p "$Le_RealCACertPath" "$Le_RealCACertPath".bak
- fi
- if [ "$Le_RealCACertPath" == "$Le_RealCertPath" ] ; then
- echo "" >> "$Le_RealCACertPath"
- cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
- else
- cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
- fi
- fi
-
-
- if [ "$Le_RealKeyPath" ] ; then
- if [ -f "$Le_RealKeyPath" ] ; then
- cp -p "$Le_RealKeyPath" "$Le_RealKeyPath".bak
- fi
- cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
- fi
-
- if [ "$Le_ReloadCmd" ] ; then
- _info "Run Le_ReloadCmd: $Le_ReloadCmd"
- eval $Le_ReloadCmd
- fi
-
-}
-
-installcronjob() {
- _initpath
- _info "Installing cron job"
- if ! crontab -l | grep 'le.sh cron' ; then
- if [ -f "$LE_WORKING_DIR/le.sh" ] ; then
- lesh="\"$LE_WORKING_DIR\"/le.sh"
- else
- _err "Can not install cronjob, le.sh not found."
- return 1
- fi
- crontab -l | { cat; echo "0 0 * * * LE_WORKING_DIR=\"$LE_WORKING_DIR\" $lesh cron > /dev/null"; } | crontab -
- fi
- return 0
-}
-
-uninstallcronjob() {
- _info "Removing cron job"
- cr="$(crontab -l | grep 'le.sh cron')"
- if [ "$cr" ] ; then
- crontab -l | sed "/le.sh cron/d" | crontab -
- LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 6 | cut -d '=' -f 2 | tr -d '"')"
- _info LE_WORKING_DIR "$LE_WORKING_DIR"
- fi
- _initpath
-
-}
-
-
-# Detect profile file if not specified as environment variable
-_detect_profile() {
- if [ -n "$PROFILE" -a -f "$PROFILE" ]; then
- echo "$PROFILE"
- return
- fi
-
- local DETECTED_PROFILE
- DETECTED_PROFILE=''
- local SHELLTYPE
- SHELLTYPE="$(basename "/$SHELL")"
-
- if [ "$SHELLTYPE" = "bash" ]; then
- if [ -f "$HOME/.bashrc" ]; then
- DETECTED_PROFILE="$HOME/.bashrc"
- elif [ -f "$HOME/.bash_profile" ]; then
- DETECTED_PROFILE="$HOME/.bash_profile"
- fi
- elif [ "$SHELLTYPE" = "zsh" ]; then
- DETECTED_PROFILE="$HOME/.zshrc"
- fi
-
- if [ -z "$DETECTED_PROFILE" ]; then
- if [ -f "$HOME/.profile" ]; then
- DETECTED_PROFILE="$HOME/.profile"
- elif [ -f "$HOME/.bashrc" ]; then
- DETECTED_PROFILE="$HOME/.bashrc"
- elif [ -f "$HOME/.bash_profile" ]; then
- DETECTED_PROFILE="$HOME/.bash_profile"
- elif [ -f "$HOME/.zshrc" ]; then
- DETECTED_PROFILE="$HOME/.zshrc"
- fi
- fi
-
- if [ ! -z "$DETECTED_PROFILE" ]; then
- echo "$DETECTED_PROFILE"
- fi
-}
-
-_initconf() {
- _initpath
- if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
- echo "#Account configurations:
-#Here are the supported macros, uncomment them to make them take effect.
-#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
-
-#STAGE=1 # Use the staging api
-#FORCE=1 # Force to issue cert
-#DEBUG=1 # Debug mode
-
-#dns api
-#######################
-#Cloudflare:
-#api key
-#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
-#account email
-#CF_Email="xxxx@sss.com"
-
-#######################
-#Dnspod.cn:
-#api key id
-#DP_Id="1234"
-#api key
-#DP_Key="sADDsdasdgdsf"
-
-#######################
-#Cloudxns.com:
-#CX_Key="1234"
-#
-#CX_Secret="sADDsdasdgdsf"
-
- " > $ACCOUNT_CONF_PATH
- fi
-}
-
-install() {
- _initpath
-
- #check if there is sudo installed, AND if the current user is a sudoer.
- if command -v sudo > /dev/null ; then
- if [ "$(sudo -n uptime 2>&1|grep "load"|wc -l)" != "0" ] ; then
- SUDO=sudo
- fi
- fi
-
- if command -v yum > /dev/null ; then
- YUM="1"
- INSTALL="$SUDO yum install -y "
- elif command -v apt-get > /dev/null ; then
- INSTALL="$SUDO apt-get install -y "
- fi
-
- if ! command -v "curl" > /dev/null ; then
- _err "Please install curl first."
- _err "$INSTALL curl"
- return 1
- fi
-
- if ! command -v "crontab" > /dev/null ; then
- _err "Please install crontab first."
- if [ "$YUM" ] ; then
- _err "$INSTALL crontabs"
- else
- _err "$INSTALL crontab"
- fi
- return 1
- fi
-
- if ! command -v "openssl" > /dev/null ; then
- _err "Please install openssl first."
- _err "$INSTALL openssl"
- return 1
- fi
-
- _info "Installing to $LE_WORKING_DIR"
-
- _info "Installed to $LE_WORKING_DIR/le.sh"
- cp le.sh $LE_WORKING_DIR/
- chmod +x $LE_WORKING_DIR/le.sh
-
- _profile="$(_detect_profile)"
- if [ "$_profile" ] ; then
- _debug "Found profile: $_profile"
-
- echo "LE_WORKING_DIR=$LE_WORKING_DIR
-alias le=\"$LE_WORKING_DIR/le.sh\"
-alias le.sh=\"$LE_WORKING_DIR/le.sh\"
- " > "$LE_WORKING_DIR/le.env"
-
- _setopt "$_profile" "source \"$LE_WORKING_DIR/le.env\""
- _info "OK, Close and reopen your terminal to start using le"
- else
- _info "No profile is found, you will need to go into $LE_WORKING_DIR to use le.sh"
- fi
-
- mkdir -p $LE_WORKING_DIR/dnsapi
- cp dnsapi/* $LE_WORKING_DIR/dnsapi/
-
- #to keep compatible mv the .acc file to .key file
- if [ -f "$LE_WORKING_DIR/account.acc" ] ; then
- mv "$LE_WORKING_DIR/account.acc" "$LE_WORKING_DIR/account.key"
- fi
-
- installcronjob
-
- if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
- _initconf
- fi
- _info OK
-}
-
-uninstall() {
- uninstallcronjob
- _initpath
-
- _profile="$(_detect_profile)"
- if [ "$_profile" ] ; then
- sed -i /le.env/d "$_profile"
- fi
-
- rm -f $LE_WORKING_DIR/le.sh
- _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
-
-}
-
-cron() {
- renewAll
-}
-
-version() {
- _info "$PROJECT"
- _info "v$VER"
-}
-
-showhelp() {
- version
- echo "Usage: le.sh [command] ...[args]....
-Avalible commands:
-
-install:
- Install le.sh to your system.
-issue:
- Issue a cert.
-installcert:
- Install the issued cert to apache/nginx or any other server.
-renew:
- Renew a cert.
-renewAll:
- Renew all the certs.
-uninstall:
- Uninstall le.sh, and uninstall the cron job.
-version:
- Show version info.
-installcronjob:
- Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
-uninstallcronjob:
- Uninstall the cron job. The 'uninstall' command can do this automatically.
-createAccountKey:
- Create an account private key, professional use.
-createDomainKey:
- Create an domain private key, professional use.
-createCSR:
- Create CSR , professional use.
- "
-}
-
-
-if [ -z "$1" ] ; then
- showhelp
-else
- "$@"
-fi
|