mirror of
https://github.com/acmesh-official/acme.sh.git
synced 2025-05-09 15:42:43 +00:00
Merge remote-tracking branch 'upstream/master' into dnsapi/dns_dnsever
This commit is contained in:
commit
33bc5aa392
23
.travis.yml
23
.travis.yml
@ -1,10 +1,14 @@
|
|||||||
language: shell
|
language: shell
|
||||||
sudo: required
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
|
- SHFMT_URL=https://github.com/mvdan/sh/releases/download/v0.4.0/shfmt_v0.4.0_linux_amd64
|
||||||
@ -18,21 +22,10 @@ addons:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
|
- if [ "$TRAVIS_OS_NAME" = 'osx' ]; then
|
||||||
brew update && brew install openssl socat;
|
brew update && brew install socat;
|
||||||
brew info openssl;
|
export PATH="/usr/local/opt/openssl@1.1/bin:$PATH" ;
|
||||||
ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/;
|
|
||||||
ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/;
|
|
||||||
ln -s /usr/local/Cellar/openssl/1.0.2j/bin/openssl /usr/local/openssl;
|
|
||||||
_old_path="$PATH";
|
|
||||||
echo "PATH=$PATH";
|
|
||||||
export PATH="";
|
|
||||||
export ACME_OPENSSL_BIN="/usr/local/openssl";
|
|
||||||
openssl version 2>&1 || true;
|
|
||||||
$ACME_OPENSSL_BIN version 2>&1 || true;
|
|
||||||
export PATH="$_old_path";
|
|
||||||
else sudo apt-get install socat;
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
|
- echo "NGROK_TOKEN=$(echo "$NGROK_TOKEN" | wc -c)"
|
||||||
- command -V openssl && openssl version
|
- command -V openssl && openssl version
|
||||||
@ -44,7 +37,7 @@ script:
|
|||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi
|
||||||
- cd ..
|
- cd ..
|
||||||
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
|
- git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./letest.sh ; fi
|
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ./rundocker.sh testplat ubuntu:latest ; fi
|
||||||
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
|
- if [ "$TRAVIS_OS_NAME" = "osx" -a "$NGROK_TOKEN" ]; then sudo TEST_LOCAL="$TEST_LOCAL" NGROK_TOKEN="$NGROK_TOKEN" ACME_OPENSSL_BIN="$ACME_OPENSSL_BIN" ./letest.sh ; fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM alpine
|
FROM alpine:3.6
|
||||||
|
|
||||||
RUN apk update -f \
|
RUN apk update -f \
|
||||||
&& apk --no-cache add -f \
|
&& apk --no-cache add -f \
|
||||||
@ -16,7 +16,7 @@ ADD ./ /install_acme.sh/
|
|||||||
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/acme.sh --install || curl https://get.acme.sh | sh) && rm -rf /install_acme.sh/
|
||||||
|
|
||||||
|
|
||||||
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | sed 's#> /dev/null##' | crontab -
|
RUN ln -s /root/.acme.sh/acme.sh /usr/local/bin/acme.sh && crontab -l | grep acme.sh | sed 's#> /dev/null##' | crontab -
|
||||||
|
|
||||||
RUN for verb in help \
|
RUN for verb in help \
|
||||||
version \
|
version \
|
||||||
|
173
acme.sh
173
acme.sh
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
VER=2.7.4
|
VER=2.7.6
|
||||||
|
|
||||||
PROJECT_NAME="acme.sh"
|
PROJECT_NAME="acme.sh"
|
||||||
|
|
||||||
@ -15,7 +15,6 @@ _SUB_FOLDERS="dnsapi deploy"
|
|||||||
|
|
||||||
_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
|
_OLD_CA_HOST="https://acme-v01.api.letsencrypt.org"
|
||||||
DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory"
|
DEFAULT_CA="https://acme-v01.api.letsencrypt.org/directory"
|
||||||
DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf"
|
|
||||||
|
|
||||||
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
|
DEFAULT_USER_AGENT="$PROJECT_NAME/$VER ($PROJECT)"
|
||||||
DEFAULT_ACCOUNT_EMAIL=""
|
DEFAULT_ACCOUNT_EMAIL=""
|
||||||
@ -463,8 +462,7 @@ if _exists xargs && [ "$(printf %s '\\x41' | xargs printf)" = 'A' ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_h2b() {
|
_h2b() {
|
||||||
if _exists xxd; then
|
if _exists xxd && xxd -r -p 2>/dev/null; then
|
||||||
xxd -r -p
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -901,17 +899,11 @@ _sign() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
|
_sign_openssl="${ACME_OPENSSL_BIN:-openssl} dgst -sign $keyfile "
|
||||||
if [ "$alg" = "sha256" ]; then
|
|
||||||
_sign_openssl="$_sign_openssl -$alg"
|
|
||||||
else
|
|
||||||
_err "$alg is not supported yet"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
|
if grep "BEGIN RSA PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
|
||||||
$_sign_openssl | _base64
|
$_sign_openssl -$alg | _base64
|
||||||
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
|
elif grep "BEGIN EC PRIVATE KEY" "$keyfile" >/dev/null 2>&1; then
|
||||||
if ! _signedECText="$($_sign_openssl | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
|
if ! _signedECText="$($_sign_openssl -sha$__ECC_KEY_LEN | ${ACME_OPENSSL_BIN:-openssl} asn1parse -inform DER)"; then
|
||||||
_err "Sign failed: $_sign_openssl"
|
_err "Sign failed: $_sign_openssl"
|
||||||
_err "Key file: $keyfile"
|
_err "Key file: $keyfile"
|
||||||
_err "Key content:$(wc -l <"$keyfile") lines"
|
_err "Key content:$(wc -l <"$keyfile") lines"
|
||||||
@ -1434,7 +1426,11 @@ _calcjwk() {
|
|||||||
_debug "EC key"
|
_debug "EC key"
|
||||||
crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
|
crv="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
|
||||||
_debug3 crv "$crv"
|
_debug3 crv "$crv"
|
||||||
|
__ECC_KEY_LEN=$(echo "$crv" | cut -d "-" -f 2)
|
||||||
|
if [ "$__ECC_KEY_LEN" = "521" ]; then
|
||||||
|
__ECC_KEY_LEN=512
|
||||||
|
fi
|
||||||
|
_debug3 __ECC_KEY_LEN "$__ECC_KEY_LEN"
|
||||||
if [ -z "$crv" ]; then
|
if [ -z "$crv" ]; then
|
||||||
_debug "Let's try ASN1 OID"
|
_debug "Let's try ASN1 OID"
|
||||||
crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
|
crv_oid="$(${ACME_OPENSSL_BIN:-openssl} ec -in "$keyfile" -noout -text 2>/dev/null | grep "^ASN1 OID:" | cut -d ":" -f 2 | tr -d " \r\n")"
|
||||||
@ -1442,12 +1438,15 @@ _calcjwk() {
|
|||||||
case "${crv_oid}" in
|
case "${crv_oid}" in
|
||||||
"prime256v1")
|
"prime256v1")
|
||||||
crv="P-256"
|
crv="P-256"
|
||||||
|
__ECC_KEY_LEN=256
|
||||||
;;
|
;;
|
||||||
"secp384r1")
|
"secp384r1")
|
||||||
crv="P-384"
|
crv="P-384"
|
||||||
|
__ECC_KEY_LEN=384
|
||||||
;;
|
;;
|
||||||
"secp521r1")
|
"secp521r1")
|
||||||
crv="P-521"
|
crv="P-521"
|
||||||
|
__ECC_KEY_LEN=512
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_err "ECC oid : $crv_oid"
|
_err "ECC oid : $crv_oid"
|
||||||
@ -1489,9 +1488,9 @@ _calcjwk() {
|
|||||||
jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
|
jwk='{"crv": "'$crv'", "kty": "EC", "x": "'$x64'", "y": "'$y64'"}'
|
||||||
_debug3 jwk "$jwk"
|
_debug3 jwk "$jwk"
|
||||||
|
|
||||||
JWK_HEADER='{"alg": "ES256", "jwk": '$jwk'}'
|
JWK_HEADER='{"alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
|
||||||
JWK_HEADERPLACE_PART1='{"nonce": "'
|
JWK_HEADERPLACE_PART1='{"nonce": "'
|
||||||
JWK_HEADERPLACE_PART2='", "alg": "ES256", "jwk": '$jwk'}'
|
JWK_HEADERPLACE_PART2='", "alg": "ES'$__ECC_KEY_LEN'", "jwk": '$jwk'}'
|
||||||
else
|
else
|
||||||
_err "Only RSA or EC key is supported."
|
_err "Only RSA or EC key is supported."
|
||||||
return 1
|
return 1
|
||||||
@ -2054,7 +2053,12 @@ _starttlsserver() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
__S_OPENSSL="socat"
|
__S_OPENSSL="${ACME_OPENSSL_BIN:-openssl} s_server -www -cert $TLS_CERT -key $TLS_KEY "
|
||||||
|
if [ "$opaddr" ]; then
|
||||||
|
__S_OPENSSL="$__S_OPENSSL -accept $opaddr:$port"
|
||||||
|
else
|
||||||
|
__S_OPENSSL="$__S_OPENSSL -accept $port"
|
||||||
|
fi
|
||||||
|
|
||||||
_debug Le_Listen_V4 "$Le_Listen_V4"
|
_debug Le_Listen_V4 "$Le_Listen_V4"
|
||||||
_debug Le_Listen_V6 "$Le_Listen_V6"
|
_debug Le_Listen_V6 "$Le_Listen_V6"
|
||||||
@ -2065,9 +2069,12 @@ _starttlsserver() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
_debug "$__S_OPENSSL"
|
_debug "$__S_OPENSSL"
|
||||||
|
if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then
|
||||||
|
$__S_OPENSSL -tlsextdebug &
|
||||||
|
else
|
||||||
|
$__S_OPENSSL >/dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
|
||||||
#todo listen address
|
|
||||||
$__S_OPENSSL openssl-listen:$port,cert=$TLS_CERT,key=$TLS_KEY,verify=0,reuseaddr,fork SYSTEM:"sleep 0.5; echo HTTP/1.1 200 OK'; echo ; echo $content; echo;" &
|
|
||||||
serverproc="$!"
|
serverproc="$!"
|
||||||
sleep 1
|
sleep 1
|
||||||
_debug serverproc "$serverproc"
|
_debug serverproc "$serverproc"
|
||||||
@ -2153,17 +2160,6 @@ _initAPI() {
|
|||||||
_api_server="${1:-$ACME_DIRECTORY}"
|
_api_server="${1:-$ACME_DIRECTORY}"
|
||||||
_debug "_init api for server: $_api_server"
|
_debug "_init api for server: $_api_server"
|
||||||
|
|
||||||
if [ "$_api_server" = "$DEFAULT_CA" ]; then
|
|
||||||
#just for performance, hardcode the default entry points
|
|
||||||
export ACME_KEY_CHANGE="https://acme-v01.api.letsencrypt.org/acme/key-change"
|
|
||||||
export ACME_NEW_AUTHZ="https://acme-v01.api.letsencrypt.org/acme/new-authz"
|
|
||||||
export ACME_NEW_ORDER="https://acme-v01.api.letsencrypt.org/acme/new-cert"
|
|
||||||
export ACME_NEW_ORDER_RES="new-cert"
|
|
||||||
export ACME_NEW_ACCOUNT="https://acme-v01.api.letsencrypt.org/acme/new-reg"
|
|
||||||
export ACME_NEW_ACCOUNT_RES="new-reg"
|
|
||||||
export ACME_REVOKE_CERT="https://acme-v01.api.letsencrypt.org/acme/revoke-cert"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$ACME_NEW_ACCOUNT" ]; then
|
if [ -z "$ACME_NEW_ACCOUNT" ]; then
|
||||||
response=$(_get "$_api_server")
|
response=$(_get "$_api_server")
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
@ -2203,13 +2199,17 @@ _initAPI() {
|
|||||||
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
|
ACME_NEW_NONCE=$(echo "$response" | _egrep_o 'new-nonce" *: *"[^"]*"' | cut -d '"' -f 3)
|
||||||
export ACME_NEW_NONCE
|
export ACME_NEW_NONCE
|
||||||
|
|
||||||
fi
|
ACME_AGREEMENT=$(echo "$response" | _egrep_o 'terms-of-service" *: *"[^"]*"' | cut -d '"' -f 3)
|
||||||
|
export ACME_AGREEMENT
|
||||||
|
|
||||||
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
|
_debug "ACME_KEY_CHANGE" "$ACME_KEY_CHANGE"
|
||||||
_debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
|
_debug "ACME_NEW_AUTHZ" "$ACME_NEW_AUTHZ"
|
||||||
_debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
|
_debug "ACME_NEW_ORDER" "$ACME_NEW_ORDER"
|
||||||
_debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
|
_debug "ACME_NEW_ACCOUNT" "$ACME_NEW_ACCOUNT"
|
||||||
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
|
_debug "ACME_REVOKE_CERT" "$ACME_REVOKE_CERT"
|
||||||
|
_debug "ACME_AGREEMENT" "$ACME_AGREEMENT"
|
||||||
|
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
#[domain] [keylength or isEcc flag]
|
#[domain] [keylength or isEcc flag]
|
||||||
@ -3051,7 +3051,7 @@ __calc_account_thumbprint() {
|
|||||||
_regAccount() {
|
_regAccount() {
|
||||||
_initpath
|
_initpath
|
||||||
_reg_length="$1"
|
_reg_length="$1"
|
||||||
|
_debug3 _regAccount "$_regAccount"
|
||||||
mkdir -p "$CA_DIR"
|
mkdir -p "$CA_DIR"
|
||||||
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
|
if [ ! -f "$ACCOUNT_KEY_PATH" ] && [ -f "$_OLD_ACCOUNT_KEY" ]; then
|
||||||
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
|
_info "mv $_OLD_ACCOUNT_KEY to $ACCOUNT_KEY_PATH"
|
||||||
@ -3074,75 +3074,45 @@ _regAccount() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_initAPI
|
_initAPI
|
||||||
_updateTos=""
|
|
||||||
_reg_res="$ACME_NEW_ACCOUNT_RES"
|
_reg_res="$ACME_NEW_ACCOUNT_RES"
|
||||||
while true; do
|
regjson='{"resource": "'$_reg_res'", "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
|
||||||
_debug AGREEMENT "$AGREEMENT"
|
if [ "$ACCOUNT_EMAIL" ]; then
|
||||||
|
regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "terms-of-service-agreed": true, "agreement": "'$ACME_AGREEMENT'"}'
|
||||||
|
fi
|
||||||
|
|
||||||
regjson='{"resource": "'$_reg_res'", "agreement": "'$AGREEMENT'"}'
|
_info "Registering account"
|
||||||
|
|
||||||
if [ "$ACCOUNT_EMAIL" ]; then
|
if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
|
||||||
regjson='{"resource": "'$_reg_res'", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
|
_err "Register account Error: $response"
|
||||||
fi
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$_updateTos" ]; then
|
if [ "$code" = "" ] || [ "$code" = '201' ]; then
|
||||||
_info "Registering account"
|
echo "$response" >"$ACCOUNT_JSON_PATH"
|
||||||
|
_info "Registered"
|
||||||
|
elif [ "$code" = '409' ]; then
|
||||||
|
_info "Already registered"
|
||||||
|
else
|
||||||
|
_err "Register account Error: $response"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if ! _send_signed_request "${ACME_NEW_ACCOUNT}" "$regjson"; then
|
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
|
||||||
_err "Register account Error: $response"
|
_debug "_accUri" "$_accUri"
|
||||||
return 1
|
_savecaconf "ACCOUNT_URL" "$_accUri"
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$code" = "" ] || [ "$code" = '201' ]; then
|
echo "$response" >"$ACCOUNT_JSON_PATH"
|
||||||
echo "$response" >"$ACCOUNT_JSON_PATH"
|
CA_KEY_HASH="$(__calcAccountKeyHash)"
|
||||||
_info "Registered"
|
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
|
||||||
elif [ "$code" = '409' ]; then
|
_savecaconf CA_KEY_HASH "$CA_KEY_HASH"
|
||||||
_info "Already registered"
|
|
||||||
else
|
|
||||||
_err "Register account Error: $response"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
_accUri="$(echo "$responseHeaders" | grep "^Location:" | _head_n 1 | cut -d ' ' -f 2 | tr -d "\r\n")"
|
if [ "$code" = '403' ]; then
|
||||||
_debug "_accUri" "$_accUri"
|
_err "It seems that the account key is already deactivated, please use a new account key."
|
||||||
_savecaconf "ACCOUNT_URL" "$_accUri"
|
return 1
|
||||||
_tos="$(echo "$responseHeaders" | grep "^Link:.*rel=\"terms-of-service\"" | _head_n 1 | _egrep_o "<.*>" | tr -d '<>')"
|
fi
|
||||||
_debug "_tos" "$_tos"
|
|
||||||
if [ -z "$_tos" ]; then
|
|
||||||
_debug "Use default tos: $DEFAULT_AGREEMENT"
|
|
||||||
_tos="$DEFAULT_AGREEMENT"
|
|
||||||
fi
|
|
||||||
if [ "$_tos" != "$AGREEMENT" ]; then
|
|
||||||
_updateTos=1
|
|
||||||
AGREEMENT="$_tos"
|
|
||||||
_reg_res="reg"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
|
||||||
_debug "Update tos: $_tos"
|
_info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
|
||||||
if ! _send_signed_request "$_accUri" "$regjson"; then
|
|
||||||
_err "Update tos error."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if [ "$code" = '202' ]; then
|
|
||||||
_info "Update account tos info success."
|
|
||||||
|
|
||||||
CA_KEY_HASH="$(__calcAccountKeyHash)"
|
|
||||||
_debug "Calc CA_KEY_HASH" "$CA_KEY_HASH"
|
|
||||||
_savecaconf CA_KEY_HASH "$CA_KEY_HASH"
|
|
||||||
elif [ "$code" = '403' ]; then
|
|
||||||
_err "It seems that the account key is already deactivated, please use a new account key."
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
_err "Update account error."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
ACCOUNT_THUMBPRINT="$(__calc_account_thumbprint)"
|
|
||||||
_info "ACCOUNT_THUMBPRINT" "$ACCOUNT_THUMBPRINT"
|
|
||||||
return 0
|
|
||||||
done
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3466,7 +3436,7 @@ issue() {
|
|||||||
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
|
token="$(printf "%s\n" "$entry" | _egrep_o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
|
||||||
_debug token "$token"
|
_debug token "$token"
|
||||||
|
|
||||||
uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d : -f 2,3 | tr -d '"')"
|
uri="$(printf "%s\n" "$entry" | _egrep_o '"uri":"[^"]*' | cut -d '"' -f 4)"
|
||||||
_debug uri "$uri"
|
_debug uri "$uri"
|
||||||
|
|
||||||
keyauthorization="$token.$thumbprint"
|
keyauthorization="$token.$thumbprint"
|
||||||
@ -4338,7 +4308,12 @@ _installcert() {
|
|||||||
if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
|
if [ -f "$_real_key" ] && [ ! "$IS_RENEW" ]; then
|
||||||
cp "$_real_key" "$_backup_path/key.bak"
|
cp "$_real_key" "$_backup_path/key.bak"
|
||||||
fi
|
fi
|
||||||
cat "$CERT_KEY_PATH" >"$_real_key"
|
if [ -f "$_real_key" ]; then
|
||||||
|
cat "$CERT_KEY_PATH" >"$_real_key"
|
||||||
|
else
|
||||||
|
cat "$CERT_KEY_PATH" >"$_real_key"
|
||||||
|
chmod 700 "$_real_key"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$_real_fullchain" ]; then
|
if [ "$_real_fullchain" ]; then
|
||||||
|
@ -4,7 +4,7 @@ Before you can deploy your cert, you must [issue the cert first](https://github.
|
|||||||
|
|
||||||
Here are the scripts to deploy the certs/key to the server/services.
|
Here are the scripts to deploy the certs/key to the server/services.
|
||||||
|
|
||||||
## 1. Deploy the certs to your cpanel host.
|
## 1. Deploy the certs to your cpanel host
|
||||||
|
|
||||||
If you want to deploy using cpanel UAPI see 7.
|
If you want to deploy using cpanel UAPI see 7.
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ export DEPLOY_CPANEL_PASSWORD=PASSWORD
|
|||||||
acme.sh --deploy -d example.com --deploy-hook cpanel
|
acme.sh --deploy -d example.com --deploy-hook cpanel
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. Deploy ssl cert on kong proxy engine based on api.
|
## 2. Deploy ssl cert on kong proxy engine based on api
|
||||||
|
|
||||||
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
|
Before you can deploy your cert, you must [issue the cert first](https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert).
|
||||||
Currently supports Kong-v0.10.x.
|
Currently supports Kong-v0.10.x.
|
||||||
@ -29,11 +29,11 @@ Currently supports Kong-v0.10.x.
|
|||||||
acme.sh --deploy -d ftp.example.com --deploy-hook kong
|
acme.sh --deploy -d ftp.example.com --deploy-hook kong
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. Deploy the cert to remote server through SSH access.
|
## 3. Deploy the cert to remote server through SSH access
|
||||||
|
|
||||||
(TODO)
|
(TODO)
|
||||||
|
|
||||||
## 4. Deploy the cert to local vsftpd server.
|
## 4. Deploy the cert to local vsftpd server
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
|
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
|
||||||
@ -55,7 +55,7 @@ export DEPLOY_VSFTPD_RELOAD="/etc/init.d/vsftpd restart"
|
|||||||
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
|
acme.sh --deploy -d ftp.example.com --deploy-hook vsftpd
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. Deploy the cert to local exim4 server.
|
## 5. Deploy the cert to local exim4 server
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
|
acme.sh --deploy -d ftp.example.com --deploy-hook exim4
|
||||||
@ -96,7 +96,23 @@ acme.sh --deploy -d example.com --deploy-hook cpanel_uapi
|
|||||||
```
|
```
|
||||||
Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.
|
Please note, that the cpanel_uapi hook will deploy only the first domain when your certificate will automatically renew. Therefore you should issue a separete certificate for each domain.
|
||||||
|
|
||||||
|
## 8. Deploy the cert to your FRITZ!Box router
|
||||||
|
|
||||||
|
You must specify the credentials that have administrative privileges on the FRITZ!Box in order to deploy the certificate, plus the URL of your FRITZ!Box, through the following environment variables:
|
||||||
|
```sh
|
||||||
|
$ export DEPLOY_FRITZBOX_USERNAME=my_username
|
||||||
|
$ export DEPLOY_FRITZBOX_PASSWORD=the_password
|
||||||
|
$ export DEPLOY_FRITZBOX_URL=https://fritzbox.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
After the first deployment, these values will be stored in your $HOME/.acme.sh/account.conf. You may now deploy the certificate like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --deploy -d fritzbox.example.com --deploy-hook fritzbox
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. Deploy the cert to strongswan
|
||||||
|
|
||||||
|
```sh
|
||||||
|
acme.sh --deploy -d ftp.example.com --deploy-hook strongswan
|
||||||
|
```
|
||||||
|
108
deploy/fritzbox.sh
Normal file
108
deploy/fritzbox.sh
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a script to deploy cert to an AVM FRITZ!Box router.
|
||||||
|
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
#DEPLOY_FRITZBOX_USERNAME="username"
|
||||||
|
#DEPLOY_FRITZBOX_PASSWORD="password"
|
||||||
|
#DEPLOY_FRITZBOX_URL="https://fritz.box"
|
||||||
|
|
||||||
|
# Kudos to wikrie at Github for his FRITZ!Box update script:
|
||||||
|
# https://gist.github.com/wikrie/f1d5747a714e0a34d0582981f7cb4cfb
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
fritzbox_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
if ! _exists iconv; then
|
||||||
|
_err "iconv not found"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_fritzbox_username="${DEPLOY_FRITZBOX_USERNAME}"
|
||||||
|
_fritzbox_password="${DEPLOY_FRITZBOX_PASSWORD}"
|
||||||
|
_fritzbox_url="${DEPLOY_FRITZBOX_URL}"
|
||||||
|
|
||||||
|
_debug _fritzbox_url "$_fritzbox_url"
|
||||||
|
_debug _fritzbox_username "$_fritzbox_username"
|
||||||
|
_secure_debug _fritzbox_password "$_fritzbox_password"
|
||||||
|
if [ -z "$_fritzbox_username" ]; then
|
||||||
|
_err "FRITZ!Box username is not found, please define DEPLOY_FRITZBOX_USERNAME."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$_fritzbox_password" ]; then
|
||||||
|
_err "FRITZ!Box password is not found, please define DEPLOY_FRITZBOX_PASSWORD."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [ -z "$_fritzbox_url" ]; then
|
||||||
|
_err "FRITZ!Box url is not found, please define DEPLOY_FRITZBOX_URL."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_saveaccountconf DEPLOY_FRITZBOX_USERNAME "${_fritzbox_username}"
|
||||||
|
_saveaccountconf DEPLOY_FRITZBOX_PASSWORD "${_fritzbox_password}"
|
||||||
|
_saveaccountconf DEPLOY_FRITZBOX_URL "${_fritzbox_url}"
|
||||||
|
|
||||||
|
# Do not check for a valid SSL certificate, because initially the cert is not valid, so it could not install the LE generated certificate
|
||||||
|
export HTTPS_INSECURE=1
|
||||||
|
|
||||||
|
_info "Log in to the FRITZ!Box"
|
||||||
|
_fritzbox_challenge="$(_get "${_fritzbox_url}/login_sid.lua" | sed -e 's/^.*<Challenge>//' -e 's/<\/Challenge>.*$//')"
|
||||||
|
_fritzbox_hash="$(printf "%s-%s" "${_fritzbox_challenge}" "${_fritzbox_password}" | iconv -f ASCII -t UTF16LE | md5sum | awk '{print $1}')"
|
||||||
|
_fritzbox_sid="$(_get "${_fritzbox_url}/login_sid.lua?sid=0000000000000000&username=${_fritzbox_username}&response=${_fritzbox_challenge}-${_fritzbox_hash}" | sed -e 's/^.*<SID>//' -e 's/<\/SID>.*$//')"
|
||||||
|
|
||||||
|
if [ -z "${_fritzbox_sid}" ] || [ "${_fritzbox_sid}" = "0000000000000000" ]; then
|
||||||
|
_err "Logging in to the FRITZ!Box failed. Please check username, password and URL."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
_info "Generate form POST request"
|
||||||
|
_post_request="$(_mktemp)"
|
||||||
|
_post_boundary="---------------------------$(date +%Y%m%d%H%M%S)"
|
||||||
|
# _CERTPASSWORD_ is unset because Let's Encrypt certificates don't have a password. But if they ever do, here's the place to use it!
|
||||||
|
_CERTPASSWORD_=
|
||||||
|
{
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"sid\"\r\n\r\n%s\r\n" "${_fritzbox_sid}"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"BoxCertPassword\"\r\n\r\n%s\r\n" "${_CERTPASSWORD_}"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s\r\n" "${_post_boundary}"
|
||||||
|
printf "Content-Disposition: form-data; name=\"BoxCertImportFile\"; filename=\"BoxCert.pem\"\r\n"
|
||||||
|
printf "Content-Type: application/octet-stream\r\n\r\n"
|
||||||
|
cat "${_ckey}" "${_cfullchain}"
|
||||||
|
printf "\r\n"
|
||||||
|
printf -- "--"
|
||||||
|
printf -- "%s--" "${_post_boundary}"
|
||||||
|
} >>"${_post_request}"
|
||||||
|
|
||||||
|
_info "Upload certificate to the FRITZ!Box"
|
||||||
|
|
||||||
|
export _H1="Content-type: multipart/form-data boundary=${_post_boundary}"
|
||||||
|
_post "$(cat "${_post_request}")" "${_fritzbox_url}/cgi-bin/firmwarecfg" | grep SSL
|
||||||
|
|
||||||
|
retval=$?
|
||||||
|
if [ $retval = 0 ]; then
|
||||||
|
_info "Upload successful"
|
||||||
|
else
|
||||||
|
_err "Upload failed"
|
||||||
|
fi
|
||||||
|
rm "${_post_request}"
|
||||||
|
|
||||||
|
return $retval
|
||||||
|
}
|
32
deploy/strongswan.sh
Normal file
32
deploy/strongswan.sh
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#Here is a sample custom api script.
|
||||||
|
#This file name is "myapi.sh"
|
||||||
|
#So, here must be a method myapi_deploy()
|
||||||
|
#Which will be called by acme.sh to deploy the cert
|
||||||
|
#returns 0 means success, otherwise error.
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#domain keyfile certfile cafile fullchain
|
||||||
|
strongswan_deploy() {
|
||||||
|
_cdomain="$1"
|
||||||
|
_ckey="$2"
|
||||||
|
_ccert="$3"
|
||||||
|
_cca="$4"
|
||||||
|
_cfullchain="$5"
|
||||||
|
|
||||||
|
_debug _cdomain "$_cdomain"
|
||||||
|
_debug _ckey "$_ckey"
|
||||||
|
_debug _ccert "$_ccert"
|
||||||
|
_debug _cca "$_cca"
|
||||||
|
_debug _cfullchain "$_cfullchain"
|
||||||
|
|
||||||
|
cat "$_ckey" >"/etc/ipsec.d/private/$(basename "$_ckey")"
|
||||||
|
cat "$_ccert" >"/etc/ipsec.d/certs/$(basename "$_ccert")"
|
||||||
|
cat "$_cca" >"/etc/ipsec.d/cacerts/$(basename "$_cca")"
|
||||||
|
cat "$_cfullchain" >"/etc/ipsec.d/cacerts/$(basename "$_cfullchain")"
|
||||||
|
|
||||||
|
ipsec reload
|
||||||
|
|
||||||
|
}
|
@ -420,6 +420,7 @@ Ok, let's issue a cert now:
|
|||||||
```
|
```
|
||||||
acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com
|
acme.sh --issue --dns dns_cloudns -d example.com -d www.example.com
|
||||||
```
|
```
|
||||||
|
The `CLOUDNS_AUTH_ID` and `CLOUDNS_AUTH_PASSWORD` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||||
|
|
||||||
## 22. Use Infoblox API
|
## 22. Use Infoblox API
|
||||||
|
|
||||||
@ -511,7 +512,7 @@ acme.sh --issue --dns dns_nsone -d example.com -d www.example.com
|
|||||||
export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
export DuckDNS_Token="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that since DuckDNS uses StartSSL as their cert provider, thus
|
Please note that since DuckDNS uses StartSSL as their cert provider, thus
|
||||||
--insecure may need to be used when issuing certs:
|
--insecure may need to be used when issuing certs:
|
||||||
```
|
```
|
||||||
acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
|
acme.sh --insecure --issue --dns dns_duckdns -d mydomain.duckdns.org
|
||||||
@ -601,7 +602,56 @@ The `HE_Username` and `HE_Password` settings will be saved in `~/.acme.sh/accoun
|
|||||||
|
|
||||||
Please report any issues to https://github.com/angel333/acme.sh or to <me@ondrejsimek.com>.
|
Please report any issues to https://github.com/angel333/acme.sh or to <me@ondrejsimek.com>.
|
||||||
|
|
||||||
##32. Use DNSEver (https://www.dnsever.com/)
|
## 32. Use UnoEuro API to automatically issue cert
|
||||||
|
|
||||||
|
First you need to login to your UnoEuro account to get your API key.
|
||||||
|
|
||||||
|
```
|
||||||
|
export UNO_Key="sdfsdfsdfljlbjkljlkjsdfoiwje"
|
||||||
|
export UNO_User="UExxxxxx"
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, let's issue a cert now:
|
||||||
|
```
|
||||||
|
acme.sh --issue --dns dns_unoeuro -d example.com -d www.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `UNO_Key` and `UNO_User` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||||
|
|
||||||
|
## 33. Use INWX
|
||||||
|
|
||||||
|
[INWX](https://www.inwx.de/) offers an [xmlrpc api](https://www.inwx.de/de/help/apidoc) with your standard login credentials, set them like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
export INWX_User="yourusername"
|
||||||
|
export INWX_Password="password"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can issue your certificates with:
|
||||||
|
|
||||||
|
```
|
||||||
|
acme.sh --issue --dns dns_inwx -d example.com -d www.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The `INWX_User` and `INWX_Password` settings will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||||
|
|
||||||
|
## 34. User Servercow API v1
|
||||||
|
|
||||||
|
Create a new user from the servercow control center. Don't forget to activate **DNS API** for this user.
|
||||||
|
|
||||||
|
```
|
||||||
|
export SERVERCOW_API_Username=username
|
||||||
|
export SERVERCOW_API_Password=password
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you cann issue a cert:
|
||||||
|
|
||||||
|
```
|
||||||
|
acme.sh --issue --dns dns_servercow -d example.com -d www.example.com
|
||||||
|
```
|
||||||
|
Both, `SERVERCOW_API_Username` and `SERVERCOW_API_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
|
||||||
|
|
||||||
|
##35. Use DNSEver (https://www.dnsever.com/)
|
||||||
|
|
||||||
You will need your login credentials (ID+PW) to the DNSEver, and export them before you run acme.sh:
|
You will need your login credentials (ID+PW) to the DNSEver, and export them before you run acme.sh:
|
||||||
```
|
```
|
||||||
@ -631,6 +681,7 @@ acme.sh --issue --dns dns_myapi -d example.com -d www.example.com
|
|||||||
|
|
||||||
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
|
For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh)
|
||||||
|
|
||||||
|
See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide
|
||||||
|
|
||||||
# Use lexicon DNS API
|
# Use lexicon DNS API
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ _get_root() {
|
|||||||
_debug "response" "$response"
|
_debug "response" "$response"
|
||||||
while true; do
|
while true; do
|
||||||
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
h=$(printf "%s" "$domain" | cut -d . -f $i-100)
|
||||||
|
_debug2 "Checking domain: $h"
|
||||||
if [ -z "$h" ]; then
|
if [ -z "$h" ]; then
|
||||||
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
|
if _contains "$response" "<IsTruncated>true</IsTruncated>" && _contains "$response" "<NextMarker>"; then
|
||||||
_debug "IsTruncated"
|
_debug "IsTruncated"
|
||||||
@ -102,23 +103,23 @@ _get_root() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
#not valid
|
#not valid
|
||||||
|
_err "Invalid domain"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if _contains "$response" "<Name>$h.</Name>"; then
|
if _contains "$response" "<Name>$h.</Name>"; then
|
||||||
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
|
hostedzone="$(echo "$response" | sed 's/<HostedZone>/#&/g' | tr '#' '\n' | _egrep_o "<HostedZone><Id>[^<]*<.Id><Name>$h.<.Name>.*<PrivateZone>false<.PrivateZone>.*<.HostedZone>")"
|
||||||
_debug hostedzone "$hostedzone"
|
_debug hostedzone "$hostedzone"
|
||||||
if [ -z "$hostedzone" ]; then
|
if [ "$hostedzone" ]; then
|
||||||
_err "Error, can not get hostedzone."
|
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
||||||
|
if [ "$_domain_id" ]; then
|
||||||
|
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
||||||
|
_domain=$h
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
_err "Can not find domain id: $h"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
_domain_id=$(printf "%s\n" "$hostedzone" | _egrep_o "<Id>.*<.Id>" | head -n 1 | _egrep_o ">.*<" | tr -d "<>")
|
|
||||||
if [ "$_domain_id" ]; then
|
|
||||||
_sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p)
|
|
||||||
_domain=$h
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
fi
|
||||||
p=$i
|
p=$i
|
||||||
i=$(_math "$i" + 1)
|
i=$(_math "$i" + 1)
|
||||||
|
@ -96,6 +96,16 @@ _dns_cloudns_init_check() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CLOUDNS_AUTH_ID="${CLOUDNS_AUTH_ID:-$(_readaccountconf_mutable CLOUDNS_AUTH_ID)}"
|
||||||
|
CLOUDNS_AUTH_PASSWORD="${CLOUDNS_AUTH_PASSWORD:-$(_readaccountconf_mutable CLOUDNS_AUTH_PASSWORD)}"
|
||||||
|
if [ -z "$CLOUDNS_AUTH_ID" ] || [ -z "$CLOUDNS_AUTH_PASSWORD" ]; then
|
||||||
|
CLOUDNS_AUTH_ID=""
|
||||||
|
CLOUDNS_AUTH_PASSWORD=""
|
||||||
|
_err "You don't specify cloudns api id and password yet."
|
||||||
|
_err "Please create you id and password and try again."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$CLOUDNS_AUTH_ID" ]; then
|
if [ -z "$CLOUDNS_AUTH_ID" ]; then
|
||||||
_err "CLOUDNS_AUTH_ID is not configured"
|
_err "CLOUDNS_AUTH_ID is not configured"
|
||||||
return 1
|
return 1
|
||||||
@ -113,6 +123,10 @@ _dns_cloudns_init_check() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#save the api id and password to the account conf file.
|
||||||
|
_saveaccountconf_mutable CLOUDNS_AUTH_ID "$CLOUDNS_AUTH_ID"
|
||||||
|
_saveaccountconf_mutable CLOUDNS_AUTH_PASSWORD "$CLOUDNS_AUTH_PASSWORD"
|
||||||
|
|
||||||
CLOUDNS_INIT_CHECK_COMPLETED=1
|
CLOUDNS_INIT_CHECK_COMPLETED=1
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -53,6 +53,8 @@ dns_freedns_add() {
|
|||||||
i="$(_math "$i" - 1)"
|
i="$(_math "$i" - 1)"
|
||||||
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
|
sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")"
|
||||||
|
|
||||||
|
_debug top_domain "$top_domain"
|
||||||
|
_debug sub_domain "$sub_domain"
|
||||||
# Sometimes FreeDNS does not return the subdomain page but rather
|
# Sometimes FreeDNS does not return the subdomain page but rather
|
||||||
# returns a page regarding becoming a premium member. This usually
|
# returns a page regarding becoming a premium member. This usually
|
||||||
# happens after a period of inactivity. Immediately trying again
|
# happens after a period of inactivity. Immediately trying again
|
||||||
@ -61,7 +63,6 @@ dns_freedns_add() {
|
|||||||
attempts=2
|
attempts=2
|
||||||
while [ "$attempts" -gt "0" ]; do
|
while [ "$attempts" -gt "0" ]; do
|
||||||
attempts="$(_math "$attempts" - 1)"
|
attempts="$(_math "$attempts" - 1)"
|
||||||
|
|
||||||
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")"
|
||||||
if [ "$?" != "0" ]; then
|
if [ "$?" != "0" ]; then
|
||||||
if [ "$using_cached_cookies" = "true" ]; then
|
if [ "$using_cached_cookies" = "true" ]; then
|
||||||
@ -70,19 +71,11 @@ dns_freedns_add() {
|
|||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
_debug2 htmlpage "$htmlpage"
|
||||||
|
|
||||||
|
subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")"
|
||||||
|
_debug2 subdomain_csv "$subdomain_csv"
|
||||||
|
|
||||||
# Now convert the tables in the HTML to CSV. This litte gem from
|
|
||||||
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
|
|
||||||
subdomain_csv="$(echo "$htmlpage" \
|
|
||||||
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
|
|
||||||
| sed 's/^[\ \t]*//g' \
|
|
||||||
| tr -d '\n' \
|
|
||||||
| sed 's/<\/TR[^>]*>/\n/Ig' \
|
|
||||||
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
|
|
||||||
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
|
|
||||||
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
|
|
||||||
| grep 'edit.php?' \
|
|
||||||
| grep "$top_domain")"
|
|
||||||
# The above beauty ends with striping out rows that do not have an
|
# The above beauty ends with striping out rows that do not have an
|
||||||
# href to edit.php and do not have the top domain we are looking for.
|
# href to edit.php and do not have the top domain we are looking for.
|
||||||
# So all we should be left with is CSV of table of subdomains we are
|
# So all we should be left with is CSV of table of subdomains we are
|
||||||
@ -90,30 +83,32 @@ dns_freedns_add() {
|
|||||||
|
|
||||||
# Now we have to read through this table and extract the data we need
|
# Now we have to read through this table and extract the data we need
|
||||||
lines="$(echo "$subdomain_csv" | wc -l)"
|
lines="$(echo "$subdomain_csv" | wc -l)"
|
||||||
nl='
|
|
||||||
'
|
|
||||||
i=0
|
i=0
|
||||||
found=0
|
found=0
|
||||||
while [ "$i" -lt "$lines" ]; do
|
while [ "$i" -lt "$lines" ]; do
|
||||||
i="$(_math "$i" + 1)"
|
i="$(_math "$i" + 1)"
|
||||||
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
|
line="$(echo "$subdomain_csv" | sed -n "${i}p")"
|
||||||
tmp="$(echo "$line" | cut -d ',' -f 1)"
|
_debug2 line "$line"
|
||||||
if [ $found = 0 ] && _startswith "$tmp" "<td>$top_domain"; then
|
if [ $found = 0 ] && _contains "$line" "<td>$top_domain</td>"; then
|
||||||
# this line will contain DNSdomainid for the top_domain
|
# this line will contain DNSdomainid for the top_domain
|
||||||
DNSdomainid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*domain_id=//;s/>.*//')"
|
DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)"
|
||||||
|
_debug2 DNSdomainid "$DNSdomainid"
|
||||||
found=1
|
found=1
|
||||||
else
|
else
|
||||||
# lines contain DNS records for all subdomains
|
# lines contain DNS records for all subdomains
|
||||||
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
|
DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
DNStype="$(echo "$line" | cut -d ',' -f 3)"
|
_debug2 DNSname "$DNSname"
|
||||||
|
DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug2 DNStype "$DNStype"
|
||||||
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
||||||
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
|
DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)"
|
||||||
# Now get current value for the TXT record. This method may
|
# Now get current value for the TXT record. This method may
|
||||||
# not produce accurate results as the value field is truncated
|
# not produce accurate results as the value field is truncated
|
||||||
# on this webpage. To get full value we would need to load
|
# on this webpage. To get full value we would need to load
|
||||||
# another page. However we don't really need this so long as
|
# another page. However we don't really need this so long as
|
||||||
# there is only one TXT record for the acme challenge subdomain.
|
# there is only one TXT record for the acme challenge subdomain.
|
||||||
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')"
|
DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug2 DNSvalue "$DNSvalue"
|
||||||
if [ $found != 0 ]; then
|
if [ $found != 0 ]; then
|
||||||
break
|
break
|
||||||
# we are breaking out of the loop at the first match of DNS name
|
# we are breaking out of the loop at the first match of DNS name
|
||||||
@ -169,8 +164,7 @@ dns_freedns_add() {
|
|||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
# Delete the old TXT record (with the wrong value)
|
# Delete the old TXT record (with the wrong value)
|
||||||
_freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"
|
if _freedns_delete_txt_record "$FREEDNS_COOKIE" "$DNSdataid"; then
|
||||||
if [ "$?" = "0" ]; then
|
|
||||||
# And add in new TXT record with the value provided
|
# And add in new TXT record with the value provided
|
||||||
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
|
_freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue"
|
||||||
fi
|
fi
|
||||||
@ -210,18 +204,9 @@ dns_freedns_rm() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Now convert the tables in the HTML to CSV. This litte gem from
|
subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '<form .*</form>' | sed 's/<tr>/@<tr>/g' | tr '@' '\n' | grep edit.php | grep "$fulldomain")"
|
||||||
# http://stackoverflow.com/questions/1403087/how-can-i-convert-an-html-table-to-csv
|
_debug2 subdomain_csv "$subdomain_csv"
|
||||||
subdomain_csv="$(echo "$htmlpage" \
|
|
||||||
| grep -i -e '</\?TABLE\|</\?TD\|</\?TR\|</\?TH' \
|
|
||||||
| sed 's/^[\ \t]*//g' \
|
|
||||||
| tr -d '\n' \
|
|
||||||
| sed 's/<\/TR[^>]*>/\n/Ig' \
|
|
||||||
| sed 's/<\/\?\(TABLE\|TR\)[^>]*>//Ig' \
|
|
||||||
| sed 's/^<T[DH][^>]*>\|<\/\?T[DH][^>]*>$//Ig' \
|
|
||||||
| sed 's/<\/T[DH][^>]*><T[DH][^>]*>/,/Ig' \
|
|
||||||
| grep 'edit.php?' \
|
|
||||||
| grep "$fulldomain")"
|
|
||||||
# The above beauty ends with striping out rows that do not have an
|
# 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.
|
# 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
|
# So all we should be left with is CSV of table of subdomains we are
|
||||||
@ -229,19 +214,21 @@ dns_freedns_rm() {
|
|||||||
|
|
||||||
# Now we have to read through this table and extract the data we need
|
# Now we have to read through this table and extract the data we need
|
||||||
lines="$(echo "$subdomain_csv" | wc -l)"
|
lines="$(echo "$subdomain_csv" | wc -l)"
|
||||||
nl='
|
|
||||||
'
|
|
||||||
i=0
|
i=0
|
||||||
found=0
|
found=0
|
||||||
while [ "$i" -lt "$lines" ]; do
|
while [ "$i" -lt "$lines" ]; do
|
||||||
i="$(_math "$i" + 1)"
|
i="$(_math "$i" + 1)"
|
||||||
line="$(echo "$subdomain_csv" | cut -d "$nl" -f "$i")"
|
line="$(echo "$subdomain_csv" | sed -n "${i}p")"
|
||||||
DNSname="$(echo "$line" | cut -d ',' -f 2 | sed 's/^[^>]*>//;s/<\/a>.*//')"
|
_debug2 line "$line"
|
||||||
DNStype="$(echo "$line" | cut -d ',' -f 3)"
|
DNSname="$(echo "$line" | _egrep_o 'edit.php.*</a>' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug2 DNSname "$DNSname"
|
||||||
|
DNStype="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '4p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug2 DNStype "$DNStype"
|
||||||
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
if [ "$DNSname" = "$fulldomain" ] && [ "$DNStype" = "TXT" ]; then
|
||||||
DNSdataid="$(echo "$line" | cut -d ',' -f 2 | sed 's/^.*data_id=//;s/>.*//')"
|
DNSdataid="$(echo "$line" | _egrep_o 'data_id=.*' | cut -d = -f 2 | cut -d '>' -f 1)"
|
||||||
DNSvalue="$(echo "$line" | cut -d ',' -f 4 | sed 's/^[^"]*"//;s/".*//;s/<\/td>.*//')"
|
_debug2 DNSdataid "$DNSdataid"
|
||||||
_debug "DNSvalue: $DNSvalue"
|
DNSvalue="$(echo "$line" | sed 's/<td/@<td/g' | tr '@' '\n' | sed -n '5p' | cut -d '>' -f 2 | cut -d '<' -f 1)"
|
||||||
|
_debug2 DNSvalue "$DNSvalue"
|
||||||
# if [ "$DNSvalue" = "$txtvalue" ]; then
|
# if [ "$DNSvalue" = "$txtvalue" ]; then
|
||||||
# Testing value match fails. Website is truncating the value
|
# Testing value match fails. Website is truncating the value
|
||||||
# field. So for now we will assume that there is only one TXT
|
# field. So for now we will assume that there is only one TXT
|
||||||
|
355
dnsapi/dns_inwx.sh
Normal file
355
dnsapi/dns_inwx.sh
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
#INWX_User="username"
|
||||||
|
#
|
||||||
|
#INWX_Password="password"
|
||||||
|
|
||||||
|
INWX_Api="https://api.domrobot.com/xmlrpc/"
|
||||||
|
|
||||||
|
######## Public functions #####################
|
||||||
|
|
||||||
|
#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
|
||||||
|
dns_inwx_add() {
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
|
||||||
|
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
|
||||||
|
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
|
||||||
|
INWX_User=""
|
||||||
|
INWX_Password=""
|
||||||
|
_err "You don't specify inwx user and password 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 INWX_User "$INWX_User"
|
||||||
|
_saveaccountconf_mutable INWX_Password "$INWX_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"
|
||||||
|
_debug "Getting txt records"
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.info</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>domain</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>type</name>
|
||||||
|
<value>
|
||||||
|
<string>TXT</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>name</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$_domain" "$_sub_domain")
|
||||||
|
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; then
|
||||||
|
_err "Error could net get txt records"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! printf "%s" "$response" | grep "count" >/dev/null; then
|
||||||
|
_info "Adding record"
|
||||||
|
_inwx_add_record "$_domain" "$_sub_domain" "$txtvalue"
|
||||||
|
else
|
||||||
|
_record_id=$(printf '%s' "$response" | _egrep_o '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+')
|
||||||
|
_info "Updating record"
|
||||||
|
_inwx_update_record "$_record_id" "$txtvalue"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#fulldomain txtvalue
|
||||||
|
dns_inwx_rm() {
|
||||||
|
|
||||||
|
fulldomain=$1
|
||||||
|
txtvalue=$2
|
||||||
|
|
||||||
|
INWX_User="${INWX_User:-$(_readaccountconf_mutable INWX_User)}"
|
||||||
|
INWX_Password="${INWX_Password:-$(_readaccountconf_mutable INWX_Password)}"
|
||||||
|
if [ -z "$INWX_User" ] || [ -z "$INWX_Password" ]; then
|
||||||
|
INWX_User=""
|
||||||
|
INWX_Password=""
|
||||||
|
_err "You don't specify inwx user and password 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 INWX_User "$INWX_User"
|
||||||
|
_saveaccountconf_mutable INWX_Password "$INWX_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"
|
||||||
|
|
||||||
|
_debug "Getting txt records"
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.info</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>domain</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>type</name>
|
||||||
|
<value>
|
||||||
|
<string>TXT</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>name</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$_domain" "$_sub_domain")
|
||||||
|
response="$(_post "$xml_content" "$INWX_Api" "" "POST")"
|
||||||
|
|
||||||
|
if ! printf "%s" "$response" | grep "Command completed successfully" >/dev/null; 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 '.*(<member><name>record){1}(.*)([0-9]+){1}' | _egrep_o '<name>id<\/name><value><int>[0-9]+' | _egrep_o '[0-9]+')
|
||||||
|
_info "Deleting record"
|
||||||
|
_inwx_delete_record "$_record_id"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#################### Private functions below ##################################
|
||||||
|
|
||||||
|
_inwx_login() {
|
||||||
|
|
||||||
|
xml_content=$(printf '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>account.login</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>user</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>pass</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' $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='<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.list</methodName>
|
||||||
|
</methodCall>'
|
||||||
|
|
||||||
|
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 '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.deleteRecord</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>id</name>
|
||||||
|
<value>
|
||||||
|
<int>%s</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$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 '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.updateRecord</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>content</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>id</name>
|
||||||
|
<value>
|
||||||
|
<int>%s</int>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$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 '<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<methodCall>
|
||||||
|
<methodName>nameserver.createRecord</methodName>
|
||||||
|
<params>
|
||||||
|
<param>
|
||||||
|
<value>
|
||||||
|
<struct>
|
||||||
|
<member>
|
||||||
|
<name>domain</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>type</name>
|
||||||
|
<value>
|
||||||
|
<string>TXT</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>content</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
<member>
|
||||||
|
<name>name</name>
|
||||||
|
<value>
|
||||||
|
<string>%s</string>
|
||||||
|
</value>
|
||||||
|
</member>
|
||||||
|
</struct>
|
||||||
|
</value>
|
||||||
|
</param>
|
||||||
|
</params>
|
||||||
|
</methodCall>' "$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
|
||||||
|
}
|
170
dnsapi/dns_servercow.sh
Normal file
170
dnsapi/dns_servercow.sh
Normal file
@ -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
|
||||||
|
}
|
202
dnsapi/dns_unoeuro.sh
Normal file
202
dnsapi/dns_unoeuro.sh
Normal file
@ -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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user