From 92477800738ceb8d465395ae2365a5404a3e64e2 Mon Sep 17 00:00:00 2001 From: Maximilian Hippler Date: Wed, 20 Mar 2019 22:34:50 +0100 Subject: [PATCH 001/191] Added oathtool to Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 68385d7..5aa8da1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN apk update -f \ curl \ socat \ tzdata \ + && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing oath-toolkit-oathtool \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh From 68b42a00e07f7e35bc777fd41a6b147233918a38 Mon Sep 17 00:00:00 2001 From: der-berni Date: Mon, 20 May 2019 17:40:43 +0200 Subject: [PATCH 002/191] updated to work with one.com rev command not found on OpenWrt CURL does not work, using wget JSESSIONID replaced with OneSIDCrmAdmin CSRF_G_TOKEN not needed --- dnsapi/dns_one.sh | 139 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 105 insertions(+), 34 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 94ac49c..6049ca1 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -4,9 +4,11 @@ # one.com ui wrapper for acme.sh # Author: github: @diseq # Created: 2019-02-17 +# Fixed by: @der-berni +# Modified: 2019-05-20 # -# export ONECOM_User="username" -# export ONECOM_Password="password" +# export ONECOM_USER="username" +# export ONECOM_PASSWORD="password" # # Usage: # acme.sh --issue --dns dns_one -d example.com @@ -14,38 +16,56 @@ # only single domain supported atm dns_one_add() { - mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) - mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + #rev command not found on OpenWrt + #mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + #mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + + fulldomain=$1 txtvalue=$2 + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + mysubdomain=$_sub_domain + mydomain=$_domain + _debug mysubdomain "$mysubdomain" + _debug mydomain "$mydomain" # get credentials - ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" - ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" - if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then - ONECOM_User="" - ONECOM_Password="" + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 fi #save the api key and email to the account conf file. - _saveaccountconf_mutable ONECOM_User "$ONECOM_User" - _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password" + _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" + _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_User" - postdata="$postdata&username=$ONECOM_User" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_Password" + postdata="$postdata&password1=$ONECOM_PASSWORD" postdata="$postdata&loginTarget=" #_debug postdata "$postdata" - + + #CURL does not work + local tmp_USE_WGET=$ACME_USE_WGET + ACME_USE_WGET=1 + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" #_debug response "$response" - JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" _debug jsessionid "$JSESSIONID" export _H1="Cookie: ${JSESSIONID}" @@ -54,8 +74,8 @@ dns_one_add() { response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" _debug response "$response" - CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" - export _H2="Cookie: ${CSRF_G_TOKEN}" + #CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + #export _H2="Cookie: ${CSRF_G_TOKEN}" # Update the IP address for domain entry postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}" @@ -65,7 +85,9 @@ dns_one_add() { _debug response "$response" id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") - + + ACME_USE_WGET=$tmp_USE_WGET + if [ -z "$id" ]; then _err "Add txt record error." return 1 @@ -77,16 +99,30 @@ dns_one_add() { } dns_one_rm() { - mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) - mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + #rev command not found on OpenWrt + #mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) + #mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) + + fulldomain=$1 txtvalue=$2 + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + mysubdomain=$_sub_domain + mydomain=$_domain + _debug mysubdomain "$mysubdomain" + _debug mydomain "$mydomain" # get credentials - ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" - ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" - if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then - ONECOM_User="" - ONECOM_Password="" + ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" + ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" + if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then + ONECOM_USER="" + ONECOM_PASSWORD="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 @@ -94,16 +130,20 @@ dns_one_rm() { # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_User" - postdata="$postdata&username=$ONECOM_User" + postdata="$postdata&displayUsername=$ONECOM_USER" + postdata="$postdata&username=$ONECOM_USER" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_Password" + postdata="$postdata&password1=$ONECOM_PASSWORD" postdata="$postdata&loginTarget=" - + + #CURL does not work + local tmp_USE_WGET=$ACME_USE_WGET + ACME_USE_WGET=1 + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" #_debug response "$response" - JSESSIONID="$(grep "JSESSIONID" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'JSESSIONID=[^;]*;' | tr -d ';')" + JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" _debug jsessionid "$JSESSIONID" export _H1="Cookie: ${JSESSIONID}" @@ -113,13 +153,14 @@ dns_one_rm() { response="$(echo "$response" | _normalizeJson)" _debug response "$response" - CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" - export _H2="Cookie: ${CSRF_G_TOKEN}" + #CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" + #export _H2="Cookie: ${CSRF_G_TOKEN}" id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p") if [ -z "$id" ]; then _err "Txt record not found." + ACME_USE_WGET=$tmp_USE_WGET return 1 fi @@ -127,7 +168,9 @@ dns_one_rm() { response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")" response="$(echo "$response" | _normalizeJson)" _debug response "$response" - + + ACME_USE_WGET=$tmp_USE_WGET + if [ "$response" = '{"result":null,"metadata":null}' ]; then _info "Removed, OK" return 0 @@ -137,3 +180,31 @@ dns_one_rm() { fi } + +#_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 [ "$(printf "%s" "$h" | tr '.' ' ' | wc -w)" = "2" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain="$h" + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + _err "Unable to parse this domain" + return 1 +} From 49bdcad4b60da9f8efec18eff7b8fc81bcb0a4e0 Mon Sep 17 00:00:00 2001 From: Maximilian Hippler Date: Tue, 21 May 2019 18:50:12 +0200 Subject: [PATCH 003/191] Updated oathtoolkit from edge/testing to edge/community --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5aa8da1..f9d7b1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apk update -f \ curl \ socat \ tzdata \ - && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/testing oath-toolkit-oathtool \ + && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/community oath-toolkit-oathtool \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh @@ -22,7 +22,7 @@ RUN cd /install_acme.sh && ([ -f /install_acme.sh/acme.sh ] && /install_acme.sh/ 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 \ install \ uninstall \ From c2dd7e0f6ed2a3e099bd6409b37cfd9a84f5f752 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Wed, 22 May 2019 22:00:39 +0800 Subject: [PATCH 004/191] Create LICENSE.md --- LICENSE.md | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 0e9ba9a0047fcbac58edb2e01d1db7ff9c7175c7 Mon Sep 17 00:00:00 2001 From: Oliver Dick Date: Wed, 22 May 2019 16:20:28 +0200 Subject: [PATCH 005/191] remove _hostingde_parse_no_strip_whitespace function as this breaks API requests --- dnsapi/dns_hostingde.sh | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 1aa7039..9e3e566 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -53,18 +53,6 @@ _hostingde_parse() { fi } -_hostingde_parse_no_strip_whitespace() { - find="${1}" - if [ "${2}" ]; then - notfind="${2}" - fi - if [ "${notfind}" ]; then - _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 - else - _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 - fi -} - _hostingde_getZoneConfig() { _info "Getting ZoneConfig" curZone="${fulldomain#*.}" @@ -97,12 +85,12 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") - zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateValues") + zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse "templateValues") if [ "$zoneConfigTemplateValues" != "null" ]; then _debug "Zone is tied to a template." zoneConfigTemplateValuesTemplateId=$(echo "${curResult}" | _hostingde_parse "templateId") - zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateName") + zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse "templateName") zoneConfigTemplateValuesTemplateReplacementsIPv4=$(echo "${curResult}" | _hostingde_parse "ipv4Replacement") zoneConfigTemplateValuesTemplateReplacementsIPv6=$(echo "${curResult}" | _hostingde_parse "ipv6Replacement") zoneConfigTemplateValuesTemplateReplacementsMailIPv4=$(echo "${curResult}" | _hostingde_parse "mailIpv4Replacement") From 50d5c4b9ca9c639d121caa6bfcbf2b8729bcea0b Mon Sep 17 00:00:00 2001 From: devNan0 Date: Wed, 22 May 2019 17:01:11 +0200 Subject: [PATCH 006/191] Fix sessionid parsing on BSD --- dnsapi/dns_netcup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index d5d7c22..6e17925 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -117,7 +117,7 @@ dns_netcup_rm() { login() { tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') + sid=$(echo "$tmp" | tr '{}' '\n\n' | grep apisessionid | cut -d '"' -f 4) _debug "$tmp" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" From 09fb9dcd92d56d3552177ae86a466157cb908fb8 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 22 May 2019 22:16:46 -0400 Subject: [PATCH 007/191] Fix bug preventing multipart TLD names to work. And simplify/cleanup the code. --- dnsapi/dns_freedns.sh | 303 ++++++++++++++++++++++++------------------ 1 file changed, 174 insertions(+), 129 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 7262755..e6c6ba5 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -46,76 +46,34 @@ dns_freedns_add() { _saveaccountconf FREEDNS_COOKIE "$FREEDNS_COOKIE" - # split our full domain name into two parts... - i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" - i="$(_math "$i" - 1)" - top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" - i="$(_math "$i" - 1)" - 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 - # returns a page regarding becoming a premium member. This usually - # happens after a period of inactivity. Immediately trying again - # returns the correct subdomain page. So, we will try twice to - # load the page and obtain our domain ID - attempts=2 - while [ "$attempts" -gt "0" ]; do - attempts="$(_math "$attempts" - 1)" - - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" - if [ "$?" != "0" ]; then - if [ "$using_cached_cookies" = "true" ]; then - _err "Has your FreeDNS username and password changed? If so..." - _err "Please export as FREEDNS_User / FREEDNS_Password and try again." - fi - return 1 - fi - - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '
' | sed 's//@/g' | tr '@' '\n' | grep edit.php | grep "$top_domain")" - _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 top domain 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 - DNSdomainid="" - while [ "$i" -lt "$lines" ]; do - i="$(_math "$i" + 1)" - line="$(echo "$subdomain_csv" | sed -n "${i}p")" - _debug2 "line: $line" - if [ $found = 0 ] && _contains "$line" "$top_domain"; then - # this line will contain DNSdomainid for the top_domain - DNSdomainid="$(echo "$line" | _egrep_o "edit_domain_id *= *.*>" | cut -d = -f 2 | cut -d '>' -f 1)" - _debug2 "DNSdomainid: $DNSdomainid" - found=1 - break - fi - done - - if [ -z "$DNSdomainid" ]; then - # If domain ID is empty then something went wrong (top level - # domain not found at FreeDNS). - if [ "$attempts" = "0" ]; then - # exhausted maximum retry attempts - _err "Domain $top_domain not found at FreeDNS" - return 1 - fi - else - # break out of the 'retry' loop... we have found our domain ID + # We may have to cycle through the domain name to find the + # TLD that we own... + i=1 + wmax="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" + while [ "$i" -lt "$wmax" ]; do + # split our full domain name into two parts... + sub_domain="$(echo "$fulldomain" | cut -d. -f -"$i")" + i="$(_math "$i" + 1)" + top_domain="$(echo "$fulldomain" | cut -d. -f "$i"-100)" + _debug "sub_domain: $sub_domain" + _debug "top_domain: $top_domain" + + DNSdomainid="$(_freedns_domain_id $top_domain)" + if [ "$?" = "0" ]; then + _info "Domain $top_domain found at FreeDNS, domain_id $DNSdomainid" break + else + _info "Domain $top_domain not found at FreeDNS, try with next level of TLD" fi - _info "Domain $top_domain not found at FreeDNS" - _info "Retry loading subdomain page ($attempts attempts remaining)" done + if [ -z "$DNSdomainid" ]; then + # If domain ID is empty then something went wrong (top level + # domain not found at FreeDNS). + _err "Domain $top_domain not found at FreeDNS" + return 1 + fi + # Add in new TXT record with the value provided _debug "Adding TXT record for $fulldomain, $txtvalue" _freedns_add_txt_record "$FREEDNS_COOKIE" "$DNSdomainid" "$sub_domain" "$txtvalue" @@ -138,76 +96,44 @@ dns_freedns_rm() { FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" _debug "FreeDNS login cookies: $FREEDNS_COOKIE" - # Sometimes FreeDNS does not return the subdomain page but rather - # returns a page regarding becoming a premium member. This usually - # happens after a period of inactivity. Immediately trying again - # returns the correct subdomain page. So, we will try twice to - # load the page and obtain our TXT record. - attempts=2 - while [ "$attempts" -gt "0" ]; do - attempts="$(_math "$attempts" - 1)" - - htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + TXTdataid="$(_freedns_data_id "$fulldomain" "TXT")" + if [ "$?" != "0" ]; then + _info "Cannot delete TXT record for $fulldomain, record does not exist at FreeDNS" + return 1 + fi + _debug "Data ID's found, $TXTdataid" + + # now we have one (or more) TXT record data ID's. Load the page + # for that record and search for the record txt value. If match + # then we can delete it. + lines="$(echo "$TXTdataid" | wc -l)" + _debug "Found $lines TXT data records for $fulldomain" + i=0 + while [ "$i" -lt "$lines" ]; do + i="$(_math "$i" + 1)" + dataid="$(echo "$TXTdataid" | sed -n "${i}p")" + _debug "$dataid" + + htmlpage="$(_freedns_retrieve_data_page "$FREEDNS_COOKIE" "$dataid")" if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + _err "Has your FreeDNS username and password changed? If so..." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + fi return 1 fi - subdomain_csv="$(echo "$htmlpage" | tr -d "\n\r" | _egrep_o '' | sed 's//@/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 + echo "$htmlpage" | grep "value=\""$txtvalue"\"" >/dev/null + if [ "$?" = "0" ]; then + # Found a match... delete the record and return + _info "Deleting TXT record for $fulldomain, $txtvalue" + _freedns_delete_txt_record "$FREEDNS_COOKIE" "$dataid" + return $? + fi done - # If we get this far we did not find a match (after two attempts) + # If we get this far we did not find a match # 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 } @@ -271,6 +197,33 @@ _freedns_retrieve_subdomain_page() { return 0 } +# usage _freedns_retrieve_data_page login_cookies data_id +# echo page retrieved (html) +# returns 0 success +_freedns_retrieve_data_page() { + export _H1="Cookie:$1" + export _H2="Accept-Language:en-US" + data_id="$2" + url="https://freedns.afraid.org/subdomain/edit.php?data_id=$2" + + _debug "Retrieve data page for ID $data_id from FreeDNS" + + htmlpage="$(_get "$url")" + + if [ "$?" != "0" ]; then + _err "FreeDNS retrieve data page failed bad RC from _get" + return 1 + elif [ -z "$htmlpage" ]; then + _err "FreeDNS returned empty data 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() { @@ -324,3 +277,95 @@ _freedns_delete_txt_record() { _info "Deleted acme challenge TXT record for $fulldomain at FreeDNS" return 0 } + +# usage _freedns_domain_id domain_name +# echo the domain_id if found +# return 0 success +_freedns_domain_id() { + # Start by escaping the dots in the domain name + search_domain="$(echo "$1" | sed 's/\./\\./g')" + + # Sometimes FreeDNS does not return the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our domain ID + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + _err "Has your FreeDNS username and password changed? If so..." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 + fi + + domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ + | grep "$search_domain\|$search_domain(.*)" \ + | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ + | cut -d = -f 2 )" + # The above beauty extracts domain ID from the html page... + # strip out all blank space and new lines. Then insert newlines + # before each table row + # search for the domain within each row (which may or may not have + # a text string in brackets (.*) after it. + # And finally extract the domain ID. + if [ -n "$domain_id" ]; then + printf "%s" "$domain_id" + return 0 + fi + _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" + done + _debug "Domain $search_domain not found after retry" + return 1 +} + +# usage _freedns_data_id domain_name record_type +# echo the data_id(s) if found +# return 0 success +_freedns_data_id() { + # Start by escaping the dots in the domain name + search_domain="$(echo "$1" | sed 's/\./\\./g')" + record_type="$2" + + # Sometimes FreeDNS does not return the subdomain page but rather + # returns a page regarding becoming a premium member. This usually + # happens after a period of inactivity. Immediately trying again + # returns the correct subdomain page. So, we will try twice to + # load the page and obtain our domain ID + attempts=2 + while [ "$attempts" -gt "0" ]; do + attempts="$(_math "$attempts" - 1)" + + htmlpage="$(_freedns_retrieve_subdomain_page "$FREEDNS_COOKIE")" + if [ "$?" != "0" ]; then + if [ "$using_cached_cookies" = "true" ]; then + _err "Has your FreeDNS username and password changed? If so..." + _err "Please export as FREEDNS_User / FREEDNS_Password and try again." + fi + return 1 + fi + + data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ + | grep "$record_type" \ + | grep "$search_domain" \ + | _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ + | cut -d = -f 2)" + # The above beauty extracts data ID from the html page... + # strip out all blank space and new lines. Then insert newlines + # before each table row + # search for the record type withing each row (e.g. TXT) + # search for the domain within each row (which is within a + # anchor. And finally extract the domain ID. + if [ -n "$data_id" ]; then + printf "%s" "$data_id" + return 0 + fi + _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" + done + _debug "Domain $search_domain not found after retry" + return 1 +} From 66c39a953ae170e5cb64ab57f8cd2d51987a1f7d Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 22 May 2019 22:50:26 -0400 Subject: [PATCH 008/191] Fix warnings from Travis build --- dnsapi/dns_freedns.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e6c6ba5..d1f3d41 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -58,7 +58,7 @@ dns_freedns_add() { _debug "sub_domain: $sub_domain" _debug "top_domain: $top_domain" - DNSdomainid="$(_freedns_domain_id $top_domain)" + DNSdomainid="$(_freedns_domain_id "$top_domain")" if [ "$?" = "0" ]; then _info "Domain $top_domain found at FreeDNS, domain_id $DNSdomainid" break @@ -304,9 +304,9 @@ _freedns_domain_id() { fi domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ - | grep "$search_domain\|$search_domain(.*)" \ - | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ - | cut -d = -f 2 )" + | grep "$search_domain\|$search_domain(.*)" \ + | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ + | cut -d = -f 2 )" # The above beauty extracts domain ID from the html page... # strip out all blank space and new lines. Then insert newlines # before each table row @@ -319,7 +319,7 @@ _freedns_domain_id() { fi _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" done - _debug "Domain $search_domain not found after retry" + _debug "Domain $search_domain not found after retry" return 1 } @@ -350,10 +350,10 @@ _freedns_data_id() { fi data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ - | grep "$record_type" \ - | grep "$search_domain" \ - | _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ - | cut -d = -f 2)" + | grep "$record_type" \ + | grep "$search_domain" \ + | _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ + | cut -d = -f 2)" # The above beauty extracts data ID from the html page... # strip out all blank space and new lines. Then insert newlines # before each table row @@ -366,6 +366,6 @@ _freedns_data_id() { fi _debug "Domain $search_domain not found. Retry loading subdomain page ($attempts attempts remaining)" done - _debug "Domain $search_domain not found after retry" + _debug "Domain $search_domain not found after retry" return 1 } From a18ce275ab91a88149cf580a964e237897f85cf6 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 22 May 2019 22:54:56 -0400 Subject: [PATCH 009/191] Another Travis CI warning fixed. --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index d1f3d41..e92cf61 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -306,7 +306,7 @@ _freedns_domain_id() { domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ - | cut -d = -f 2 )" + | cut -d = -f 2)" # The above beauty extracts domain ID from the html page... # strip out all blank space and new lines. Then insert newlines # before each table row From 10994d65bed6039aa32adfa39cf454fe3619eb2c Mon Sep 17 00:00:00 2001 From: David Kerr Date: Wed, 22 May 2019 23:01:23 -0400 Subject: [PATCH 010/191] Even blank lines (with spaces) give Travis heartache. Sigh. --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e92cf61..71c6aa8 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -348,7 +348,7 @@ _freedns_data_id() { fi return 1 fi - + data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ From 05b6afcd172cd7e15909c6162094f1a00595bf77 Mon Sep 17 00:00:00 2001 From: devNan0 Date: Thu, 23 May 2019 08:15:03 +0200 Subject: [PATCH 011/191] Make travis happy. (SC2020) --- dnsapi/dns_netcup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index 6e17925..d519e4f 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -117,7 +117,7 @@ dns_netcup_rm() { login() { tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(echo "$tmp" | tr '{}' '\n\n' | grep apisessionid | cut -d '"' -f 4) + sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4) _debug "$tmp" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" From e340593ad1c313d178ef1b425bd66831b5f425be Mon Sep 17 00:00:00 2001 From: der-berni Date: Thu, 23 May 2019 09:39:54 +0200 Subject: [PATCH 012/191] Revert parameter changes Revert ONECOM_PASSWORD back to ONECOM_Password and ONECOM_USER back to ONECOM_User --- dnsapi/dns_one.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 6049ca1..a237a0d 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -7,8 +7,8 @@ # Fixed by: @der-berni # Modified: 2019-05-20 # -# export ONECOM_USER="username" -# export ONECOM_PASSWORD="password" +# export ONECOM_User="username" +# export ONECOM_Password="password" # # Usage: # acme.sh --issue --dns dns_one -d example.com @@ -35,26 +35,26 @@ dns_one_add() { _debug mydomain "$mydomain" # get credentials - ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" - ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" - if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then - ONECOM_USER="" - ONECOM_PASSWORD="" + ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" + ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" + if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then + ONECOM_User="" + ONECOM_Password="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 fi #save the api key and email to the account conf file. - _saveaccountconf_mutable ONECOM_USER "$ONECOM_USER" - _saveaccountconf_mutable ONECOM_PASSWORD "$ONECOM_PASSWORD" + _saveaccountconf_mutable ONECOM_User "$ONECOM_User" + _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password" # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_USER" - postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&displayUsername=$ONECOM_User" + postdata="$postdata&username=$ONECOM_User" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&password1=$ONECOM_Password" postdata="$postdata&loginTarget=" #_debug postdata "$postdata" @@ -118,11 +118,11 @@ dns_one_rm() { _debug mydomain "$mydomain" # get credentials - ONECOM_USER="${ONECOM_USER:-$(_readaccountconf_mutable ONECOM_USER)}" - ONECOM_PASSWORD="${ONECOM_PASSWORD:-$(_readaccountconf_mutable ONECOM_PASSWORD)}" - if [ -z "$ONECOM_USER" ] || [ -z "$ONECOM_PASSWORD" ]; then - ONECOM_USER="" - ONECOM_PASSWORD="" + ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" + ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" + if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then + ONECOM_User="" + ONECOM_Password="" _err "You didn't specify a one.com username and password yet." _err "Please create the key and try again." return 1 @@ -130,10 +130,10 @@ dns_one_rm() { # Login with user and password postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_USER" - postdata="$postdata&username=$ONECOM_USER" + postdata="$postdata&displayUsername=$ONECOM_User" + postdata="$postdata&username=$ONECOM_User" postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_PASSWORD" + postdata="$postdata&password1=$ONECOM_Password" postdata="$postdata&loginTarget=" #CURL does not work From 93740c997c5ea74fe198c37223bba13e4e777b36 Mon Sep 17 00:00:00 2001 From: andreasschulze Date: Thu, 23 May 2019 16:19:08 +0200 Subject: [PATCH 013/191] fix for https://github.com/Neilpang/acme.sh/issues/2286 --- acme.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/acme.sh b/acme.sh index 60580f8..eb2194a 100755 --- a/acme.sh +++ b/acme.sh @@ -4338,6 +4338,8 @@ $_authorizations_map" if [ "$status" = "pending" ]; then _info "Pending" + elif [ "$status" = "processing" ]; then + _info "Processing" else _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" From 2cb0b00e3acb6f1530a811b2e0eda32d628771b6 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Thu, 23 May 2019 18:11:25 -0400 Subject: [PATCH 014/191] replace _read_conf() with _readaccountconf() --- dnsapi/dns_freedns.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 71c6aa8..5928837 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -92,8 +92,7 @@ dns_freedns_rm() { # Need to read cookie from conf file again in case new value set # during login to FreeDNS when TXT record was created. - # acme.sh does not have a _readaccountconf() function - FREEDNS_COOKIE="$(_read_conf "$ACCOUNT_CONF_PATH" "FREEDNS_COOKIE")" + FREEDNS_COOKIE="$(_readaccountconf "FREEDNS_COOKIE")" _debug "FreeDNS login cookies: $FREEDNS_COOKIE" TXTdataid="$(_freedns_data_id "$fulldomain" "TXT")" From a3089a719f8dcd2be92ee8c236671cfc7d5ac5f6 Mon Sep 17 00:00:00 2001 From: der-berni Date: Fri, 24 May 2019 09:44:13 +0200 Subject: [PATCH 015/191] Updated to work with curl Now works with curl. Check the root domain. --- dnsapi/dns_one.sh | 153 ++++++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 92 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index a237a0d..a77603e 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -16,16 +16,17 @@ # only single domain supported atm dns_one_add() { - #rev command not found on OpenWrt - #mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) - #mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) - fulldomain=$1 txtvalue=$2 - _debug "First detect the root zone" + if ! _dns_one_login; then + _err "login failed" + return 1 + fi + + _debug "detect the root domain" if ! _get_root "$fulldomain"; then - _err "invalid domain" + _err "root domain not found" return 1 fi @@ -33,50 +34,11 @@ dns_one_add() { mydomain=$_domain _debug mysubdomain "$mysubdomain" _debug mydomain "$mydomain" - - # get credentials - ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" - ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" - if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then - ONECOM_User="" - ONECOM_Password="" - _err "You didn't specify a one.com username and password yet." - _err "Please create the key and try again." - return 1 - fi - - #save the api key and email to the account conf file. - _saveaccountconf_mutable ONECOM_User "$ONECOM_User" - _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password" - - # Login with user and password - postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_User" - postdata="$postdata&username=$ONECOM_User" - postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_Password" - postdata="$postdata&loginTarget=" - #_debug postdata "$postdata" - #CURL does not work - local tmp_USE_WGET=$ACME_USE_WGET - ACME_USE_WGET=1 - - response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" - #_debug response "$response" - - JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" - _debug jsessionid "$JSESSIONID" - - export _H1="Cookie: ${JSESSIONID}" - # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" _debug response "$response" - #CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" - #export _H2="Cookie: ${CSRF_G_TOKEN}" - # Update the IP address for domain entry postdata="{\"type\":\"dns_custom_records\",\"attributes\":{\"priority\":0,\"ttl\":600,\"type\":\"TXT\",\"prefix\":\"$mysubdomain\",\"content\":\"$txtvalue\"}}" _debug postdata "$postdata" @@ -86,8 +48,6 @@ dns_one_add() { id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") - ACME_USE_WGET=$tmp_USE_WGET - if [ -z "$id" ]; then _err "Add txt record error." return 1 @@ -99,16 +59,17 @@ dns_one_add() { } dns_one_rm() { - #rev command not found on OpenWrt - #mysubdomain=$(printf -- "%s" "$1" | rev | cut -d"." -f3- | rev) - #mydomain=$(printf -- "%s" "$1" | rev | cut -d"." -f1-2 | rev) - fulldomain=$1 txtvalue=$2 - _debug "First detect the root zone" + if ! _dns_one_login; then + _err "login failed" + return 1 + fi + + _debug "detect the root domain" if ! _get_root "$fulldomain"; then - _err "invalid domain" + _err "root domain not found" return 1 fi @@ -116,51 +77,16 @@ dns_one_rm() { mydomain=$_domain _debug mysubdomain "$mysubdomain" _debug mydomain "$mydomain" - - # get credentials - ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" - ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" - if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then - ONECOM_User="" - ONECOM_Password="" - _err "You didn't specify a one.com username and password yet." - _err "Please create the key and try again." - return 1 - fi - - # Login with user and password - postdata="loginDomain=true" - postdata="$postdata&displayUsername=$ONECOM_User" - postdata="$postdata&username=$ONECOM_User" - postdata="$postdata&targetDomain=$mydomain" - postdata="$postdata&password1=$ONECOM_Password" - postdata="$postdata&loginTarget=" - - #CURL does not work - local tmp_USE_WGET=$ACME_USE_WGET - ACME_USE_WGET=1 - response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" - #_debug response "$response" - - JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _tail_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" - _debug jsessionid "$JSESSIONID" - - export _H1="Cookie: ${JSESSIONID}" - # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" response="$(echo "$response" | _normalizeJson)" _debug response "$response" - #CSRF_G_TOKEN="$(grep "CSRF_G_TOKEN=" "$HTTP_HEADER" | grep "^Set-Cookie:" | _tail_n 1 | _egrep_o 'CSRF_G_TOKEN=[^;]*;' | tr -d ';')" - #export _H2="Cookie: ${CSRF_G_TOKEN}" - id=$(printf -- "%s" "$response" | sed -n "s/.*{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}.*/\1/p") if [ -z "$id" ]; then _err "Txt record not found." - ACME_USE_WGET=$tmp_USE_WGET return 1 fi @@ -169,8 +95,6 @@ dns_one_rm() { response="$(echo "$response" | _normalizeJson)" _debug response "$response" - ACME_USE_WGET=$tmp_USE_WGET - if [ "$response" = '{"result":null,"metadata":null}' ]; then _info "Removed, OK" return 0 @@ -196,8 +120,10 @@ _get_root() { #not valid return 1 fi - - if [ "$(printf "%s" "$h" | tr '.' ' ' | wc -w)" = "2" ]; then + + response="$(_get "https://www.one.com/admin/api/domains/$h/dns/custom_records")" + + if ! _contains "$response" "CRMRST_000302" ; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain="$h" return 0 @@ -208,3 +134,46 @@ _get_root() { _err "Unable to parse this domain" return 1 } + +_dns_one_login() { + + # get credentials + ONECOM_User="${ONECOM_User:-$(_readaccountconf_mutable ONECOM_User)}" + ONECOM_Password="${ONECOM_Password:-$(_readaccountconf_mutable ONECOM_Password)}" + if [ -z "$ONECOM_User" ] || [ -z "$ONECOM_Password" ]; then + ONECOM_User="" + ONECOM_Password="" + _err "You didn't specify a one.com username and password yet." + _err "Please create the key and try again." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable ONECOM_User "$ONECOM_User" + _saveaccountconf_mutable ONECOM_Password "$ONECOM_Password" + + # Login with user and password + postdata="loginDomain=true" + postdata="$postdata&displayUsername=$ONECOM_User" + postdata="$postdata&username=$ONECOM_User" + postdata="$postdata&targetDomain=" + postdata="$postdata&password1=$ONECOM_Password" + postdata="$postdata&loginTarget=" + #_debug postdata "$postdata" + + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" + #_debug response "$response" + + # Get SessionID + JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _head_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" + _debug jsessionid "$JSESSIONID" + + if [ -z "$JSESSIONID" ]; then + _err "error sessionid cookie not found" + return 1 + fi + + export _H1="Cookie: ${JSESSIONID}" + + return 0 + } From 815230943543fe74205e1a23ddbf1e575a30e6e5 Mon Sep 17 00:00:00 2001 From: Awal Garg Date: Tue, 26 Mar 2019 12:18:53 +0530 Subject: [PATCH 016/191] Add support for MaraDNS MaraDNS is a lightweight self-hosting DNS server. This patch adds support for adding records to zone files stored on the server in the format expected by MaraDNS. Path to the file should be exported in MARA_ZONE_FILE environment variable. To reload the configuration automatically, the user must provide path to the pid file of duende (the daemonization tool that ships with MaraDNS) in MARA_DUENDE_PID_PATH (--pid argument to duende). --- dnsapi/dns_maradns.sh | 69 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100755 dnsapi/dns_maradns.sh diff --git a/dnsapi/dns_maradns.sh b/dnsapi/dns_maradns.sh new file mode 100755 index 0000000..4ff6ca2 --- /dev/null +++ b/dnsapi/dns_maradns.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh + +#Usage: dns_maradns_add _acme-challenge.www.domain.com "token" +dns_maradns_add() { + fulldomain="$1" + txtvalue="$2" + + MARA_ZONE_FILE="${MARA_ZONE_FILE:-$(_readaccountconf_mutable MARA_ZONE_FILE)}" + MARA_DUENDE_PID_PATH="${MARA_DUENDE_PID_PATH:-$(_readaccountconf_mutable MARA_DUENDE_PID_PATH)}" + + _check_zone_file "$MARA_ZONE_FILE" || return 1 + _check_duende_pid_path "$MARA_DUENDE_PID_PATH" || return 1 + + _saveaccountconf_mutable MARA_ZONE_FILE "$MARA_ZONE_FILE" + _saveaccountconf_mutable MARA_DUENDE_PID_PATH "$MARA_DUENDE_PID_PATH" + + printf "%s. TXT '%s' ~\n" "$fulldomain" "$txtvalue" >>"$MARA_ZONE_FILE" + _reload_maradns "$MARA_DUENDE_PID_PATH" || return 1 +} + +#Usage: dns_maradns_rm _acme-challenge.www.domain.com "token" +dns_maradns_rm() { + fulldomain="$1" + txtvalue="$2" + + MARA_ZONE_FILE="${MARA_ZONE_FILE:-$(_readaccountconf_mutable MARA_ZONE_FILE)}" + MARA_DUENDE_PID_PATH="${MARA_DUENDE_PID_PATH:-$(_readaccountconf_mutable MARA_DUENDE_PID_PATH)}" + + _check_zone_file "$MARA_ZONE_FILE" || return 1 + _check_duende_pid_path "$MARA_DUENDE_PID_PATH" || return 1 + + _saveaccountconf_mutable MARA_ZONE_FILE "$MARA_ZONE_FILE" + _saveaccountconf_mutable MARA_DUENDE_PID_PATH "$MARA_DUENDE_PID_PATH" + + _sed_i "/^$fulldomain.\+TXT '$txtvalue' ~/d" "$MARA_ZONE_FILE" + _reload_maradns "$MARA_DUENDE_PID_PATH" || return 1 +} + +_check_zone_file() { + zonefile="$1" + if [ -z "$zonefile" ]; then + _err "MARA_ZONE_FILE not passed!" + return 1 + elif [ ! -w "$zonefile" ]; then + _err "MARA_ZONE_FILE not writable: $zonefile" + return 1 + fi +} + +_check_duende_pid_path() { + pidpath="$1" + if [ -z "$pidpath" ]; then + _err "MARA_DUENDE_PID_PATH not passed!" + return 1 + fi + if [ ! -r "$pidpath" ]; then + _err "MARA_DUENDE_PID_PATH not readable: $pidpath" + return 1 + fi +} + +_reload_maradns() { + pidpath="$1" + kill -s HUP -- "$(cat "$pidpath")" + if [ $? -ne 0 ]; then + _err "Unable to reload MaraDNS, kill returned $?" + return 1 + fi +} From 89e73594eb04dbc3ca4eaed3acacc0b59b4e5a60 Mon Sep 17 00:00:00 2001 From: der-berni Date: Sat, 25 May 2019 17:35:40 +0200 Subject: [PATCH 017/191] fixed error in CI --- dnsapi/dns_one.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index a77603e..74db40b 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -123,7 +123,7 @@ _get_root() { response="$(_get "https://www.one.com/admin/api/domains/$h/dns/custom_records")" - if ! _contains "$response" "CRMRST_000302" ; then + if ! _contains "$response" "CRMRST_000302"; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain="$h" return 0 From 51447961cbc7350a77052d3fab12381986573832 Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 27 May 2019 16:45:44 +0200 Subject: [PATCH 018/191] Notify mail update (#2293) * feat: disable e-mail validation if MAIL_NOVALIDATE is set * fix: expose _MAIL_BIN variable * fix: call _mail_body and _mail_cmnd directly to make sure that all used variables are exposed * fix: update notify/mail.sh Co-Authored-By: Matej Mihevc * fix: remove useless echo, quote eval --- notify/mail.sh | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/notify/mail.sh b/notify/mail.sh index dbecc3a..ec9aa0d 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -5,6 +5,7 @@ #MAIL_BIN="sendmail" #MAIL_FROM="yyyy@gmail.com" #MAIL_TO="yyyy@gmail.com" +#MAIL_NOVALIDATE="" mail_send() { _subject="$1" @@ -14,22 +15,28 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" + MAIL_NOVALIDATE="${MAIL_NOVALIDATE:-$(_readaccountconf_mutable MAIL_NOVALIDATE)}" + if [ -n "$MAIL_NOVALIDATE" ]; then + _saveaccountconf_mutable MAIL_NOVALIDATE 1 + else + _clearaccountconf "MAIL_NOVALIDATE" + fi + MAIL_BIN="${MAIL_BIN:-$(_readaccountconf_mutable MAIL_BIN)}" if [ -n "$MAIL_BIN" ] && ! _exists "$MAIL_BIN"; then _err "It seems that the command $MAIL_BIN is not in path." return 1 fi - _MAIL_CMD=$(_mail_cmnd) + _MAIL_BIN=$(_mail_bin) if [ -n "$MAIL_BIN" ]; then _saveaccountconf_mutable MAIL_BIN "$MAIL_BIN" else _clearaccountconf "MAIL_BIN" fi - _MAIL_BODY=$(_mail_body) MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" if [ -n "$MAIL_FROM" ]; then - if ! _contains "$MAIL_FROM" "@"; then + if ! _mail_valid "$MAIL_FROM"; then _err "It seems that the MAIL_FROM=$MAIL_FROM is not a valid email address." return 1 fi @@ -39,7 +46,7 @@ mail_send() { MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" if [ -n "$MAIL_TO" ]; then - if ! _contains "$MAIL_TO" "@"; then + if ! _mail_valid "$MAIL_TO"; then _err "It seems that the MAIL_TO=$MAIL_TO is not a valid email address." return 1 fi @@ -55,8 +62,9 @@ mail_send() { contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - result=$({ echo "$_MAIL_BODY" | eval "$_MAIL_CMD"; } 2>&1) + result=$({ _mail_body | eval "$(_mail_cmnd)"; } 2>&1) + # shellcheck disable=SC2181 if [ $? -ne 0 ]; then _debug "mail send error." _err "$result" @@ -67,7 +75,7 @@ mail_send() { return 0 } -_mail_cmnd() { +_mail_bin() { if [ -n "$MAIL_BIN" ]; then _MAIL_BIN="$MAIL_BIN" elif _exists "sendmail"; then @@ -83,6 +91,10 @@ _mail_cmnd() { return 1 fi + echo "$_MAIL_BIN" +} + +_mail_cmnd() { case $(basename "$_MAIL_BIN") in sendmail) if [ -n "$MAIL_FROM" ]; then @@ -105,16 +117,22 @@ _mail_cmnd() { } _mail_body() { - if [ "$_MAIL_BIN" = "sendmail" ] || [ "$_MAIL_BIN" = "ssmtp" ]; then - if [ -n "$MAIL_FROM" ]; then - echo "From: $MAIL_FROM" - fi + case $(basename "$_MAIL_BIN") in + sendmail | ssmtp) + if [ -n "$MAIL_FROM" ]; then + echo "From: $MAIL_FROM" + fi - echo "To: $MAIL_TO" - echo "Subject: $subject" - echo "Content-Type: $contenttype" - echo - fi + echo "To: $MAIL_TO" + echo "Subject: $subject" + echo "Content-Type: $contenttype" + echo + ;; + esac echo "$_content" } + +_mail_valid() { + [ -n "$MAIL_NOVALIDATE" ] || _contains "$1" "@" +} From 09bce5e6d6be6b97b3c843b815087874e3e44a21 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Tue, 28 May 2019 08:47:33 +0800 Subject: [PATCH 019/191] sync (#2297) * Create LICENSE.md * remove _hostingde_parse_no_strip_whitespace function as this breaks API requests * Fix sessionid parsing on BSD * Make travis happy. (SC2020) * fix for https://github.com/Neilpang/acme.sh/issues/2286 * Notify mail update (#2293) * feat: disable e-mail validation if MAIL_NOVALIDATE is set * fix: expose _MAIL_BIN variable * fix: call _mail_body and _mail_cmnd directly to make sure that all used variables are exposed * fix: update notify/mail.sh Co-Authored-By: Matej Mihevc * fix: remove useless echo, quote eval --- LICENSE.md | 674 ++++++++++++++++++++++++++++++++++++++++ acme.sh | 2 + dnsapi/dns_hostingde.sh | 16 +- dnsapi/dns_netcup.sh | 2 +- notify/mail.sh | 48 ++- 5 files changed, 712 insertions(+), 30 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/acme.sh b/acme.sh index 60580f8..eb2194a 100755 --- a/acme.sh +++ b/acme.sh @@ -4338,6 +4338,8 @@ $_authorizations_map" if [ "$status" = "pending" ]; then _info "Pending" + elif [ "$status" = "processing" ]; then + _info "Processing" else _err "$d:Verify error:$response" _clearupwebbroot "$_currentRoot" "$removelevel" "$token" diff --git a/dnsapi/dns_hostingde.sh b/dnsapi/dns_hostingde.sh index 1aa7039..9e3e566 100644 --- a/dnsapi/dns_hostingde.sh +++ b/dnsapi/dns_hostingde.sh @@ -53,18 +53,6 @@ _hostingde_parse() { fi } -_hostingde_parse_no_strip_whitespace() { - find="${1}" - if [ "${2}" ]; then - notfind="${2}" - fi - if [ "${notfind}" ]; then - _egrep_o \""${find}\":.*" | grep -v "${notfind}" | cut -d ':' -f 2 | cut -d ',' -f 1 - else - _egrep_o \""${find}\":.*" | cut -d ':' -f 2 | cut -d ',' -f 1 - fi -} - _hostingde_getZoneConfig() { _info "Getting ZoneConfig" curZone="${fulldomain#*.}" @@ -97,12 +85,12 @@ _hostingde_getZoneConfig() { zoneConfigDnsServerGroupId=$(echo "${curResult}" | _hostingde_parse "dnsServerGroupId") zoneConfigEmailAddress=$(echo "${curResult}" | _hostingde_parse "emailAddress") zoneConfigDnsSecMode=$(echo "${curResult}" | _hostingde_parse "dnsSecMode") - zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateValues") + zoneConfigTemplateValues=$(echo "${curResult}" | _hostingde_parse "templateValues") if [ "$zoneConfigTemplateValues" != "null" ]; then _debug "Zone is tied to a template." zoneConfigTemplateValuesTemplateId=$(echo "${curResult}" | _hostingde_parse "templateId") - zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse_no_strip_whitespace "templateName") + zoneConfigTemplateValuesTemplateName=$(echo "${curResult}" | _hostingde_parse "templateName") zoneConfigTemplateValuesTemplateReplacementsIPv4=$(echo "${curResult}" | _hostingde_parse "ipv4Replacement") zoneConfigTemplateValuesTemplateReplacementsIPv6=$(echo "${curResult}" | _hostingde_parse "ipv6Replacement") zoneConfigTemplateValuesTemplateReplacementsMailIPv4=$(echo "${curResult}" | _hostingde_parse "mailIpv4Replacement") diff --git a/dnsapi/dns_netcup.sh b/dnsapi/dns_netcup.sh index d5d7c22..d519e4f 100644 --- a/dnsapi/dns_netcup.sh +++ b/dnsapi/dns_netcup.sh @@ -117,7 +117,7 @@ dns_netcup_rm() { login() { tmp=$(_post "{\"action\": \"login\", \"param\": {\"apikey\": \"$NC_Apikey\", \"apipassword\": \"$NC_Apipw\", \"customernumber\": \"$NC_CID\"}}" "$end" "" "POST") - sid=$(_getfield "$tmp" "8" | sed s/\"responsedata\":\{\"apisessionid\":\"//g | sed 's/\"\}\}//g') + sid=$(echo "$tmp" | tr '{}' '\n' | grep apisessionid | cut -d '"' -f 4) _debug "$tmp" if [ "$(_getfield "$msg" "4" | sed s/\"status\":\"//g | sed s/\"//g)" != "success" ]; then _err "$msg" diff --git a/notify/mail.sh b/notify/mail.sh index dbecc3a..ec9aa0d 100644 --- a/notify/mail.sh +++ b/notify/mail.sh @@ -5,6 +5,7 @@ #MAIL_BIN="sendmail" #MAIL_FROM="yyyy@gmail.com" #MAIL_TO="yyyy@gmail.com" +#MAIL_NOVALIDATE="" mail_send() { _subject="$1" @@ -14,22 +15,28 @@ mail_send() { _debug "_content" "$_content" _debug "_statusCode" "$_statusCode" + MAIL_NOVALIDATE="${MAIL_NOVALIDATE:-$(_readaccountconf_mutable MAIL_NOVALIDATE)}" + if [ -n "$MAIL_NOVALIDATE" ]; then + _saveaccountconf_mutable MAIL_NOVALIDATE 1 + else + _clearaccountconf "MAIL_NOVALIDATE" + fi + MAIL_BIN="${MAIL_BIN:-$(_readaccountconf_mutable MAIL_BIN)}" if [ -n "$MAIL_BIN" ] && ! _exists "$MAIL_BIN"; then _err "It seems that the command $MAIL_BIN is not in path." return 1 fi - _MAIL_CMD=$(_mail_cmnd) + _MAIL_BIN=$(_mail_bin) if [ -n "$MAIL_BIN" ]; then _saveaccountconf_mutable MAIL_BIN "$MAIL_BIN" else _clearaccountconf "MAIL_BIN" fi - _MAIL_BODY=$(_mail_body) MAIL_FROM="${MAIL_FROM:-$(_readaccountconf_mutable MAIL_FROM)}" if [ -n "$MAIL_FROM" ]; then - if ! _contains "$MAIL_FROM" "@"; then + if ! _mail_valid "$MAIL_FROM"; then _err "It seems that the MAIL_FROM=$MAIL_FROM is not a valid email address." return 1 fi @@ -39,7 +46,7 @@ mail_send() { MAIL_TO="${MAIL_TO:-$(_readaccountconf_mutable MAIL_TO)}" if [ -n "$MAIL_TO" ]; then - if ! _contains "$MAIL_TO" "@"; then + if ! _mail_valid "$MAIL_TO"; then _err "It seems that the MAIL_TO=$MAIL_TO is not a valid email address." return 1 fi @@ -55,8 +62,9 @@ mail_send() { contenttype="text/plain; charset=utf-8" subject="=?UTF-8?B?$(echo "$_subject" | _base64)?=" - result=$({ echo "$_MAIL_BODY" | eval "$_MAIL_CMD"; } 2>&1) + result=$({ _mail_body | eval "$(_mail_cmnd)"; } 2>&1) + # shellcheck disable=SC2181 if [ $? -ne 0 ]; then _debug "mail send error." _err "$result" @@ -67,7 +75,7 @@ mail_send() { return 0 } -_mail_cmnd() { +_mail_bin() { if [ -n "$MAIL_BIN" ]; then _MAIL_BIN="$MAIL_BIN" elif _exists "sendmail"; then @@ -83,6 +91,10 @@ _mail_cmnd() { return 1 fi + echo "$_MAIL_BIN" +} + +_mail_cmnd() { case $(basename "$_MAIL_BIN") in sendmail) if [ -n "$MAIL_FROM" ]; then @@ -105,16 +117,22 @@ _mail_cmnd() { } _mail_body() { - if [ "$_MAIL_BIN" = "sendmail" ] || [ "$_MAIL_BIN" = "ssmtp" ]; then - if [ -n "$MAIL_FROM" ]; then - echo "From: $MAIL_FROM" - fi + case $(basename "$_MAIL_BIN") in + sendmail | ssmtp) + if [ -n "$MAIL_FROM" ]; then + echo "From: $MAIL_FROM" + fi - echo "To: $MAIL_TO" - echo "Subject: $subject" - echo "Content-Type: $contenttype" - echo - fi + echo "To: $MAIL_TO" + echo "Subject: $subject" + echo "Content-Type: $contenttype" + echo + ;; + esac echo "$_content" } + +_mail_valid() { + [ -n "$MAIL_NOVALIDATE" ] || _contains "$1" "@" +} From 145b1f4fb3cbeafa167d86f8f6004df194e5cd55 Mon Sep 17 00:00:00 2001 From: Kay Roepke Date: Tue, 28 May 2019 13:46:19 +0200 Subject: [PATCH 020/191] Improve compatibility with *BSD xargs The --no-run-if-empty option is a GNU extension and the long version isn't supported by *BSD variants. Instead use the short version (-r) which is present, but ignored as it is the default behavior, in at least FreeBSD: https://www.freebsd.org/cgi/man.cgi?xargs --- dnsapi/dns_gcloud.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_gcloud.sh b/dnsapi/dns_gcloud.sh index c2ead9a..ebbeecf 100755 --- a/dnsapi/dns_gcloud.sh +++ b/dnsapi/dns_gcloud.sh @@ -93,7 +93,7 @@ _dns_gcloud_execute_tr() { } _dns_gcloud_remove_rrs() { - if ! xargs --no-run-if-empty gcloud dns record-sets transaction remove \ + if ! xargs -r gcloud dns record-sets transaction remove \ --name="$fulldomain." \ --ttl="$ttl" \ --type=TXT \ @@ -108,7 +108,7 @@ _dns_gcloud_remove_rrs() { _dns_gcloud_add_rrs() { ttl=60 - if ! xargs --no-run-if-empty gcloud dns record-sets transaction add \ + if ! xargs -r gcloud dns record-sets transaction add \ --name="$fulldomain." \ --ttl="$ttl" \ --type=TXT \ From 937d5b5472a85f998177a00547327f144d2b7fcd Mon Sep 17 00:00:00 2001 From: der-berni Date: Fri, 31 May 2019 08:26:48 +0200 Subject: [PATCH 021/191] try to remove errors in travis-ci --- dnsapi/dns_one.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 74db40b..68f54ed 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -5,7 +5,7 @@ # Author: github: @diseq # Created: 2019-02-17 # Fixed by: @der-berni -# Modified: 2019-05-20 +# Modified: 2019-05-31 # # export ONECOM_User="username" # export ONECOM_Password="password" @@ -18,7 +18,7 @@ dns_one_add() { fulldomain=$1 txtvalue=$2 - + if ! _dns_one_login; then _err "login failed" return 1 @@ -29,12 +29,12 @@ dns_one_add() { _err "root domain not found" return 1 fi - + mysubdomain=$_sub_domain mydomain=$_domain _debug mysubdomain "$mysubdomain" _debug mydomain "$mydomain" - + # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" _debug response "$response" @@ -47,7 +47,7 @@ dns_one_add() { _debug response "$response" id=$(echo "$response" | sed -n "s/{\"result\":{\"data\":{\"type\":\"dns_custom_records\",\"id\":\"\([^\"]*\)\",\"attributes\":{\"prefix\":\"$mysubdomain\",\"type\":\"TXT\",\"content\":\"$txtvalue\",\"priority\":0,\"ttl\":600}}},\"metadata\":null}/\1/p") - + if [ -z "$id" ]; then _err "Add txt record error." return 1 @@ -61,7 +61,7 @@ dns_one_add() { dns_one_rm() { fulldomain=$1 txtvalue=$2 - + if ! _dns_one_login; then _err "login failed" return 1 @@ -72,12 +72,12 @@ dns_one_rm() { _err "root domain not found" return 1 fi - + mysubdomain=$_sub_domain mydomain=$_domain _debug mysubdomain "$mysubdomain" _debug mydomain "$mydomain" - + # get entries response="$(_get "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records")" response="$(echo "$response" | _normalizeJson)" @@ -94,7 +94,7 @@ dns_one_rm() { response="$(_post "$postdata" "https://www.one.com/admin/api/domains/$mydomain/dns/custom_records/$id" "" "DELETE" "application/json")" response="$(echo "$response" | _normalizeJson)" _debug response "$response" - + if [ "$response" = '{"result":null,"metadata":null}' ]; then _info "Removed, OK" return 0 @@ -176,4 +176,4 @@ _dns_one_login() { export _H1="Cookie: ${JSESSIONID}" return 0 - } +} From 1a5279bd6e8773786b388fc31def1c21da6ec2c7 Mon Sep 17 00:00:00 2001 From: der-berni Date: Fri, 31 May 2019 08:55:21 +0200 Subject: [PATCH 023/191] cleanup according to styleguide --- dnsapi/dns_one.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_one.sh b/dnsapi/dns_one.sh index 68f54ed..0fdc3d5 100644 --- a/dnsapi/dns_one.sh +++ b/dnsapi/dns_one.sh @@ -110,19 +110,19 @@ dns_one_rm() { # _sub_domain=_acme-challenge.www # _domain=domain.com _get_root() { - domain=$1 + 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 - + response="$(_get "https://www.one.com/admin/api/domains/$h/dns/custom_records")" - + if ! _contains "$response" "CRMRST_000302"; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain="$h" @@ -160,20 +160,20 @@ _dns_one_login() { postdata="$postdata&password1=$ONECOM_Password" postdata="$postdata&loginTarget=" #_debug postdata "$postdata" - + response="$(_post "$postdata" "https://www.one.com/admin/login.do" "" "POST" "application/x-www-form-urlencoded")" #_debug response "$response" - + # Get SessionID JSESSIONID="$(grep "OneSIDCrmAdmin" "$HTTP_HEADER" | grep "^[Ss]et-[Cc]ookie:" | _head_n 1 | _egrep_o 'OneSIDCrmAdmin=[^;]*;' | tr -d ';')" _debug jsessionid "$JSESSIONID" - + if [ -z "$JSESSIONID" ]; then _err "error sessionid cookie not found" return 1 fi - + export _H1="Cookie: ${JSESSIONID}" - + return 0 } From 561803c0a75a73abe3a392c080d6678b84a31891 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 1 Jun 2019 22:30:25 +0800 Subject: [PATCH 024/191] add deploy hook to docker containers --- Dockerfile | 1 + deploy/docker.sh | 264 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100755 deploy/docker.sh diff --git a/Dockerfile b/Dockerfile index 0e8b58d..8a7fd03 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN apk update -f \ curl \ socat \ tzdata \ + tar \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh diff --git a/deploy/docker.sh b/deploy/docker.sh new file mode 100755 index 0000000..57081cc --- /dev/null +++ b/deploy/docker.sh @@ -0,0 +1,264 @@ +#!/usr/bin/env sh + +#DEPLOY_DOCKER_CONTAINER_LABEL="xxxxxxx" +#DOCKER_HOST=/var/run/docker.sock | tcp://localhost:8888 + + +#DEPLOY_DOCKER_CONTAINER_KEY_FILE="/path/to/key.pem" +#DEPLOY_DOCKER_CONTAINER_CERT_FILE="/path/to/cert.pem" +#DEPLOY_DOCKER_CONTAINER_CA_FILE="/path/to/ca.pem" +#DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem" +#DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload" + +_DEPLOY_DOCKER_WIKI="http://xxxxxx" + +_DOCKER_HOST_DEFAULT="/var/run/docker.sock" + +docker_deploy() { + _cdomain="$1" + _ckey="$2" + _ccert="$3" + _cca="$4" + _cfullchain="$5" + + if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then + _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container." + _err "See: $_DEPLOY_DOCKER_WIKI" + fi + + _savedomainconf DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL" + + if [ "$DOCKER_HOST" ]; then + _saveaccountconf DOCKER_HOST "$DOCKER_HOST" + fi + + if _exists docker && docker version | grep -i docker >/dev/null; then + _info "Using docker command" + export _USE_DOCKER_COMMAND=1 + else + export _USE_DOCKER_COMMAND= + fi + + export _USE_UNIX_SOCKET= + if [ -z "$_USE_DOCKER_COMMAND" ]; then + export _USE_REST= + if [ "$DOCKER_HOST" ]; then + _debug "Try use docker host: $DOCKER_HOST" + export _USE_REST=1 + else + export _DOCKER_SOCK="$_DOCKER_HOST_DEFAULT" + _debug "Try use $_DOCKER_SOCK" + if [ ! -e "$_DOCKER_SOCK" ] || [ ! -w "$_DOCKER_SOCK" ]; then + _err "$_DOCKER_SOCK is not available" + return 1 + fi + export _USE_UNIX_SOCKET=1 + if ! _exists "curl"; then + _err "Please install curl first." + _err "We need curl to work." + return 1 + fi + if ! _check_curl_version; then + return 1 + fi + fi + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then + _savedomainconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then + _savedomainconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then + _savedomainconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then + _savedomainconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then + _savedomainconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" + fi + + _cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")" + _info "Container id: $_cid" + if [ -z "$_cid" ]; then + _err "can not find container id" + return 1 + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then + if ! _docker_cp "$_cid" "$_ckey" "$DEPLOY_DOCKER_CONTAINER_KEY_FILE"; then + return 1 + fi + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then + if ! _docker_cp "$_cid" "$_ccert" "$DEPLOY_DOCKER_CONTAINER_CERT_FILE"; then + return 1 + fi + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then + if ! _docker_cp "$_cid" "$_cca" "$DEPLOY_DOCKER_CONTAINER_CA_FILE"; then + return 1 + fi + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then + if ! _docker_cp "$_cid" "$_cfullchain" "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE"; then + return 1 + fi + fi + + if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then + if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then + return 1 + fi + fi + return 0 +} + +#label +_get_id() { + _label="$1" + if [ "$_USE_DOCKER_COMMAND" ]; then + docker ps -f label="$_label" --format "{{.ID}}" + elif [ "$_USE_REST" ]; then + _err "Not implemented yet." + return 1 + elif [ "$_USE_UNIX_SOCKET" ]; then + _req="{\"label\":[\"$_label\"]}" + _debug2 _req "$_req" + _req="$(printf "%s" "$_req" | _url_encode)" + _debug2 _req "$_req" + listjson="$(_curl_unix_sock "${_DOCKER_SOCK:-$_DOCKER_HOST_DEFAULT}" GET "/containers/json?filters=$_req")" + _debug2 "listjson" "$listjson" + echo "$listjson" | tr '{,' '\n' | grep -i '"id":' | _head_n 1 | cut -d '"' -f 4 + else + _err "Not implemented yet." + return 1 + fi +} + +#id cmd +_docker_exec() { + _eargs="$@" + _debug2 "_docker_exec $_eargs" + _dcid="$1" + shift + if [ "$_USE_DOCKER_COMMAND" ]; then + docker exec -i "$_dcid" $@ + elif [ "$_USE_REST" ]; then + _err "Not implemented yet." + return 1 + elif [ "$_USE_UNIX_SOCKET" ]; then + _cmd="$@" + _cmd="$(printf "$_cmd" | sed 's/ /","/g')" + _debug2 _cmd "$_cmd" + #create exec instance: + cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"$_cmd\"]}")"; + _debug2 cjson "$cjson" + execid="$(echo "$cjson" | cut -d '"' -f 4)" + _debug execid "$execid" + ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")"; + _debug2 ejson "$ejson" + else + _err "Not implemented yet." + return 1 + fi +} + +#id from to +_docker_cp() { + _dcid="$1" + _from="$2" + _to="$3" + _info "Copying file from $_from to $_to" + _dir="$(dirname "$_to")" + _docker_exec "$_dcid" mkdir -p "$_dir" + if [ "$_USE_DOCKER_COMMAND" ]; then + cat "$_from" | _docker_exec "$_dcid" tee "$_to" >/dev/null + if [ "$?" = "0" ]; then + _info "Success" + return 0 + else + _info "Error" + return 1 + fi + elif [ "$_USE_REST" ]; then + _err "Not implemented yet." + return 1 + elif [ "$_USE_UNIX_SOCKET" ]; then + _frompath="$_from" + if _startswith "$_frompath" '/'; then + _frompath="$(echo "$_from" | cut -b 2- )" #remove the first '/' char + fi + _debug2 "_frompath" "$_frompath" + _toname="$(basename "$_to")" + _debug2 "_toname" "$_toname" + if ! tar --transform="s,$_frompath,$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then + _err "copy error" + return 1 + fi + return 0 + else + _err "Not implemented yet." + return 1 + fi + +} + +#sock method endpoint data content-type +_curl_unix_sock() { + _socket="$1" + _method="$2" + _endpoint="$3" + _data="$4" + _ctype="$5" + if [ -z "$_ctype" ]; then + _ctype="Content-Type: application/json" + fi + _debug _data "$_data" + _debug2 "url" "http://localhost$_endpoint" + if [ "$_CURL_NO_HOST" ]; then + _cux_url="http:$_endpoint" + else + _cux_url="http://localhost$_endpoint" + fi + + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + curl -vvv --silent --unix-socket "$_socket" -X $_method --data-binary "$_data" --header "$_ctype" "$_cux_url" + else + curl --silent --unix-socket "$_socket" -X $_method --data-binary "$_data" --header "$_ctype" "$_cux_url" + fi + +} + +_check_curl_version() { + _cversion="$(curl -V | grep '^curl ' | cut -d ' ' -f 2)" + _debug2 "_cversion" "$_cversion" + + _major="$(_getfield "$_cversion" 1 '.')" + _debug2 "_major" "$_major" + + _minor="$(_getfield "$_cversion" 2 '.')" + _debug2 "_minor" "$_minor" + + if [ "$_major$_minor" -lt "740" ]; then + _err "curl v$_cversion doesn't support unit socket" + return 1 + fi + if [ "$_major$_minor" -lt "750" ]; then + _debug "Use short host name" + export _CURL_NO_HOST=1 + else + export _CURL_NO_HOST= + fi + return 0 +} + From 0bbaa51945f882c1197f64ccb8c4522cd4d3b304 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Jun 2019 10:05:24 +0800 Subject: [PATCH 025/191] fix format --- deploy/docker.sh | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/deploy/docker.sh b/deploy/docker.sh index 57081cc..0e22ddc 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -1,8 +1,6 @@ #!/usr/bin/env sh #DEPLOY_DOCKER_CONTAINER_LABEL="xxxxxxx" -#DOCKER_HOST=/var/run/docker.sock | tcp://localhost:8888 - #DEPLOY_DOCKER_CONTAINER_KEY_FILE="/path/to/key.pem" #DEPLOY_DOCKER_CONTAINER_CERT_FILE="/path/to/cert.pem" @@ -10,7 +8,7 @@ #DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/path/to/fullchain.pem" #DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload" -_DEPLOY_DOCKER_WIKI="http://xxxxxx" +_DEPLOY_DOCKER_WIKI="https://github.com/Neilpang/acme.sh/wiki/deploy-to-docker-containers" _DOCKER_HOST_DEFAULT="/var/run/docker.sock" @@ -20,7 +18,7 @@ docker_deploy() { _ccert="$3" _cca="$4" _cfullchain="$5" - + _debug _cdomain "$_cdomain" if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container." _err "See: $_DEPLOY_DOCKER_WIKI" @@ -136,7 +134,7 @@ _get_id() { _debug2 _req "$_req" _req="$(printf "%s" "$_req" | _url_encode)" _debug2 _req "$_req" - listjson="$(_curl_unix_sock "${_DOCKER_SOCK:-$_DOCKER_HOST_DEFAULT}" GET "/containers/json?filters=$_req")" + listjson="$(_curl_unix_sock "${_DOCKER_SOCK:-$_DOCKER_HOST_DEFAULT}" GET "/containers/json?filters=$_req")" _debug2 "listjson" "$listjson" echo "$listjson" | tr '{,' '\n' | grep -i '"id":' | _head_n 1 | cut -d '"' -f 4 else @@ -147,25 +145,25 @@ _get_id() { #id cmd _docker_exec() { - _eargs="$@" + _eargs="$*" _debug2 "_docker_exec $_eargs" _dcid="$1" shift if [ "$_USE_DOCKER_COMMAND" ]; then - docker exec -i "$_dcid" $@ + docker exec -i "$_dcid" "$@" elif [ "$_USE_REST" ]; then _err "Not implemented yet." return 1 elif [ "$_USE_UNIX_SOCKET" ]; then - _cmd="$@" - _cmd="$(printf "$_cmd" | sed 's/ /","/g')" + _cmd="$*" + _cmd="$(printf "%s" "$_cmd" | sed 's/ /","/g')" _debug2 _cmd "$_cmd" #create exec instance: - cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"$_cmd\"]}")"; + cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"$_cmd\"]}")" _debug2 cjson "$cjson" execid="$(echo "$cjson" | cut -d '"' -f 4)" _debug execid "$execid" - ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")"; + ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")" _debug2 ejson "$ejson" else _err "Not implemented yet." @@ -182,7 +180,11 @@ _docker_cp() { _dir="$(dirname "$_to")" _docker_exec "$_dcid" mkdir -p "$_dir" if [ "$_USE_DOCKER_COMMAND" ]; then - cat "$_from" | _docker_exec "$_dcid" tee "$_to" >/dev/null + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + _docker_exec "$_dcid" tee "$_to" <"$_from" + else + _docker_exec "$_dcid" tee "$_to" <"$_from" >/dev/null + fi if [ "$?" = "0" ]; then _info "Success" return 0 @@ -196,7 +198,7 @@ _docker_cp() { elif [ "$_USE_UNIX_SOCKET" ]; then _frompath="$_from" if _startswith "$_frompath" '/'; then - _frompath="$(echo "$_from" | cut -b 2- )" #remove the first '/' char + _frompath="$(echo "$_from" | cut -b 2-)" #remove the first '/' char fi _debug2 "_frompath" "$_frompath" _toname="$(basename "$_to")" @@ -232,9 +234,9 @@ _curl_unix_sock() { fi if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then - curl -vvv --silent --unix-socket "$_socket" -X $_method --data-binary "$_data" --header "$_ctype" "$_cux_url" + curl -vvv --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url" else - curl --silent --unix-socket "$_socket" -X $_method --data-binary "$_data" --header "$_ctype" "$_cux_url" + curl --silent --unix-socket "$_socket" -X "$_method" --data-binary "$_data" --header "$_ctype" "$_cux_url" fi } @@ -261,4 +263,3 @@ _check_curl_version() { fi return 0 } - From 64928b28bcb77165e9630355b37e85574da3fe65 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Jun 2019 11:11:34 +0800 Subject: [PATCH 026/191] trim quotation marks --- deploy/docker.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/deploy/docker.sh b/deploy/docker.sh index 0e22ddc..18a9d42 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -19,6 +19,7 @@ docker_deploy() { _cca="$4" _cfullchain="$5" _debug _cdomain "$_cdomain" + DEPLOY_DOCKER_CONTAINER_LABEL="$(echo "$DEPLOY_DOCKER_CONTAINER_LABEL" | tr -d '"')" if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container." _err "See: $_DEPLOY_DOCKER_WIKI" @@ -62,22 +63,27 @@ docker_deploy() { fi fi + DEPLOY_DOCKER_CONTAINER_KEY_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" | tr -d '"')" if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then _savedomainconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" fi + DEPLOY_DOCKER_CONTAINER_CERT_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" | tr -d '"')" if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then _savedomainconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" fi + DEPLOY_DOCKER_CONTAINER_CA_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_CA_FILE" | tr -d '"')" if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then _savedomainconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" fi + DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" | tr -d '"')" if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then _savedomainconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" fi + DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="$(echo "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" | tr -d '"')" if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then _savedomainconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" fi From a18c3ff07d83a28dad3bf4391633a3e86cf6721d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Jun 2019 15:21:08 +0800 Subject: [PATCH 027/191] use `sh -c` --- deploy/docker.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/deploy/docker.sh b/deploy/docker.sh index 18a9d42..d2dc70b 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -156,21 +156,25 @@ _docker_exec() { _dcid="$1" shift if [ "$_USE_DOCKER_COMMAND" ]; then - docker exec -i "$_dcid" "$@" + docker exec -i "$_dcid" sh -c "$*" elif [ "$_USE_REST" ]; then _err "Not implemented yet." return 1 elif [ "$_USE_UNIX_SOCKET" ]; then _cmd="$*" - _cmd="$(printf "%s" "$_cmd" | sed 's/ /","/g')" + #_cmd="$(printf "%s" "$_cmd" | sed 's/ /","/g')" _debug2 _cmd "$_cmd" #create exec instance: - cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"$_cmd\"]}")" + cjson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/containers/$_dcid/exec" "{\"Cmd\": [\"sh\", \"-c\", \"$_cmd\"]}")" _debug2 cjson "$cjson" execid="$(echo "$cjson" | cut -d '"' -f 4)" _debug execid "$execid" ejson="$(_curl_unix_sock "$_DOCKER_SOCK" POST "/exec/$execid/start" "{\"Detach\": false,\"Tty\": false}")" _debug2 ejson "$ejson" + if [ "$ejson" ]; then + _err "$ejson" + return 1 + fi else _err "Not implemented yet." return 1 From aec6636205d539eae83f54b4fa4f868f5079d55b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Jun 2019 19:36:11 +0800 Subject: [PATCH 028/191] add _getdeployconf --- acme.sh | 17 +++++++++++++++++ deploy/docker.sh | 24 ++++++++++++------------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/acme.sh b/acme.sh index eb2194a..45f0ea8 100755 --- a/acme.sh +++ b/acme.sh @@ -2078,6 +2078,23 @@ _readdomainconf() { _read_conf "$DOMAIN_CONF" "$1" } +#key value base64encode +_savedeployconf() { + _savedomainconf "SAVED_$1" "$2" "$3" + #remove later + _clearaccountconf "$1" +} + +#key +_getdeployconf() { + _rac_key="$1" + if [ "$(eval echo \$"$_rac_key")" ]; then + return 0 # do nothing + fi + _saved=$(_readdomainconf "SAVED_$_rac_key") + eval "export $_rac_key=$_saved" +} + #_saveaccountconf key value base64encode _saveaccountconf() { _save_conf "$ACCOUNT_CONF_PATH" "$@" diff --git a/deploy/docker.sh b/deploy/docker.sh index d2dc70b..6f3a271 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -19,13 +19,13 @@ docker_deploy() { _cca="$4" _cfullchain="$5" _debug _cdomain "$_cdomain" - DEPLOY_DOCKER_CONTAINER_LABEL="$(echo "$DEPLOY_DOCKER_CONTAINER_LABEL" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container." _err "See: $_DEPLOY_DOCKER_WIKI" fi - _savedomainconf DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL" + _savedeployconf DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL" if [ "$DOCKER_HOST" ]; then _saveaccountconf DOCKER_HOST "$DOCKER_HOST" @@ -63,29 +63,29 @@ docker_deploy() { fi fi - DEPLOY_DOCKER_CONTAINER_KEY_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then - _savedomainconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" + _savedeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" fi - DEPLOY_DOCKER_CONTAINER_CERT_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then - _savedomainconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" + _savedeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" fi - DEPLOY_DOCKER_CONTAINER_CA_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_CA_FILE" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then - _savedomainconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" + _savedeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" fi - DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="$(echo "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then - _savedomainconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" + _savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" fi - DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="$(echo "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" | tr -d '"')" + _getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then - _savedomainconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" + _savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" fi _cid="$(_get_id "$DEPLOY_DOCKER_CONTAINER_LABEL")" From dc5eda7ebb53ba9dadb631d703829b37a3879be7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 2 Jun 2019 20:04:36 +0800 Subject: [PATCH 029/191] fix savedeployconf --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 45f0ea8..4c856db 100755 --- a/acme.sh +++ b/acme.sh @@ -2082,7 +2082,7 @@ _readdomainconf() { _savedeployconf() { _savedomainconf "SAVED_$1" "$2" "$3" #remove later - _clearaccountconf "$1" + _cleardomainconf "$1" } #key From 924e0261f9431be00e24d08080bd7774ee0e4de0 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Sun, 2 Jun 2019 13:09:57 -0400 Subject: [PATCH 030/191] Update dns_freedns.sh --- dnsapi/dns_freedns.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 5928837..e76e649 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -7,6 +7,7 @@ # #Author: David Kerr #Report Bugs here: https://github.com/dkerr64/acme.sh +#or here... https://github.com/Neilpang/acme.sh/issues/2305 # ######## Public functions ##################### From 51099bf1487a3637f00ccd2ba033128afab0416c Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Mon, 3 Jun 2019 01:54:04 +0200 Subject: [PATCH 031/191] Add postmark notify --- notify/postmark.sh | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 notify/postmark.sh diff --git a/notify/postmark.sh b/notify/postmark.sh new file mode 100644 index 0000000..6523feb --- /dev/null +++ b/notify/postmark.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env sh + +#Support postmarkapp.com API (https://postmarkapp.com/developer/user-guide/sending-email/sending-with-api) + +#POSTMARK_TOKEN="" +#POSTMARK_TO="xxxx@xxx.com" +#POSTMARK_FROM="xxxx@cccc.com" + +postmark_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + POSTMARK_TOKEN="${POSTMARK_TOKEN:-$(_readaccountconf_mutable POSTMARK_TOKEN)}" + if [ -z "$POSTMARK_TOKEN" ]; then + POSTMARK_TOKEN="" + _err "You didn't specify a POSTMARK api token POSTMARK_TOKEN yet ." + _err "You can get yours from here https://account.postmarkapp.com" + return 1 + fi + _saveaccountconf_mutable POSTMARK_TOKEN "$POSTMARK_TOKEN" + + POSTMARK_TO="${POSTMARK_TO:-$(_readaccountconf_mutable POSTMARK_TO)}" + if [ -z "$POSTMARK_TO" ]; then + POSTMARK_TO="" + _err "You didn't specify an email to POSTMARK_TO receive messages." + return 1 + fi + _saveaccountconf_mutable POSTMARK_TO "$POSTMARK_TO" + + POSTMARK_FROM="${POSTMARK_FROM:-$(_readaccountconf_mutable POSTMARK_FROM)}" + if [ -z "$POSTMARK_FROM" ]; then + POSTMARK_FROM="" + _err "You didn't specify an email from POSTMARK_FROM receive messages." + return 1 + fi + _saveaccountconf_mutable POSTMARK_FROM "$POSTMARK_FROM" + + export _H1="Accept: application/json" + export _H2="Content-Type: application/json" + export _H3="X-Postmark-Server-Token: $POSTMARK_TOKEN" + + _content="$(echo "$_content" | _json_encode)" + _data="{\"To\": \"$POSTMARK_TO\", \"From\": \"$POSTMARK_FROM\", \"Subject\": \"$_subject\", \"TextBody\": \"$_content\"}" + if _post "$_data" "https://api.postmarkapp.com/email"; then + # shellcheck disable=SC2154 + _message=$(printf "%s\n" "$response" | _lower_case | _egrep_o "\"message\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + if [ "$_message" = "ok" ]; then + _info "postmark send success." + return 0 + fi + fi + _err "postmark send error." + _err "$response" + return 1 + +} From 2e3ddd3a61da8a412d0926520e9a0006bd48857f Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 3 Jun 2019 20:55:22 +0800 Subject: [PATCH 032/191] trim quotation marks --- acme.sh | 7 ++++++- deploy/docker.sh | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4c856db..42988ac 100755 --- a/acme.sh +++ b/acme.sh @@ -2088,7 +2088,12 @@ _savedeployconf() { #key _getdeployconf() { _rac_key="$1" - if [ "$(eval echo \$"$_rac_key")" ]; then + _rac_value="$(eval echo \$"$_rac_key")" + if [ "$_rac_value" ]; then + if _startswith "$_rac_value" '"' && _endswith "$_rac_value" '"'; then + _debug2 "trim quotation marks" + eval "export $_rac_key=$_rac_value" + fi return 0 # do nothing fi _saved=$(_readdomainconf "SAVED_$_rac_key") diff --git a/deploy/docker.sh b/deploy/docker.sh index 6f3a271..dc3c010 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -20,6 +20,7 @@ docker_deploy() { _cfullchain="$5" _debug _cdomain "$_cdomain" _getdeployconf DEPLOY_DOCKER_CONTAINER_LABEL + _debug2 DEPLOY_DOCKER_CONTAINER_LABEL "$DEPLOY_DOCKER_CONTAINER_LABEL" if [ -z "$DEPLOY_DOCKER_CONTAINER_LABEL" ]; then _err "The DEPLOY_DOCKER_CONTAINER_LABEL variable is not defined, we use this label to find the container." _err "See: $_DEPLOY_DOCKER_WIKI" @@ -64,26 +65,31 @@ docker_deploy() { fi _getdeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE + _debug2 DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" if [ "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" ]; then _savedeployconf DEPLOY_DOCKER_CONTAINER_KEY_FILE "$DEPLOY_DOCKER_CONTAINER_KEY_FILE" fi _getdeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE + _debug2 DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" if [ "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" ]; then _savedeployconf DEPLOY_DOCKER_CONTAINER_CERT_FILE "$DEPLOY_DOCKER_CONTAINER_CERT_FILE" fi _getdeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE + _debug2 DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" if [ "$DEPLOY_DOCKER_CONTAINER_CA_FILE" ]; then _savedeployconf DEPLOY_DOCKER_CONTAINER_CA_FILE "$DEPLOY_DOCKER_CONTAINER_CA_FILE" fi _getdeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE + _debug2 DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" if [ "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" ]; then _savedeployconf DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE "$DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE" fi _getdeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD + _debug2 DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then _savedeployconf DEPLOY_DOCKER_CONTAINER_RELOAD_CMD "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" fi @@ -188,6 +194,7 @@ _docker_cp() { _to="$3" _info "Copying file from $_from to $_to" _dir="$(dirname "$_to")" + _debug2 _dir "$_dir" _docker_exec "$_dcid" mkdir -p "$_dir" if [ "$_USE_DOCKER_COMMAND" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then From 951bd3a5172945d36344428d6710e211157ad50b Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 3 Jun 2019 21:03:03 +0800 Subject: [PATCH 033/191] minor, check for mkdir --- deploy/docker.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deploy/docker.sh b/deploy/docker.sh index dc3c010..4e55099 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -195,7 +195,10 @@ _docker_cp() { _info "Copying file from $_from to $_to" _dir="$(dirname "$_to")" _debug2 _dir "$_dir" - _docker_exec "$_dcid" mkdir -p "$_dir" + if ! _docker_exec "$_dcid" mkdir -p "$_dir"; then + _err "Can not create dir: $_dir" + return 1 + fi if [ "$_USE_DOCKER_COMMAND" ]; then if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then _docker_exec "$_dcid" tee "$_to" <"$_from" From 10d1361a2c4baa57dc7db17af3a1ac42300c9af2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 10 Jun 2019 22:35:55 +0800 Subject: [PATCH 034/191] add guide link --- dnsapi/dns_myapi.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dnsapi/dns_myapi.sh b/dnsapi/dns_myapi.sh index 6bf6250..2451d19 100755 --- a/dnsapi/dns_myapi.sh +++ b/dnsapi/dns_myapi.sh @@ -11,6 +11,8 @@ # ######## Public functions ##################### +# Please Read this guide first: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide + #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_myapi_add() { fulldomain=$1 From 465ece5d2529f38f44c147a7b554a52884bd001c Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 10 Jun 2019 22:40:14 +0800 Subject: [PATCH 035/191] fix format --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 42988ac..7aa500e 100755 --- a/acme.sh +++ b/acme.sh @@ -2092,7 +2092,7 @@ _getdeployconf() { if [ "$_rac_value" ]; then if _startswith "$_rac_value" '"' && _endswith "$_rac_value" '"'; then _debug2 "trim quotation marks" - eval "export $_rac_key=$_rac_value" + eval "export $_rac_key=$_rac_value" fi return 0 # do nothing fi From e05ef230a7b62fff63865843d0ed182305a43ef4 Mon Sep 17 00:00:00 2001 From: AndreyIsakov Date: Tue, 11 Jun 2019 19:37:39 +0300 Subject: [PATCH 036/191] test CI error --- dnsapi/dns_regru.sh | 205 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 dnsapi/dns_regru.sh diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh new file mode 100644 index 0000000..cd93189 --- /dev/null +++ b/dnsapi/dns_regru.sh @@ -0,0 +1,205 @@ +#!/usr/bin/env sh + +# +#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# +#CF_Email="xxxx@sss.com" + +CF_Api="https://api.cloudflare.com/client/v4" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_cf_add() { + fulldomain=$1 + txtvalue=$2 + + CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" + CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then + CF_Key="" + CF_Email="" + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." + return 1 + fi + + if ! _contains "$CF_Email" "@"; then + _err "It seems that the CF_Email=$CF_Email is not a valid email address." + _err "Please check and retry." + return 1 + fi + + #save the api key and email to the account conf file. + _saveaccountconf_mutable CF_Key "$CF_Key" + _saveaccountconf_mutable CF_Email "$CF_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" + + _debug "Getting txt records" + _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain" + + if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then + _err "Error" + return 1 + fi + + # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so + # we can not use updating anymore. + # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) + # _debug count "$count" + # if [ "$count" = "0" ]; then + _info "Adding record" + if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then + if _contains "$response" "$txtvalue"; then + _info "Added, OK" + return 0 + elif _contains "$response" "The record already exists"; then + _info "Already exists, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + return 1 + # else + # _info "Updating record" + # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) + # _debug "record_id" "$record_id" + # + # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" + # if [ "$?" = "0" ]; then + # _info "Updated, OK" + # return 0 + # fi + # _err "Update error" + # return 1 + # fi + +} + +#fulldomain txtvalue +dns_cf_rm() { + fulldomain=$1 + txtvalue=$2 + + CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" + CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then + CF_Key="" + CF_Email="" + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." + 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" + _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue" + + if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | 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 "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | 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 ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then + _err "Delete record error." + return 1 + fi + _contains "$response" '"success":true' + 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=1 + 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 ! _cf_rest GET "zones?name=$h"; then + return 1 + fi + + if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then + _domain_id=$(echo "$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 +} + +_cf_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + email_trimmed=$(echo $CF_Email | tr -d '"') + key_trimmed=$(echo $CF_Key | tr -d '"') + + export _H1="X-Auth-Email: $email_trimmed" + export _H2="X-Auth-Key: $key_trimmed" + export _H3="Content-Type: application/json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$CF_Api/$ep" "" "$m")" + else + response="$(_get "$CF_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + return 0 +} From 6151debeab598245fc86cb711b759f1816a88b89 Mon Sep 17 00:00:00 2001 From: AndreyIsakov Date: Tue, 11 Jun 2019 19:59:02 +0300 Subject: [PATCH 037/191] dnsapi for regru: CI linter ok --- dnsapi/dns_regru.sh | 211 ++++++++------------------------------------ 1 file changed, 36 insertions(+), 175 deletions(-) diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh index cd93189..4efec56 100644 --- a/dnsapi/dns_regru.sh +++ b/dnsapi/dns_regru.sh @@ -1,205 +1,66 @@ #!/usr/bin/env sh # -#CF_Key="sdfsdfsdfljlbjkljlkjsdfoiwje" +# REGRU_API_Username="test" # -#CF_Email="xxxx@sss.com" +# REGRU_API_Password="test" +# +_domain=$_domain -CF_Api="https://api.cloudflare.com/client/v4" +REGRU_API_URL="https://api.reg.ru/api/regru2" ######## Public functions ##################### -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_cf_add() { +dns_regru_add() { fulldomain=$1 txtvalue=$2 - CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" - CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi - - if ! _contains "$CF_Email" "@"; then - _err "It seems that the CF_Email=$CF_Email is not a valid email address." - _err "Please check and retry." + REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}" + REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}" + if [ -z "$REGRU_API_Username" ] || [ -z "$REGRU_API_Password" ]; then + REGRU_API_Username="" + REGRU_API_Password="" + _err "You don't specify regru password or username." return 1 fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable CF_Key "$CF_Key" - _saveaccountconf_mutable CF_Email "$CF_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" + _saveaccountconf_mutable REGRU_API_Username "$REGRU_API_Username" + _saveaccountconf_mutable REGRU_API_Password "$REGRU_API_Password" - _debug "Getting txt records" - _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain" - - if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then - _err "Error" - return 1 - fi + _info "Adding TXT record to ${fulldomain}" + response="$(_get "$REGRU_API_URL/zone/add_txt?input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22text%22:%22${txtvalue}%22,%22output_content_type%22:%22plain%22}&input_format=json")" - # For wildcard cert, the main root domain and the wildcard domain have the same txt subdomain name, so - # we can not use updating anymore. - # count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | cut -d : -f 2) - # _debug count "$count" - # if [ "$count" = "0" ]; then - _info "Adding record" - if _cf_rest POST "zones/$_domain_id/dns_records" "{\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"ttl\":120}"; then - if _contains "$response" "$txtvalue"; then - _info "Added, OK" - return 0 - elif _contains "$response" "The record already exists"; then - _info "Already exists, OK" - return 0 - else - _err "Add txt record error." - return 1 - fi + if _contains "${response}" 'success'; then + return 0 fi - _err "Add txt record error." + _err "Could not create resource record, check logs" + _err "${response}" return 1 - # else - # _info "Updating record" - # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - # _debug "record_id" "$record_id" - # - # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - # if [ "$?" = "0" ]; then - # _info "Updated, OK" - # return 0 - # fi - # _err "Update error" - # return 1 - # fi - } -#fulldomain txtvalue -dns_cf_rm() { +dns_regru_rm() { fulldomain=$1 txtvalue=$2 - - CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" - CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi - - _debug "First detect the root zone" - if ! _get_root "$fulldomain"; then - _err "invalid domain" + echo 'RM-ONE '"$1" + echo 'RM-TWO '"$2" + + REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}" + REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}" + if [ -z "$REGRU_API_Username" ] || [ -z "$REGRU_API_Password" ]; then + REGRU_API_Username="" + REGRU_API_Password="" + _err "You don't specify regru password or username." return 1 fi - _debug _domain_id "$_domain_id" - _debug _sub_domain "$_sub_domain" - _debug _domain "$_domain" - _debug "Getting txt records" - _cf_rest GET "zones/${_domain_id}/dns_records?type=TXT&name=$fulldomain&content=$txtvalue" + _info "Deleting resource record $fulldomain" + response="$(_get "$REGRU_API_URL/zone/remove_record?input_data={%22username%22:%22${REGRU_API_Username}%22,%22password%22:%22${REGRU_API_Password}%22,%22domains%22:[{%22dname%22:%22${_domain}%22}],%22subdomain%22:%22_acme-challenge%22,%22content%22:%22${txtvalue}%22,%22record_type%22:%22TXT%22,%22output_content_type%22:%22plain%22}&input_format=json")" - if ! printf "%s" "$response" | grep \"success\":true >/dev/null; then - _err "Error" - return 1 + if _contains "${response}" 'success'; then + return 0 fi - - count=$(printf "%s\n" "$response" | _egrep_o "\"count\":[^,]*" | 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 "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | 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 ! _cf_rest DELETE "zones/$_domain_id/dns_records/$record_id"; then - _err "Delete record error." - return 1 - fi - _contains "$response" '"success":true' - 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=1 - 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 ! _cf_rest GET "zones?name=$h"; then - return 1 - fi - - if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then - _domain_id=$(echo "$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 + _err "Could not delete resource record, check logs" + _err "${response}" return 1 } - -_cf_rest() { - m=$1 - ep="$2" - data="$3" - _debug "$ep" - - email_trimmed=$(echo $CF_Email | tr -d '"') - key_trimmed=$(echo $CF_Key | tr -d '"') - - export _H1="X-Auth-Email: $email_trimmed" - export _H2="X-Auth-Key: $key_trimmed" - export _H3="Content-Type: application/json" - - if [ "$m" != "GET" ]; then - _debug data "$data" - response="$(_post "$data" "$CF_Api/$ep" "" "$m")" - else - response="$(_get "$CF_Api/$ep")" - fi - - if [ "$?" != "0" ]; then - _err "error $ep" - return 1 - fi - _debug2 response "$response" - return 0 -} From 487d2a9221802f5c4287c3a4637245f8ce1e329c Mon Sep 17 00:00:00 2001 From: AndreyIsakov Date: Tue, 11 Jun 2019 20:13:48 +0300 Subject: [PATCH 038/191] dnsapi for regru: CI linter remove empty line --- dnsapi/dns_regru.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh index 4efec56..4c9dc9f 100644 --- a/dnsapi/dns_regru.sh +++ b/dnsapi/dns_regru.sh @@ -24,7 +24,6 @@ dns_regru_add() { return 1 fi - _saveaccountconf_mutable REGRU_API_Username "$REGRU_API_Username" _saveaccountconf_mutable REGRU_API_Password "$REGRU_API_Password" From 9b564431b08bc433e34c4d4b1cdac45eb333c4c5 Mon Sep 17 00:00:00 2001 From: Endre Szabo Date: Thu, 13 Jun 2019 12:39:38 +0200 Subject: [PATCH 039/191] Help text suggests bad practice. Please remove the phrase `No news is good news.` as it suggests to decide to go on with a bad operational habit. Why I am stating this is because that `no news` also could mean that: - your `cron` daemon stopped working, - your MTA has issues (in case or mail notifications of course), - anything in between the host running `acme.sh` and your client went wrong. (... and probably you will not notice in time if `acme.sh` would otherwise send an error notification (if it runs anyway)) If you expect a daily mail (using `--notify-level 3`) you can always be sure that `acme.sh` has ran successfully before. You can also tick the `acme.sh` checkbox in the daily operational report of your enterprise. ;) --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 7aa500e..9bcab81 100755 --- a/acme.sh +++ b/acme.sh @@ -6108,7 +6108,7 @@ Parameters: --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT. 0: disabled, no notification will be sent. - 1: send notification only when there is an error. No news is good news. + 1: send notification only when there is an error. 2: send notification when a cert is successfully renewed, or there is an error 3: send notification when a cert is skipped, renewdd, or error --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT. From f2c6e3f65b41e66257744afb8c8baca89c2d18cf Mon Sep 17 00:00:00 2001 From: AndreyIsakov Date: Thu, 13 Jun 2019 19:31:44 +0300 Subject: [PATCH 040/191] dnsapi for regru: replace echo by _info() --- dnsapi/dns_regru.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh index 4c9dc9f..ff4443b 100644 --- a/dnsapi/dns_regru.sh +++ b/dnsapi/dns_regru.sh @@ -41,8 +41,8 @@ dns_regru_add() { dns_regru_rm() { fulldomain=$1 txtvalue=$2 - echo 'RM-ONE '"$1" - echo 'RM-TWO '"$2" + _info 'RM-ONE '"$1" + _info 'RM-TWO '"$2" REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}" REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}" From d883a870e183a5f5679a0d3be597b38745c359c5 Mon Sep 17 00:00:00 2001 From: AndreyIsakov Date: Thu, 13 Jun 2019 19:56:40 +0300 Subject: [PATCH 041/191] dnsapi for regru: remove debug info --- dnsapi/dns_regru.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/dnsapi/dns_regru.sh b/dnsapi/dns_regru.sh index ff4443b..369f62a 100644 --- a/dnsapi/dns_regru.sh +++ b/dnsapi/dns_regru.sh @@ -41,8 +41,6 @@ dns_regru_add() { dns_regru_rm() { fulldomain=$1 txtvalue=$2 - _info 'RM-ONE '"$1" - _info 'RM-TWO '"$2" REGRU_API_Username="${REGRU_API_Username:-$(_readaccountconf_mutable REGRU_API_Username)}" REGRU_API_Password="${REGRU_API_Password:-$(_readaccountconf_mutable REGRU_API_Password)}" From 882ac74a0c0dfa1e389dd54e6ce8180eddcfda91 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 14 Jun 2019 22:41:28 +0800 Subject: [PATCH 042/191] fix issue: clear Le_Vlist earlier --- acme.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 9bcab81..7042533 100755 --- a/acme.sh +++ b/acme.sh @@ -3863,7 +3863,7 @@ issue() { _savedomainconf "Le_Keylength" "$_key_length" vlist="$Le_Vlist" - + _cleardomainconf "Le_Vlist" _info "Getting domain auth token for each domain" sep='#' dvsep=',' @@ -4512,8 +4512,6 @@ $_authorizations_map" fi fi - _cleardomainconf "Le_Vlist" - if [ "$ACME_VERSION" = "2" ]; then _debug "v2 chain." else From c6b6855131108ca16c94e25349e24c1243d7703b Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 14 Jun 2019 23:55:59 +0800 Subject: [PATCH 043/191] fix https://github.com/Neilpang/acme.sh/issues/2321 https://github.com/Neilpang/acme.sh/issues/2291 --- acme.sh | 133 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/acme.sh b/acme.sh index 7042533..bcc88ef 100755 --- a/acme.sh +++ b/acme.sh @@ -2450,7 +2450,7 @@ _initpath() { . "$ACCOUNT_CONF_PATH" fi - if [ "$IN_CRON" ]; then + if [ "$ACME_IN_CRON" ]; then if [ ! "$_USER_PATH_EXPORTED" ]; then _USER_PATH_EXPORTED=1 export PATH="$USER_PATH:$PATH" @@ -3216,14 +3216,6 @@ _on_issue_err() { _err "See: $_DEBUG_WIKI" fi - if [ "$IN_CRON" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_ERROR ]; then - if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then - _send_notify "Renew $_main_domain error" "There is an error." "$NOTIFY_HOOK" 1 - fi - fi - fi - #run the post hook if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" @@ -3266,13 +3258,7 @@ _on_issue_success() { _chk_post_hook="$1" _chk_renew_hook="$2" _debug _on_issue_success - if [ "$IN_CRON" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_RENEW ]; then - if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then - _send_notify "Renew $_main_domain success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0 - fi - fi - fi + #run the post hook if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" @@ -4506,7 +4492,7 @@ $_authorizations_map" _info "Your cert key is in $(__green " $CERT_KEY_PATH ")" fi - if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ]; then + if [ ! "$USER_PATH" ] || [ ! "$ACME_IN_CRON" ]; then USER_PATH="$PATH" _saveaccountconf "USER_PATH" "$USER_PATH" fi @@ -4686,19 +4672,10 @@ renew() { if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(_time)" -lt "$Le_NextRenewTime" ]; then _info "Skip, Next renewal time is: $(__green "$Le_NextRenewTimeStr")" _info "Add '$(__red '--force')' to force to renew." - - if [ "$IN_CRON" = "1" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_SKIP ]; then - if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then - _send_notify "Renew $Le_Domain skipped" "Good, the cert next renewal time is $Le_NextRenewTimeStr." "$NOTIFY_HOOK" "$RENEW_SKIP" - fi - fi - fi - return "$RENEW_SKIP" fi - if [ "$IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then + if [ "$ACME_IN_CRON" = "1" ] && [ -z "$Le_CertCreateTime" ]; then _info "Skip invalid cert for: $Le_Domain" return $RENEW_SKIP fi @@ -4733,6 +4710,8 @@ renewAll() { _success_msg="" _error_msg="" _skipped_msg="" + _error_level=$NOTIFY_LEVEL_SKIP + _notify_code=$RENEW_SKIP for di in "${CERT_HOME}"/*.*/; do _debug di "$di" if ! [ -d "$di" ]; then @@ -4750,49 +4729,83 @@ renewAll() { ) rc="$?" _debug "Return code: $rc" - if [ "$rc" != "0" ]; then - if [ "$rc" = "$RENEW_SKIP" ]; then - _info "Skipped $d" - _skipped_msg="${_skipped_msg} $d -" - else - _error_msg="${_error_msg} $d + if [ "$rc" = "0" ]; then + if [ $_error_level -gt $NOTIFY_LEVEL_RENEW ]; then + _error_level="$NOTIFY_LEVEL_RENEW" + _notify_code=0 + fi + if [ "$ACME_IN_CRON" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_RENEW ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0 + fi + fi + fi + _success_msg="${_success_msg} $d " - if [ "$_stopRenewOnError" ]; then - _err "Error renew $d, stop now." - _ret="$rc" - break - else - _ret="$rc" - _err "Error renew $d." + elif [ "$rc" = "$RENEW_SKIP" ]; then + if [ $_error_level -gt $NOTIFY_LEVEL_SKIP ]; then + _error_level="$NOTIFY_LEVEL_SKIP" + _notify_code=$RENEW_SKIP + fi + if [ "$ACME_IN_CRON" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_SKIP ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP" + fi fi fi + _info "Skipped $d" + _skipped_msg="${_skipped_msg} $d +" else - _success_msg="${_success_msg} $d + if [ $_error_level -gt $NOTIFY_LEVEL_ERROR ]; then + _error_level="$NOTIFY_LEVEL_ERROR" + _notify_code=1 + fi + if [ "$ACME_IN_CRON" ]; then + if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_ERROR ]; then + if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then + _send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1 + fi + fi + fi + _error_msg="${_error_msg} $d " + if [ "$_stopRenewOnError" ]; then + _err "Error renew $d, stop now." + _ret="$rc" + break + else + _ret="$rc" + _err "Error renew $d." + fi fi done - - if [ "$IN_CRON" = "1" ]; then + _debug _error_level "$_error_level" + if [ "$ACME_IN_CRON" ] && [ $_error_level -le $NOTIFY_LEVEL ]; then if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then _msg_subject="Renew" if [ "$_error_msg" ]; then _msg_subject="${_msg_subject} Error" + _msg_data="Error certs: +${_error_msg} +" fi if [ "$_success_msg" ]; then _msg_subject="${_msg_subject} Success" + _msg_data="${_msg_data}Success certs: +${_success_msg} +" fi if [ "$_skipped_msg" ]; then _msg_subject="${_msg_subject} Skipped" - fi - _msg_data="Error certs: -${_error_msg} -Success certs: -${_success_msg} -Skipped certs: -$_skipped_msg + _msg_data="${_msg_data}Skipped certs: +${_skipped_msg} " - _send_notify "$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" 0 + fi + + _send_notify "$_msg_subject" "$_msg_data" "$NOTIFY_HOOK" "$_notify_code" fi fi @@ -5708,7 +5721,7 @@ install() { _debug "Skip install cron job" fi - if [ "$IN_CRON" != "1" ]; then + if [ "$ACME_IN_CRON" != "1" ]; then if ! _precheck "$_nocron"; then _err "Pre-check failed, can not install." return 1 @@ -5765,7 +5778,7 @@ install() { _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY" - if [ "$IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then + if [ "$ACME_IN_CRON" != "1" ] && [ -z "$_noprofile" ]; then _installalias "$_c_home" fi @@ -5863,7 +5876,7 @@ _uninstallalias() { } cron() { - export IN_CRON=1 + export ACME_IN_CRON=1 _initpath _info "$(__green "===Starting cron===")" if [ "$AUTO_UPGRADE" = "1" ]; then @@ -5884,7 +5897,7 @@ cron() { fi renewAll _ret="$?" - IN_CRON="" + ACME_IN_CRON="" _info "$(__green "===End cron===")" exit $_ret } @@ -6106,11 +6119,11 @@ Parameters: --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT. 0: disabled, no notification will be sent. - 1: send notification only when there is an error. - 2: send notification when a cert is successfully renewed, or there is an error - 3: send notification when a cert is skipped, renewdd, or error + 1: send notifications only when there is an error. + 2: send notifications when a cert is successfully renewed, or there is an error. + 3: send notifications when a cert is skipped, renewed, or error. --notify-mode 0|1 Set notification mode. Default value is $NOTIFY_MODE_DEFAULT. - 0: Bulk mode. Send all the domain's notifications in one message(mail) + 0: Bulk mode. Send all the domain's notifications in one message(mail). 1: Cert mode. Send a message for every single cert. --notify-hook [hookname] Set the notify hook From f803c6c0bf3988efd9579832cf1f108a21d06316 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 15 Jun 2019 08:33:16 +0800 Subject: [PATCH 044/191] fix https://github.com/Neilpang/acme.sh/issues/2341 --- acme.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index bcc88ef..c9f65ec 100755 --- a/acme.sh +++ b/acme.sh @@ -4712,6 +4712,8 @@ renewAll() { _skipped_msg="" _error_level=$NOTIFY_LEVEL_SKIP _notify_code=$RENEW_SKIP + _set_level=${NOTIFY_LEVEL:-$NOTIFY_LEVEL_DEFAULT} + _debug "_set_level" "$_set_level" for di in "${CERT_HOME}"/*.*/; do _debug di "$di" if ! [ -d "$di" ]; then @@ -4735,7 +4737,7 @@ renewAll() { _notify_code=0 fi if [ "$ACME_IN_CRON" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_RENEW ]; then + if [ $_set_level -ge $NOTIFY_LEVEL_RENEW ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then _send_notify "Renew $d success" "Good, the cert is renewed." "$NOTIFY_HOOK" 0 fi @@ -4749,7 +4751,7 @@ renewAll() { _notify_code=$RENEW_SKIP fi if [ "$ACME_IN_CRON" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_SKIP ]; then + if [ $_set_level -ge $NOTIFY_LEVEL_SKIP ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then _send_notify "Renew $d skipped" "Good, the cert is skipped." "$NOTIFY_HOOK" "$RENEW_SKIP" fi @@ -4764,7 +4766,7 @@ renewAll() { _notify_code=1 fi if [ "$ACME_IN_CRON" ]; then - if [ "$NOTIFY_LEVEL" ] && [ $NOTIFY_LEVEL -ge $NOTIFY_LEVEL_ERROR ]; then + if [ $_set_level -ge $NOTIFY_LEVEL_ERROR ]; then if [ "$NOTIFY_MODE" = "$NOTIFY_MODE_CERT" ]; then _send_notify "Renew $d error" "There is an error." "$NOTIFY_HOOK" 1 fi @@ -4783,7 +4785,7 @@ renewAll() { fi done _debug _error_level "$_error_level" - if [ "$ACME_IN_CRON" ] && [ $_error_level -le $NOTIFY_LEVEL ]; then + if [ "$ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then _msg_subject="Renew" if [ "$_error_msg" ]; then From c83f2f98bd2edde3c602bfdf7cc9f9bfd061d4b8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 19 Jun 2019 21:49:42 +0800 Subject: [PATCH 045/191] fix https://github.com/Neilpang/acme.sh/issues/2300 --- dnsapi/dns_ovh.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ovh.sh b/dnsapi/dns_ovh.sh index 2669cc8..65567ef 100755 --- a/dnsapi/dns_ovh.sh +++ b/dnsapi/dns_ovh.sh @@ -121,7 +121,7 @@ _initAuth() { _info "Checking authentication" - if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL"; then + if ! _ovh_rest GET "domain" || _contains "$response" "INVALID_CREDENTIAL" || _contains "$response" "NOT_CREDENTIAL"; then _err "The consumer key is invalid: $OVH_CK" _err "Please retry to create a new one." _clearaccountconf OVH_CK From a2738e8599918f3e7309237d686e7e1cbd28982d Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 19 Jun 2019 21:50:41 +0800 Subject: [PATCH 046/191] minor, add debug info --- acme.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/acme.sh b/acme.sh index c9f65ec..67fcdcb 100755 --- a/acme.sh +++ b/acme.sh @@ -4785,6 +4785,7 @@ renewAll() { fi done _debug _error_level "$_error_level" + _debug _set_level "$_set_level" if [ "$ACME_IN_CRON" ] && [ $_error_level -le $_set_level ]; then if [ -z "$NOTIFY_MODE" ] || [ "$NOTIFY_MODE" = "$NOTIFY_MODE_BULK" ]; then _msg_subject="Renew" From e0d4115ed7c45dd31f46cf3ff68d1b03e7f545a9 Mon Sep 17 00:00:00 2001 From: Maximilian Hippler Date: Fri, 21 Jun 2019 23:43:32 +0200 Subject: [PATCH 047/191] Finally added oathtool --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f9d7b1b..752692c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.9 +FROM alpine:3.10 RUN apk update -f \ && apk --no-cache add -f \ @@ -8,7 +8,7 @@ RUN apk update -f \ curl \ socat \ tzdata \ - && apk add --no-cache -X http://dl-cdn.alpinelinux.org/alpine/edge/community oath-toolkit-oathtool \ + oath-toolkit-oathtool \ && rm -rf /var/cache/apk/* ENV LE_CONFIG_HOME /acme.sh From 971a85a6f82a72d74ab3bf29e4c80d9b05318b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Berlamont?= Date: Sat, 29 Jun 2019 18:14:34 +0200 Subject: [PATCH 048/191] dns_azure : Multiple domains with same ending bug We have a few domains that ends the same. For example : iperfony.com perfony.com The problem was in the _get_root functions, when getting the domain_id : only the first result "iperfony.com" was returned, because "perfony.com" is contained in the "iperfony.com" string. The correction consist of being strict in the regex, adding a slash (/) so that it will only match on ".*/(perfony.com).*" and not ".*(perfony.com).*". --- dnsapi/dns_azure.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_azure.sh b/dnsapi/dns_azure.sh index ae8aa1c..8b52dee 100644 --- a/dnsapi/dns_azure.sh +++ b/dnsapi/dns_azure.sh @@ -317,7 +317,7 @@ _get_root() { ## Per https://docs.microsoft.com/en-us/azure/azure-subscription-service-limits#dns-limits you are limited to 100 Zone/subscriptions anyways ## _azure_rest GET "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Network/dnszones?\$top=500&api-version=2017-09-01" "" "$accesstoken" - # Find matching domain name is Json response + # Find matching domain name in Json response while true; do h=$(printf "%s" "$domain" | cut -d . -f $i-100) _debug2 "Checking domain: $h" @@ -328,7 +328,7 @@ _get_root() { fi if _contains "$response" "\"name\":\"$h\"" >/dev/null; then - _domain_id=$(echo "$response" | _egrep_o "\\{\"id\":\"[^\"]*$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") + _domain_id=$(echo "$response" | _egrep_o "\\{\"id\":\"[^\"]*\\/$h\"" | head -n 1 | cut -d : -f 2 | tr -d \") if [ "$_domain_id" ]; then if [ "$i" = 1 ]; then #create the record at the domain apex (@) if only the domain name was provided as --domain-alias From d694ee8651a25b585cae76377e8d0e622f170b0b Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 30 Jun 2019 10:48:21 +0800 Subject: [PATCH 049/191] update --- .github/ISSUE_TEMPLATE.md | 4 +++- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f7d4d1d..189155e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,7 @@ \ No newline at end of file From ae66c6f0b484ce5c451ff8c195f382115b3d15a0 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Thu, 11 Jul 2019 15:46:17 -0400 Subject: [PATCH 050/191] Fix bug (in egrep regex) reported by @maks2018 in issue 2305 Fix bug reported by @maks2018 in issue https://github.com/Neilpang/acme.sh/issues/2305 by updating the regex in egrep of the subdomain html page. --- dnsapi/dns_freedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e76e649..ec845f8 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -305,7 +305,7 @@ _freedns_domain_id() { domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ - | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ + | _egrep_o "edit\.php?edit_domain_id=[0-9a-zA-Z]*" \ | cut -d = -f 2)" # The above beauty extracts domain ID from the html page... # strip out all blank space and new lines. Then insert newlines @@ -352,7 +352,7 @@ _freedns_data_id() { data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ - | _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ + | _egrep_o "edit\.php?data_id=[0-9a-zA-Z]*" \ | cut -d = -f 2)" # The above beauty extracts data ID from the html page... # strip out all blank space and new lines. Then insert newlines From 2ce9fb976024373850bb1de1e9ed939a995d3378 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Thu, 11 Jul 2019 18:06:56 -0400 Subject: [PATCH 051/191] Work around bug in _egrep_o() function _egrep_o() function accepts extended regex and on systems that do not have egrep uses sed to emulate egrep. This is failing on the specific regex I was using before my last commit... https://github.com/dkerr64/acme.sh/commit/ae66c6f0b484ce5c451ff8c195f382115b3d15a0 The problem is that I fixed it by passing in non-extended regex which then fails on systems that do have egrep. So I am no longer using _egrep_o. --- dnsapi/dns_freedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index ec845f8..8a48cf7 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -305,7 +305,7 @@ _freedns_domain_id() { domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ - | _egrep_o "edit\.php?edit_domain_id=[0-9a-zA-Z]*" \ + | grep -o "edit\.php?edit_domain_id=[0-9a-zA-Z]*" \ | cut -d = -f 2)" # The above beauty extracts domain ID from the html page... # strip out all blank space and new lines. Then insert newlines @@ -352,7 +352,7 @@ _freedns_data_id() { data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ - | _egrep_o "edit\.php?data_id=[0-9a-zA-Z]*" \ + | grep -o "edit\.php?data_id=[0-9a-zA-Z]*" \ | cut -d = -f 2)" # The above beauty extracts data ID from the html page... # strip out all blank space and new lines. Then insert newlines From bd9af86de152697f3dbc8532ee813dad01566c10 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 13 Jul 2019 17:33:04 +0800 Subject: [PATCH 052/191] support jdcloud.com --- dnsapi/dns_jd.sh | 305 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 dnsapi/dns_jd.sh diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh new file mode 100644 index 0000000..28ac7dc --- /dev/null +++ b/dnsapi/dns_jd.sh @@ -0,0 +1,305 @@ +#!/usr/bin/env sh + +# +#JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje" +#JD_ACCESS_KEY_SECRET="xxxxxxx" +#JD_REGION="cn-north-1" +#JD_PACK_ID=0 + + +_JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey" + +_JD_PROD="clouddnsservice" +_JD_API="jdcloud-api.com" + +_JD_API_VERSION="v1" +_JD_DEFAULT_REGION="cn-north-1" + +_JD_HOST="$_JD_PROD.$_JD_API" + +_JD_PACK_FREE=0 +_JD_PACK_ENTERPRISE=1 +_JD_PACK_PREMIUM=2 + + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_jd_add() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + JD_PACK_ID="${JD_PACK_ID:-$(_readaccountconf_mutable JD_PACK_ID)}" + + if [ -z "$JD_ACCESS_KEY_ID" ] || [ -z "$JD_ACCESS_KEY_SECRET" ]; then + JD_ACCESS_KEY_ID="" + JD_ACCESS_KEY_SECRET="" + _err "You haven't specifed the jdcloud api key id or api key secret yet." + _err "Please create your key and try again. see $(__green $_JD_ACCOUNT)" + return 1 + fi + + _saveaccountconf_mutable JD_ACCESS_KEY_ID "$JD_ACCESS_KEY_ID" + _saveaccountconf_mutable JD_ACCESS_KEY_SECRET "$JD_ACCESS_KEY_SECRET" + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + else + _saveaccountconf_mutable JD_REGION "$JD_REGION" + fi + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + if [ -z "$JD_PACK_ID" ]; then + _debug "Using default free pack: $_JD_PACK_FREE" + JD_PACK_ID=$_JD_PACK_FREE + else + _saveaccountconf_mutable JD_PACK_ID "$JD_PACK_ID" + 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 getViewTree" + + _debug "Adding records" + + _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":120,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + #_addrr='{"req":{"hostRecord":"xx","hostValue":"\"value4\"","jcloudRes":false,"mxPriority":null,"port":null,"ttl":300,"type":"TXT","weight":null,"viewValue":-1},"regionId":"cn-north-1","domainId":"8824"}' + if jd_rest POST "domain/$_domain_id/RRAdd" "" "$_addrr"; then + _rid="$(echo "$response" | tr '{},' '\n' | grep '"id":' | cut -d : -f 2)" + if [ -z "$_rid" ]; then + _err "Can not find record id from the result." + return 1 + fi + _info "TXT record added successfully." + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + if [ "$_srid" ]; then + _rid="$_srid,$_rid" + fi + _savedomainconf "JD_CLOUD_RIDS" "$_rid" + return 0 + fi + + return 1 +} + + +dns_jd_rm() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + JD_PACK_ID="${JD_PACK_ID:-$(_readaccountconf_mutable JD_PACK_ID)}" + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + fi + + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + _info "Getting existing records for $fulldomain" + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + _debug _srid "$_srid" + + if [ -z "$_srid" ]; then + _err "Not rid skip" + return 0 + 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" + + _cleardomainconf JD_CLOUD_RIDS + + _aws_tmpl_xml="{\"ids\":[$_srid],\"action\":\"del\",\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + + if jd_rest POST "domain/$_domain_id/RROperate" "" "$_aws_tmpl_xml" && _contains "$response" "\"code\":\"OK\""; then + _info "TXT record deleted successfully." + return 0 + fi + return 1 + +} + + +#################### Private functions below ################################## + +_get_root() { + domain=$1 + i=1 + p=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" + if ! jd_rest GET "domain"; then + _err "error get domain list" + return 1 + fi + if [ -z "$h" ]; then + #not valid + _err "Invalid domain" + return 1 + fi + + if _contains "$response" "\"domainName\":\"$h\""; then + hostedzone="$(echo "$response" | tr '{}' '\n' | grep "\"domainName\":\"$h\"")" + _debug hostedzone "$hostedzone" + if [ "$hostedzone" ]; then + _domain_id="$(echo "$hostedzone" | tr ',' '\n' | grep "\"id\":" | cut -d : -f 2)" + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + fi + _err "Can't find domain with id: $h" + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + + +#method uri qstr data +jd_rest() { + mtd="$1" + ep="$2" + qsr="$3" + data="$4" + + _debug mtd "$mtd" + _debug ep "$ep" + _debug qsr "$qsr" + _debug data "$data" + + CanonicalURI="/$_JD_BASE_URI/$ep" + _debug2 CanonicalURI "$CanonicalURI" + + CanonicalQueryString="$qsr" + _debug2 CanonicalQueryString "$CanonicalQueryString" + + RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")" + #RequestDate="20190713T082155Z" ###################################################### + _debug2 RequestDate "$RequestDate" + export _H1="X-Jdcloud-Date: $RequestDate" + + RequestNonce="2bd0852a-8bae-4087-b2d5-$(_time)" + #RequestNonce="894baff5-72d4-4244-883a-7b2eb51e7fbe" ################################# + _debug2 RequestNonce "$RequestNonce" + export _H2="X-Jdcloud-Nonce: $RequestNonce" + + if [ "$data" ]; then + CanonicalHeaders="content-type:application/json\n" + SignedHeaders="content-type;" + else + CanonicalHeaders="" + SignedHeaders="" + fi + CanonicalHeaders="${CanonicalHeaders}host:$_JD_HOST\nx-jdcloud-date:$RequestDate\nx-jdcloud-nonce:$RequestNonce\n" + SignedHeaders="${SignedHeaders}host;x-jdcloud-date;x-jdcloud-nonce" + + _debug2 CanonicalHeaders "$CanonicalHeaders" + _debug2 SignedHeaders "$SignedHeaders" + + Hash="sha256" + + RequestPayload="$data" + _debug2 RequestPayload "$RequestPayload" + + RequestPayloadHash="$(printf "%s" "$RequestPayload" | _digest "$Hash" hex | _lower_case)" + _debug2 RequestPayloadHash "$RequestPayloadHash" + + CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$RequestPayloadHash" + _debug2 CanonicalRequest "$CanonicalRequest" + + HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)" + _debug2 HashedCanonicalRequest "$HashedCanonicalRequest" + + Algorithm="JDCLOUD2-HMAC-SHA256" + _debug2 Algorithm "$Algorithm" + + RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)" + _debug2 RequestDateOnly "$RequestDateOnly" + + Region="$JD_REGION" + Service="$_JD_PROD" + + CredentialScope="$RequestDateOnly/$Region/$Service/jdcloud2_request" + _debug2 CredentialScope "$CredentialScope" + + StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest" + + _debug2 StringToSign "$StringToSign" + + kSecret="JDCLOUD2$JD_ACCESS_KEY_SECRET" + + _secure_debug2 kSecret "$kSecret" + + kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")" + _secure_debug2 kSecretH "$kSecretH" + + kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" + _debug2 kDateH "$kDateH" + + kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)" + _debug2 kRegionH "$kRegionH" + + kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" + _debug2 kServiceH "$kServiceH" + + kSigningH="$(printf "%s" "jdcloud2_request" | _hmac "$Hash" "$kServiceH" hex)" + _debug2 kSigningH "$kSigningH" + + signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" + _debug2 signature "$signature" + + Authorization="$Algorithm Credential=$JD_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" + _debug2 Authorization "$Authorization" + + _H3="Authorization: $Authorization" + _debug _H3 "$_H3" + + url="https://$_JD_HOST$CanonicalURI" + if [ "$qsr" ]; then + url="https://$_JD_HOST$CanonicalURI?$qsr" + fi + + if [ "$mtd" = "GET" ]; then + response="$(_get "$url")" + else + response="$(_post "$data" "$url" "" "$mtd" "application/json")" + fi + + _ret="$?" + _debug2 response "$response" + if [ "$_ret" = "0" ]; then + if _contains "$response" "\"error\""; then + _err "Response error:$response" + return 1 + fi + fi + + return "$_ret" +} + From 57b16e3ac2f18401cecceef1ca32d818c3b11bf0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 13 Jul 2019 17:42:01 +0800 Subject: [PATCH 053/191] fix format --- dnsapi/dns_jd.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh index 28ac7dc..2efc91a 100644 --- a/dnsapi/dns_jd.sh +++ b/dnsapi/dns_jd.sh @@ -6,7 +6,6 @@ #JD_REGION="cn-north-1" #JD_PACK_ID=0 - _JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey" _JD_PROD="clouddnsservice" @@ -21,7 +20,6 @@ _JD_PACK_FREE=0 _JD_PACK_ENTERPRISE=1 _JD_PACK_PREMIUM=2 - ######## Public functions ##################### #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" @@ -92,7 +90,6 @@ dns_jd_add() { return 1 } - dns_jd_rm() { fulldomain=$1 txtvalue=$2 @@ -138,7 +135,6 @@ dns_jd_rm() { } - #################### Private functions below ################################## _get_root() { @@ -180,7 +176,6 @@ _get_root() { return 1 } - #method uri qstr data jd_rest() { mtd="$1" @@ -302,4 +297,3 @@ jd_rest() { return "$_ret" } - From 42497028c458053f9fba1cb9fe43e3c03a3701b4 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 13 Jul 2019 19:35:55 +0800 Subject: [PATCH 054/191] ttl 3000 --- dnsapi/dns_jd.sh | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh index 2efc91a..d0f2a50 100644 --- a/dnsapi/dns_jd.sh +++ b/dnsapi/dns_jd.sh @@ -4,7 +4,6 @@ #JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje" #JD_ACCESS_KEY_SECRET="xxxxxxx" #JD_REGION="cn-north-1" -#JD_PACK_ID=0 _JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey" @@ -16,10 +15,6 @@ _JD_DEFAULT_REGION="cn-north-1" _JD_HOST="$_JD_PROD.$_JD_API" -_JD_PACK_FREE=0 -_JD_PACK_ENTERPRISE=1 -_JD_PACK_PREMIUM=2 - ######## Public functions ##################### #Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" @@ -30,7 +25,6 @@ dns_jd_add() { JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" - JD_PACK_ID="${JD_PACK_ID:-$(_readaccountconf_mutable JD_PACK_ID)}" if [ -z "$JD_ACCESS_KEY_ID" ] || [ -z "$JD_ACCESS_KEY_SECRET" ]; then JD_ACCESS_KEY_ID="" @@ -50,13 +44,6 @@ dns_jd_add() { fi _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" - if [ -z "$JD_PACK_ID" ]; then - _debug "Using default free pack: $_JD_PACK_FREE" - JD_PACK_ID=$_JD_PACK_FREE - else - _saveaccountconf_mutable JD_PACK_ID "$JD_PACK_ID" - fi - _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -70,7 +57,7 @@ dns_jd_add() { _debug "Adding records" - _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":120,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":300,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" #_addrr='{"req":{"hostRecord":"xx","hostValue":"\"value4\"","jcloudRes":false,"mxPriority":null,"port":null,"ttl":300,"type":"TXT","weight":null,"viewValue":-1},"regionId":"cn-north-1","domainId":"8824"}' if jd_rest POST "domain/$_domain_id/RRAdd" "" "$_addrr"; then _rid="$(echo "$response" | tr '{},' '\n' | grep '"id":' | cut -d : -f 2)" @@ -97,7 +84,7 @@ dns_jd_rm() { JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" - JD_PACK_ID="${JD_PACK_ID:-$(_readaccountconf_mutable JD_PACK_ID)}" + if [ -z "$JD_REGION" ]; then _debug "Using default region: $_JD_DEFAULT_REGION" JD_REGION="$_JD_DEFAULT_REGION" From 5f944743304976a55c1e974c5835c5b2996c2ce2 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sat, 13 Jul 2019 20:01:06 +0800 Subject: [PATCH 055/191] support jdcloud.com (#2390) fix https://github.com/Neilpang/acme.sh/pull/2390 --- dnsapi/dns_jd.sh | 286 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 dnsapi/dns_jd.sh diff --git a/dnsapi/dns_jd.sh b/dnsapi/dns_jd.sh new file mode 100644 index 0000000..d0f2a50 --- /dev/null +++ b/dnsapi/dns_jd.sh @@ -0,0 +1,286 @@ +#!/usr/bin/env sh + +# +#JD_ACCESS_KEY_ID="sdfsdfsdfljlbjkljlkjsdfoiwje" +#JD_ACCESS_KEY_SECRET="xxxxxxx" +#JD_REGION="cn-north-1" + +_JD_ACCOUNT="https://uc.jdcloud.com/account/accesskey" + +_JD_PROD="clouddnsservice" +_JD_API="jdcloud-api.com" + +_JD_API_VERSION="v1" +_JD_DEFAULT_REGION="cn-north-1" + +_JD_HOST="$_JD_PROD.$_JD_API" + +######## Public functions ##################### + +#Usage: dns_myapi_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_jd_add() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + + if [ -z "$JD_ACCESS_KEY_ID" ] || [ -z "$JD_ACCESS_KEY_SECRET" ]; then + JD_ACCESS_KEY_ID="" + JD_ACCESS_KEY_SECRET="" + _err "You haven't specifed the jdcloud api key id or api key secret yet." + _err "Please create your key and try again. see $(__green $_JD_ACCOUNT)" + return 1 + fi + + _saveaccountconf_mutable JD_ACCESS_KEY_ID "$JD_ACCESS_KEY_ID" + _saveaccountconf_mutable JD_ACCESS_KEY_SECRET "$JD_ACCESS_KEY_SECRET" + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + else + _saveaccountconf_mutable JD_REGION "$JD_REGION" + fi + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + _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 getViewTree" + + _debug "Adding records" + + _addrr="{\"req\":{\"hostRecord\":\"$_sub_domain\",\"hostValue\":\"$txtvalue\",\"ttl\":300,\"type\":\"TXT\",\"viewValue\":-1},\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + #_addrr='{"req":{"hostRecord":"xx","hostValue":"\"value4\"","jcloudRes":false,"mxPriority":null,"port":null,"ttl":300,"type":"TXT","weight":null,"viewValue":-1},"regionId":"cn-north-1","domainId":"8824"}' + if jd_rest POST "domain/$_domain_id/RRAdd" "" "$_addrr"; then + _rid="$(echo "$response" | tr '{},' '\n' | grep '"id":' | cut -d : -f 2)" + if [ -z "$_rid" ]; then + _err "Can not find record id from the result." + return 1 + fi + _info "TXT record added successfully." + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + if [ "$_srid" ]; then + _rid="$_srid,$_rid" + fi + _savedomainconf "JD_CLOUD_RIDS" "$_rid" + return 0 + fi + + return 1 +} + +dns_jd_rm() { + fulldomain=$1 + txtvalue=$2 + + JD_ACCESS_KEY_ID="${JD_ACCESS_KEY_ID:-$(_readaccountconf_mutable JD_ACCESS_KEY_ID)}" + JD_ACCESS_KEY_SECRET="${JD_ACCESS_KEY_SECRET:-$(_readaccountconf_mutable JD_ACCESS_KEY_SECRET)}" + JD_REGION="${JD_REGION:-$(_readaccountconf_mutable JD_REGION)}" + + if [ -z "$JD_REGION" ]; then + _debug "Using default region: $_JD_DEFAULT_REGION" + JD_REGION="$_JD_DEFAULT_REGION" + fi + + _JD_BASE_URI="$_JD_API_VERSION/regions/$JD_REGION" + + _info "Getting existing records for $fulldomain" + _srid="$(_readdomainconf "JD_CLOUD_RIDS")" + _debug _srid "$_srid" + + if [ -z "$_srid" ]; then + _err "Not rid skip" + return 0 + 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" + + _cleardomainconf JD_CLOUD_RIDS + + _aws_tmpl_xml="{\"ids\":[$_srid],\"action\":\"del\",\"regionId\":\"$JD_REGION\",\"domainId\":\"$_domain_id\"}" + + if jd_rest POST "domain/$_domain_id/RROperate" "" "$_aws_tmpl_xml" && _contains "$response" "\"code\":\"OK\""; then + _info "TXT record deleted successfully." + return 0 + fi + return 1 + +} + +#################### Private functions below ################################## + +_get_root() { + domain=$1 + i=1 + p=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug2 "Checking domain: $h" + if ! jd_rest GET "domain"; then + _err "error get domain list" + return 1 + fi + if [ -z "$h" ]; then + #not valid + _err "Invalid domain" + return 1 + fi + + if _contains "$response" "\"domainName\":\"$h\""; then + hostedzone="$(echo "$response" | tr '{}' '\n' | grep "\"domainName\":\"$h\"")" + _debug hostedzone "$hostedzone" + if [ "$hostedzone" ]; then + _domain_id="$(echo "$hostedzone" | tr ',' '\n' | grep "\"id\":" | cut -d : -f 2)" + if [ "$_domain_id" ]; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + return 0 + fi + fi + _err "Can't find domain with id: $h" + return 1 + fi + p=$i + i=$(_math "$i" + 1) + done + + return 1 +} + +#method uri qstr data +jd_rest() { + mtd="$1" + ep="$2" + qsr="$3" + data="$4" + + _debug mtd "$mtd" + _debug ep "$ep" + _debug qsr "$qsr" + _debug data "$data" + + CanonicalURI="/$_JD_BASE_URI/$ep" + _debug2 CanonicalURI "$CanonicalURI" + + CanonicalQueryString="$qsr" + _debug2 CanonicalQueryString "$CanonicalQueryString" + + RequestDate="$(date -u +"%Y%m%dT%H%M%SZ")" + #RequestDate="20190713T082155Z" ###################################################### + _debug2 RequestDate "$RequestDate" + export _H1="X-Jdcloud-Date: $RequestDate" + + RequestNonce="2bd0852a-8bae-4087-b2d5-$(_time)" + #RequestNonce="894baff5-72d4-4244-883a-7b2eb51e7fbe" ################################# + _debug2 RequestNonce "$RequestNonce" + export _H2="X-Jdcloud-Nonce: $RequestNonce" + + if [ "$data" ]; then + CanonicalHeaders="content-type:application/json\n" + SignedHeaders="content-type;" + else + CanonicalHeaders="" + SignedHeaders="" + fi + CanonicalHeaders="${CanonicalHeaders}host:$_JD_HOST\nx-jdcloud-date:$RequestDate\nx-jdcloud-nonce:$RequestNonce\n" + SignedHeaders="${SignedHeaders}host;x-jdcloud-date;x-jdcloud-nonce" + + _debug2 CanonicalHeaders "$CanonicalHeaders" + _debug2 SignedHeaders "$SignedHeaders" + + Hash="sha256" + + RequestPayload="$data" + _debug2 RequestPayload "$RequestPayload" + + RequestPayloadHash="$(printf "%s" "$RequestPayload" | _digest "$Hash" hex | _lower_case)" + _debug2 RequestPayloadHash "$RequestPayloadHash" + + CanonicalRequest="$mtd\n$CanonicalURI\n$CanonicalQueryString\n$CanonicalHeaders\n$SignedHeaders\n$RequestPayloadHash" + _debug2 CanonicalRequest "$CanonicalRequest" + + HashedCanonicalRequest="$(printf "$CanonicalRequest%s" | _digest "$Hash" hex)" + _debug2 HashedCanonicalRequest "$HashedCanonicalRequest" + + Algorithm="JDCLOUD2-HMAC-SHA256" + _debug2 Algorithm "$Algorithm" + + RequestDateOnly="$(echo "$RequestDate" | cut -c 1-8)" + _debug2 RequestDateOnly "$RequestDateOnly" + + Region="$JD_REGION" + Service="$_JD_PROD" + + CredentialScope="$RequestDateOnly/$Region/$Service/jdcloud2_request" + _debug2 CredentialScope "$CredentialScope" + + StringToSign="$Algorithm\n$RequestDate\n$CredentialScope\n$HashedCanonicalRequest" + + _debug2 StringToSign "$StringToSign" + + kSecret="JDCLOUD2$JD_ACCESS_KEY_SECRET" + + _secure_debug2 kSecret "$kSecret" + + kSecretH="$(printf "%s" "$kSecret" | _hex_dump | tr -d " ")" + _secure_debug2 kSecretH "$kSecretH" + + kDateH="$(printf "$RequestDateOnly%s" | _hmac "$Hash" "$kSecretH" hex)" + _debug2 kDateH "$kDateH" + + kRegionH="$(printf "$Region%s" | _hmac "$Hash" "$kDateH" hex)" + _debug2 kRegionH "$kRegionH" + + kServiceH="$(printf "$Service%s" | _hmac "$Hash" "$kRegionH" hex)" + _debug2 kServiceH "$kServiceH" + + kSigningH="$(printf "%s" "jdcloud2_request" | _hmac "$Hash" "$kServiceH" hex)" + _debug2 kSigningH "$kSigningH" + + signature="$(printf "$StringToSign%s" | _hmac "$Hash" "$kSigningH" hex)" + _debug2 signature "$signature" + + Authorization="$Algorithm Credential=$JD_ACCESS_KEY_ID/$CredentialScope, SignedHeaders=$SignedHeaders, Signature=$signature" + _debug2 Authorization "$Authorization" + + _H3="Authorization: $Authorization" + _debug _H3 "$_H3" + + url="https://$_JD_HOST$CanonicalURI" + if [ "$qsr" ]; then + url="https://$_JD_HOST$CanonicalURI?$qsr" + fi + + if [ "$mtd" = "GET" ]; then + response="$(_get "$url")" + else + response="$(_post "$data" "$url" "" "$mtd" "application/json")" + fi + + _ret="$?" + _debug2 response "$response" + if [ "$_ret" = "0" ]; then + if _contains "$response" "\"error\""; then + _err "Response error:$response" + return 1 + fi + fi + + return "$_ret" +} From 28a9df669d80e696677ef5ca0248e2801a7bfc0e Mon Sep 17 00:00:00 2001 From: Honza Hommer Date: Sat, 13 Jul 2019 14:35:09 +0200 Subject: [PATCH 056/191] Escape slashes (#2375) --- dnsapi/dns_namecheap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 6553deb..a82e12d 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -164,7 +164,7 @@ _namecheap_set_publicip() { _debug sourceip "$NAMECHEAP_SOURCEIP" ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*') + addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https):\/\/.*') _debug2 ip "$ip" _debug2 addr "$addr" From 3cdfa4051d98a0bbc1dab64d8028556324b1e0a4 Mon Sep 17 00:00:00 2001 From: Jeff Wang <3102114+wangqiliang@users.noreply.github.com> Date: Sat, 13 Jul 2019 23:05:30 +0800 Subject: [PATCH 057/191] Change 1.1.1.1 to 1.0.0.1 to probe compatibility (#2330) As we can see, 1.1.1.1 is not routed or routed to an Intranet devices due to historical reason. Change 1.1.1.1 to 1.0.0.1 will have a better compatibility. I found this problem on my Tencent Cloud server. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 67fcdcb..d168b56 100755 --- a/acme.sh +++ b/acme.sh @@ -3621,7 +3621,7 @@ _ns_purge_cf() { _cf_d="$1" _cf_d_type="$2" _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" - _cf_purl="https://1.1.1.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" + _cf_purl="https://1.0.0.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" response="$(_post "" "$_cf_purl")" _debug2 response "$response" } From 28cadc5e06f9d79ba315636e240514c197656bf6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 18 Jul 2019 21:05:59 +0800 Subject: [PATCH 058/191] check empty id --- dnsapi/dns_namesilo.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh index dc1a4fd..15e4f21 100755 --- a/dnsapi/dns_namesilo.sh +++ b/dnsapi/dns_namesilo.sh @@ -59,9 +59,14 @@ dns_namesilo_rm() { 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) + _record_id=$(echo "$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." + if [ "$record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi else _err "Unable to retrieve the record id." return 1 From 5c09788ec4cacc85a426f3444436b5b374976cff Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 18 Jul 2019 22:20:51 +0800 Subject: [PATCH 059/191] fix error --- dnsapi/dns_namesilo.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh index 15e4f21..ed6d0e0 100755 --- a/dnsapi/dns_namesilo.sh +++ b/dnsapi/dns_namesilo.sh @@ -60,8 +60,8 @@ dns_namesilo_rm() { retcode=$(printf "%s\n" "$response" | _egrep_o "300") if [ "$retcode" ]; then _record_id=$(echo "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1) - _debug record_id "$_record_id" - if [ "$record_id" ]; then + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then _info "Successfully retrieved the record id for ACME challenge." else _info "Empty record id, it seems no such record." From 8d393ff13722bf7acfac84879363b8d50a04c7b7 Mon Sep 17 00:00:00 2001 From: Yuri S Date: Sat, 20 Jul 2019 09:26:23 +0500 Subject: [PATCH 060/191] Add dnsapi for Vultr (#2370) * Add Vultr dns api --- dnsapi/dns_vultr.sh | 163 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 dnsapi/dns_vultr.sh diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh new file mode 100644 index 0000000..f15e7c4 --- /dev/null +++ b/dnsapi/dns_vultr.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env sh + +# +#VULTR_API_KEY=000011112222333344445555666677778888 + +VULTR_Api="https://api.vultr.com/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_vultr_add() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY='' + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + if ! _vultr_rest POST 'dns/create_record' "domain=$_domain&name=$_sub_domain&data=\"$txtvalue\"&type=TXT"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#fulldomain txtvalue +dns_vultr_rm() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY="" + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep "$txtvalue" | tr ',' '\n' | grep -i 'RECORDID' | cut -d : -f 2)" + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi + + if ! _vultr_rest POST 'dns/delete_record' "domain=$_domain&RECORDID=$_record_id"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + return 1 + fi + + if ! _vultr_rest GET "dns/list"; then + return 1 + fi + + if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then + if _contains "$response" "\"domain\":\"$_domain\""; then + _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" + _domain=$_domain + return 0 + else + _err 'Invalid domain' + return 1 + fi + else + _err "$response" + return 1 + fi + i=$(_math "$i" + 1) + done + + return 1 +} + +_vultr_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"') + + export _H1="Api-Key: $api_key_trimmed" + export _H2='Content-Type: application/x-www-form-urlencoded' + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$VULTR_Api/$ep" "" "$m")" + else + response="$(_get "$VULTR_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "Error $ep" + return 1 + fi + + _debug2 response "$response" + return 0 +} From b8e6287774b64ca42de3b301df7d5b57a3e216af Mon Sep 17 00:00:00 2001 From: tdk1069 Date: Sat, 20 Jul 2019 05:30:56 +0100 Subject: [PATCH 061/191] PushOver notifications (#2325) * PushOver notifications, using AppToken, UserKey, and optional sounds --- notify/pushover.sh | 67 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 notify/pushover.sh diff --git a/notify/pushover.sh b/notify/pushover.sh new file mode 100644 index 0000000..70eba73 --- /dev/null +++ b/notify/pushover.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env sh + +#Support for pushover.net's api. Push notification platform for multiple platforms +#PUSHOVER_TOKEN="" Required, pushover application token +#PUSHOVER_USER="" Required, pushover userkey +#PUSHOVER_DEVICE="" Optional, Specific device or devices by hostnames, joining multiples with a comma (such as device=iphone,nexus5) +#PUSHOVER_PRIORITY="" Optional, Lowest Priority (-2), Low Priority (-1), Normal Priority (0), High Priority (1) + +PUSHOVER_URI="https://api.pushover.net/1/messages.json" + +pushover_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + PUSHOVER_TOKEN="${PUSHOVER_TOKEN:-$(_readaccountconf_mutable PUSHOVER_TOKEN)}" + if [ -z "$PUSHOVER_TOKEN" ]; then + PUSHOVER_TOKEN="" + _err "You didn't specify a PushOver application token yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_TOKEN "$PUSHOVER_TOKEN" + + PUSHOVER_USER="${PUSHOVER_USER:-$(_readaccountconf_mutable PUSHOVER_USER)}" + if [ -z "$PUSHOVER_USER" ]; then + PUSHOVER_USER="" + _err "You didn't specify a PushOver UserKey yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_USER "$PUSHOVER_USER" + + PUSHOVER_DEVICE="${PUSHOVER_DEVICE:-$(_readaccountconf_mutable PUSHOVER_DEVICE)}" + if [ -z "$PUSHOVER_DEVICE" ]; then + PUSHOVER_DEVICE="" + fi + _saveaccountconf_mutable PUSHOVER_DEVICE "$PUSHOVER_DEVICE" + + PUSHOVER_PRIORITY="${PUSHOVER_PRIORITY:-$(_readaccountconf_mutable PUSHOVER_PRIORITY)}" + if [ -z "$PUSHOVER_PRIORITY" ]; then + PUSHOVER_PRIORITY="0" + fi + _saveaccountconf_mutable PUSHOVER_PRIORITY "$PUSHOVER_PRIORITY" + + + PUSHOVER_SOUND="${PUSHOVER_SOUND:-$(_readaccountconf_mutable PUSHOVER_SOUND)}" + if [ -z "$PUSHOVER_SOUND" ]; then + PUSHOVER_SOUND="" # Play default if not specified. + fi + _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" + + export _H1="Content-Type: application/json" + _content="$(printf "*%s*\n" "$_content" | _json_encode)" + _subject="$(printf "*%s*\n" "$_subject" | _json_encode)" + _data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}" + + response="" #just make shellcheck happy + if _post "$_data" "$PUSHOVER_URI"; then + if _contains "$response" "{\"status\":1"; then + _info "PUSHOVER send sccess." + return 0 + fi + fi + _err "PUSHOVER send error." + _err "$response" + return 1 +} From 28c153a0a2dd667f2eb2e810d4f2a3cea4471506 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 20 Jul 2019 12:36:28 +0800 Subject: [PATCH 062/191] fix errors --- notify/pushover.sh | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/notify/pushover.sh b/notify/pushover.sh index 70eba73..309b390 100644 --- a/notify/pushover.sh +++ b/notify/pushover.sh @@ -31,23 +31,20 @@ pushover_send() { _saveaccountconf_mutable PUSHOVER_USER "$PUSHOVER_USER" PUSHOVER_DEVICE="${PUSHOVER_DEVICE:-$(_readaccountconf_mutable PUSHOVER_DEVICE)}" - if [ -z "$PUSHOVER_DEVICE" ]; then - PUSHOVER_DEVICE="" + if [ "$PUSHOVER_DEVICE" ]; then + _saveaccountconf_mutable PUSHOVER_DEVICE "$PUSHOVER_DEVICE" fi - _saveaccountconf_mutable PUSHOVER_DEVICE "$PUSHOVER_DEVICE" PUSHOVER_PRIORITY="${PUSHOVER_PRIORITY:-$(_readaccountconf_mutable PUSHOVER_PRIORITY)}" - if [ -z "$PUSHOVER_PRIORITY" ]; then - PUSHOVER_PRIORITY="0" + if [ "$PUSHOVER_PRIORITY" ]; then + _saveaccountconf_mutable PUSHOVER_PRIORITY "$PUSHOVER_PRIORITY" fi - _saveaccountconf_mutable PUSHOVER_PRIORITY "$PUSHOVER_PRIORITY" - PUSHOVER_SOUND="${PUSHOVER_SOUND:-$(_readaccountconf_mutable PUSHOVER_SOUND)}" - if [ -z "$PUSHOVER_SOUND" ]; then + if [ "$PUSHOVER_SOUND" ]; then PUSHOVER_SOUND="" # Play default if not specified. + _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" fi - _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" export _H1="Content-Type: application/json" _content="$(printf "*%s*\n" "$_content" | _json_encode)" From ccc2142b452a5b86e483fc5d80ff658f91d14146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szil=C3=A1rd=20Pfeiffer?= Date: Sat, 20 Jul 2019 10:00:38 +0200 Subject: [PATCH 063/191] added dns api support for hexonet (#1776) --- dnsapi/dns_hexonet.sh | 156 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100755 dnsapi/dns_hexonet.sh diff --git a/dnsapi/dns_hexonet.sh b/dnsapi/dns_hexonet.sh new file mode 100755 index 0000000..ccd201e --- /dev/null +++ b/dnsapi/dns_hexonet.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env sh + +# +# Hexonet_Username="username" +# +# Hexonet_Password="password" + +Hexonet_Api="https://coreapi.1api.net/api/call.cgi" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_hexonet_add() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Username="${Hexonet_Username:-$(_readaccountconf_mutable Hexonet_Username)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Username" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Username="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Username and Hexonet_Password" + return 1 + fi + + if ! _contains "$Hexonet_Username" "!"; then + _err "It seems that the Hexonet_Username=$Hexonet_Username is not a restrivteed user." + _err "Please check and retry." + return 1 + fi + + #save the username and password to the account conf file. + _saveaccountconf_mutable Hexonet_Username "$Hexonet_Username" + _saveaccountconf_mutable Hexonet_Password "$Hexonet_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" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + _info "Adding record" + if _hexonet_rest "command=UpdateDNSZone&dnszone=${_domain}.&addrr0=${_sub_domain}%20IN%20TXT%20${txtvalue}"; then + if _contains "$response" "CODE=200"; then + _info "Added, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + return 1 + +} + +#fulldomain txtvalue +dns_hexonet_rm() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Username="${Hexonet_Username:-$(_readaccountconf_mutable Hexonet_Username)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Username" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Username="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Username and Hexonet_Password" + 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" + + _debug "Getting txt records" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT&RR=${txtvalue}" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "PROPERTY[TOTAL][0]=" | cut -d = -f 2) + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Don't need to remove." + else + if ! _hexonet_rest "&command=UpdateDNSZone&dnszone=${_domain}.&delrr0='${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""; then + _err "Delete record error." + return 1 + fi + _contains "$response" "CODE=200" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=1 + 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 ! _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}."; then + return 1 + fi + + if _contains "$response" "CODE=200"; 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 +} + +_hexonet_rest() { + query_params="$1" + _debug "$query_params" + + response="$(_get "${Hexonet_Api}?s_login=${Hexonet_Username}&s_pw=${Hexonet_Password}&${query_params}")" + + if [ "$?" != "0" ]; then + _err "error $query_params" + return 1 + fi + _debug2 response "$response" + return 0 +} From 93d29a9733169437cbc5df4a65a8636edbde2c8e Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 20 Jul 2019 17:09:36 +0800 Subject: [PATCH 064/191] update --- dnsapi/dns_hexonet.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/dnsapi/dns_hexonet.sh b/dnsapi/dns_hexonet.sh index ccd201e..f150311 100755 --- a/dnsapi/dns_hexonet.sh +++ b/dnsapi/dns_hexonet.sh @@ -1,9 +1,9 @@ #!/usr/bin/env sh # -# Hexonet_Username="username" +# Hexonet_Login="username!roleId" # -# Hexonet_Password="password" +# Hexonet_Password="rolePassword" Hexonet_Api="https://coreapi.1api.net/api/call.cgi" @@ -14,23 +14,23 @@ dns_hexonet_add() { fulldomain=$1 txtvalue=$2 - Hexonet_Username="${Hexonet_Username:-$(_readaccountconf_mutable Hexonet_Username)}" + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" - if [ -z "$Hexonet_Username" ] || [ -z "$Hexonet_Password" ]; then - Hexonet_Username="" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" Hexonet_Password="" - _err "You must export variables: Hexonet_Username and Hexonet_Password" + _err "You must export variables: Hexonet_Login and Hexonet_Password" return 1 fi - if ! _contains "$Hexonet_Username" "!"; then - _err "It seems that the Hexonet_Username=$Hexonet_Username is not a restrivteed user." + if ! _contains "$Hexonet_Login" "!"; then + _err "It seems that the Hexonet_Login=$Hexonet_Login is not a restrivteed user." _err "Please check and retry." return 1 fi #save the username and password to the account conf file. - _saveaccountconf_mutable Hexonet_Username "$Hexonet_Username" + _saveaccountconf_mutable Hexonet_Login "$Hexonet_Login" _saveaccountconf_mutable Hexonet_Password "$Hexonet_Password" _debug "First detect the root zone" @@ -69,12 +69,12 @@ dns_hexonet_rm() { fulldomain=$1 txtvalue=$2 - Hexonet_Username="${Hexonet_Username:-$(_readaccountconf_mutable Hexonet_Username)}" + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" - if [ -z "$Hexonet_Username" ] || [ -z "$Hexonet_Password" ]; then - Hexonet_Username="" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" Hexonet_Password="" - _err "You must export variables: Hexonet_Username and Hexonet_Password" + _err "You must export variables: Hexonet_Login and Hexonet_Password" return 1 fi @@ -145,7 +145,7 @@ _hexonet_rest() { query_params="$1" _debug "$query_params" - response="$(_get "${Hexonet_Api}?s_login=${Hexonet_Username}&s_pw=${Hexonet_Password}&${query_params}")" + response="$(_get "${Hexonet_Api}?s_login=${Hexonet_Login}&s_pw=${Hexonet_Password}&${query_params}")" if [ "$?" != "0" ]; then _err "error $query_params" From 80af3d6ada23098bb68e3be63e72e0cfd4406ce1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 22 Jul 2019 21:26:47 +0800 Subject: [PATCH 065/191] minor --- notify/pushover.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notify/pushover.sh b/notify/pushover.sh index 309b390..a07cbd3 100644 --- a/notify/pushover.sh +++ b/notify/pushover.sh @@ -42,7 +42,6 @@ pushover_send() { PUSHOVER_SOUND="${PUSHOVER_SOUND:-$(_readaccountconf_mutable PUSHOVER_SOUND)}" if [ "$PUSHOVER_SOUND" ]; then - PUSHOVER_SOUND="" # Play default if not specified. _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" fi @@ -54,7 +53,7 @@ pushover_send() { response="" #just make shellcheck happy if _post "$_data" "$PUSHOVER_URI"; then if _contains "$response" "{\"status\":1"; then - _info "PUSHOVER send sccess." + _info "PUSHOVER send success." return 0 fi fi From c25947d5447ac03ac0fae56b338e8821d49d61ac Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 22 Jul 2019 22:25:50 +0800 Subject: [PATCH 066/191] support new Cloudflare Token format fix https://github.com/Neilpang/acme.sh/issues/2398 --- dnsapi/dns_cf.sh | 83 +++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index cd93189..d4266d1 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -5,6 +5,9 @@ # #CF_Email="xxxx@sss.com" +#CF_Token="xxxx" +#CF_Account_ID="xxxx" + CF_Api="https://api.cloudflare.com/client/v4" ######## Public functions ##################### @@ -14,26 +17,33 @@ dns_cf_add() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi + + if [ "$CF_Token" ]; then + _saveaccountconf_mutable CF_Token "$CF_Token" + _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID" + else + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then + CF_Key="" + CF_Email="" + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." + return 1 + fi - if ! _contains "$CF_Email" "@"; then - _err "It seems that the CF_Email=$CF_Email is not a valid email address." - _err "Please check and retry." - return 1 + if ! _contains "$CF_Email" "@"; then + _err "It seems that the CF_Email=$CF_Email is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _saveaccountconf_mutable CF_Key "$CF_Key" + _saveaccountconf_mutable CF_Email "$CF_Email" fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable CF_Key "$CF_Key" - _saveaccountconf_mutable CF_Email "$CF_Email" - _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" @@ -71,19 +81,6 @@ dns_cf_add() { fi _err "Add txt record error." return 1 - # else - # _info "Updating record" - # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - # _debug "record_id" "$record_id" - # - # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - # if [ "$?" = "0" ]; then - # _info "Updated, OK" - # return 0 - # fi - # _err "Update error" - # return 1 - # fi } @@ -92,15 +89,10 @@ dns_cf_rm() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -157,8 +149,14 @@ _get_root() { return 1 fi - if ! _cf_rest GET "zones?name=$h"; then - return 1 + if [ "$CF_Account_ID" ]; then + if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then + return 1 + fi + else + if ! _cf_rest GET "zones?name=$h"; then + return 1 + fi fi if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then @@ -184,10 +182,15 @@ _cf_rest() { email_trimmed=$(echo $CF_Email | tr -d '"') key_trimmed=$(echo $CF_Key | tr -d '"') + token_trimmed=$(echo $CF_Token | tr -d '"') - export _H1="X-Auth-Email: $email_trimmed" - export _H2="X-Auth-Key: $key_trimmed" - export _H3="Content-Type: application/json" + export _H1="Content-Type: application/json" + if [ "$token_trimmed" ]; then + export _H2="Authorization: Bearer $token_trimmed" + else + export _H2="X-Auth-Email: $email_trimmed" + export _H3="X-Auth-Key: $key_trimmed" + fi if [ "$m" != "GET" ]; then _debug data "$data" From 54e189616c0e96288a42e5fb24ae7fa2846e16d0 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 23 Jul 2019 21:36:42 +0800 Subject: [PATCH 067/191] fix wildcard domain name --- deploy/docker.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/docker.sh b/deploy/docker.sh index 4e55099..44bb044 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -223,7 +223,8 @@ _docker_cp() { _debug2 "_frompath" "$_frompath" _toname="$(basename "$_to")" _debug2 "_toname" "$_toname" - if ! tar --transform="s,$_frompath,$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then + _debug2 "_from" "$_from" + if ! tar --transform="s,$(printf "%s" "$_frompath" | tr '*' .),$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then _err "copy error" return 1 fi From 45e8bb03e438892d974153b5326b06aece628a56 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 23 Jul 2019 21:43:00 +0800 Subject: [PATCH 068/191] add more info --- deploy/docker.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/docker.sh b/deploy/docker.sh index 44bb044..05333b3 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -126,6 +126,7 @@ docker_deploy() { fi if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then + _info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then return 1 fi From 9a733a57e73fbb09f300f8eac5004aafef6e61d6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 24 Jul 2019 21:49:26 +0800 Subject: [PATCH 069/191] fix https://github.com/Neilpang/acme.sh/issues/2377 --- acme.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index d168b56..891d2e1 100755 --- a/acme.sh +++ b/acme.sh @@ -3035,11 +3035,13 @@ _clearupdns() { d=$(_getfield "$entry" 1) txtdomain=$(_getfield "$entry" 2) aliasDomain=$(_getfield "$entry" 3) + _currentRoot=$(_getfield "$entry" 4) txt=$(_getfield "$entry" 5) d_api=$(_getfield "$entry" 6) _debug "d" "$d" _debug "txtdomain" "$txtdomain" _debug "aliasDomain" "$aliasDomain" + _debug "_currentRoot" "$_currentRoot" _debug "txt" "$txt" _debug "d_api" "$d_api" if [ "$d_api" = "$txt" ]; then @@ -6787,7 +6789,7 @@ _process() { _debug "Using server: $_server" fi fi - + _debug "Running cmd: ${_CMD}" case "${_CMD}" in install) install "$_nocron" "$_confighome" "$_noprofile" ;; uninstall) uninstall "$_nocron" ;; From 72e7eb6777d14097961422959eaa0e9ecb3becfc Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 27 Jul 2019 10:49:11 +0800 Subject: [PATCH 070/191] fix format --- dnsapi/dns_cf.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index d4266d1..f1725bd 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -21,7 +21,7 @@ dns_cf_add() { CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - + if [ "$CF_Token" ]; then _saveaccountconf_mutable CF_Token "$CF_Token" _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID" From 41c951811e38002df1d7efad8b1197368aed4697 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 27 Jul 2019 11:09:13 +0800 Subject: [PATCH 071/191] fix format --- dnsapi/dns_cf.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index f1725bd..62e40ca 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -180,9 +180,9 @@ _cf_rest() { data="$3" _debug "$ep" - email_trimmed=$(echo $CF_Email | tr -d '"') - key_trimmed=$(echo $CF_Key | tr -d '"') - token_trimmed=$(echo $CF_Token | tr -d '"') + email_trimmed=$(echo "$CF_Email" | tr -d '"') + key_trimmed=$(echo "$CF_Key" | tr -d '"') + token_trimmed=$(echo "$CF_Token" | tr -d '"') export _H1="Content-Type: application/json" if [ "$token_trimmed" ]; then From 55dea4ee9d2187912ecf97689e12a6e8183a1091 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sat, 27 Jul 2019 11:48:29 +0800 Subject: [PATCH 072/191] sync (#2404) * support jdcloud.com * fix format * ttl 3000 * Escape slashes (#2375) * Change 1.1.1.1 to 1.0.0.1 to probe compatibility (#2330) As we can see, 1.1.1.1 is not routed or routed to an Intranet devices due to historical reason. Change 1.1.1.1 to 1.0.0.1 will have a better compatibility. I found this problem on my Tencent Cloud server. * check empty id * fix error * Add dnsapi for Vultr (#2370) * Add Vultr dns api * PushOver notifications (#2325) * PushOver notifications, using AppToken, UserKey, and optional sounds * fix errors * added dns api support for hexonet (#1776) * update * minor * support new Cloudflare Token format fix https://github.com/Neilpang/acme.sh/issues/2398 * fix wildcard domain name * add more info * fix https://github.com/Neilpang/acme.sh/issues/2377 * fix format * fix format --- acme.sh | 6 +- deploy/docker.sh | 4 +- dnsapi/dns_cf.sh | 87 ++++++++++----------- dnsapi/dns_hexonet.sh | 156 ++++++++++++++++++++++++++++++++++++++ dnsapi/dns_namecheap.sh | 2 +- dnsapi/dns_namesilo.sh | 11 ++- dnsapi/dns_vultr.sh | 163 ++++++++++++++++++++++++++++++++++++++++ notify/pushover.sh | 63 ++++++++++++++++ 8 files changed, 443 insertions(+), 49 deletions(-) create mode 100755 dnsapi/dns_hexonet.sh create mode 100644 dnsapi/dns_vultr.sh create mode 100644 notify/pushover.sh diff --git a/acme.sh b/acme.sh index 67fcdcb..891d2e1 100755 --- a/acme.sh +++ b/acme.sh @@ -3035,11 +3035,13 @@ _clearupdns() { d=$(_getfield "$entry" 1) txtdomain=$(_getfield "$entry" 2) aliasDomain=$(_getfield "$entry" 3) + _currentRoot=$(_getfield "$entry" 4) txt=$(_getfield "$entry" 5) d_api=$(_getfield "$entry" 6) _debug "d" "$d" _debug "txtdomain" "$txtdomain" _debug "aliasDomain" "$aliasDomain" + _debug "_currentRoot" "$_currentRoot" _debug "txt" "$txt" _debug "d_api" "$d_api" if [ "$d_api" = "$txt" ]; then @@ -3621,7 +3623,7 @@ _ns_purge_cf() { _cf_d="$1" _cf_d_type="$2" _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" - _cf_purl="https://1.1.1.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" + _cf_purl="https://1.0.0.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" response="$(_post "" "$_cf_purl")" _debug2 response "$response" } @@ -6787,7 +6789,7 @@ _process() { _debug "Using server: $_server" fi fi - + _debug "Running cmd: ${_CMD}" case "${_CMD}" in install) install "$_nocron" "$_confighome" "$_noprofile" ;; uninstall) uninstall "$_nocron" ;; diff --git a/deploy/docker.sh b/deploy/docker.sh index 4e55099..05333b3 100755 --- a/deploy/docker.sh +++ b/deploy/docker.sh @@ -126,6 +126,7 @@ docker_deploy() { fi if [ "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" ]; then + _info "Reloading: $DEPLOY_DOCKER_CONTAINER_RELOAD_CMD" if ! _docker_exec "$_cid" "$DEPLOY_DOCKER_CONTAINER_RELOAD_CMD"; then return 1 fi @@ -223,7 +224,8 @@ _docker_cp() { _debug2 "_frompath" "$_frompath" _toname="$(basename "$_to")" _debug2 "_toname" "$_toname" - if ! tar --transform="s,$_frompath,$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then + _debug2 "_from" "$_from" + if ! tar --transform="s,$(printf "%s" "$_frompath" | tr '*' .),$_toname," -cz "$_from" 2>/dev/null | _curl_unix_sock "$_DOCKER_SOCK" PUT "/containers/$_dcid/archive?noOverwriteDirNonDir=1&path=$(printf "%s" "$_dir" | _url_encode)" '@-' "Content-Type: application/octet-stream"; then _err "copy error" return 1 fi diff --git a/dnsapi/dns_cf.sh b/dnsapi/dns_cf.sh index cd93189..62e40ca 100755 --- a/dnsapi/dns_cf.sh +++ b/dnsapi/dns_cf.sh @@ -5,6 +5,9 @@ # #CF_Email="xxxx@sss.com" +#CF_Token="xxxx" +#CF_Account_ID="xxxx" + CF_Api="https://api.cloudflare.com/client/v4" ######## Public functions ##################### @@ -14,25 +17,32 @@ dns_cf_add() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi - if ! _contains "$CF_Email" "@"; then - _err "It seems that the CF_Email=$CF_Email is not a valid email address." - _err "Please check and retry." - return 1 - fi + if [ "$CF_Token" ]; then + _saveaccountconf_mutable CF_Token "$CF_Token" + _saveaccountconf_mutable CF_Account_ID "$CF_Account_ID" + else + if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then + CF_Key="" + CF_Email="" + _err "You didn't specify a Cloudflare api key and email yet." + _err "You can get yours from here https://dash.cloudflare.com/profile." + return 1 + fi - #save the api key and email to the account conf file. - _saveaccountconf_mutable CF_Key "$CF_Key" - _saveaccountconf_mutable CF_Email "$CF_Email" + if ! _contains "$CF_Email" "@"; then + _err "It seems that the CF_Email=$CF_Email is not a valid email address." + _err "Please check and retry." + return 1 + fi + #save the api key and email to the account conf file. + _saveaccountconf_mutable CF_Key "$CF_Key" + _saveaccountconf_mutable CF_Email "$CF_Email" + fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -71,19 +81,6 @@ dns_cf_add() { fi _err "Add txt record error." return 1 - # else - # _info "Updating record" - # record_id=$(printf "%s\n" "$response" | _egrep_o "\"id\":\"[^\"]*\"" | cut -d : -f 2 | tr -d \" | head -n 1) - # _debug "record_id" "$record_id" - # - # _cf_rest PUT "zones/$_domain_id/dns_records/$record_id" "{\"id\":\"$record_id\",\"type\":\"TXT\",\"name\":\"$fulldomain\",\"content\":\"$txtvalue\",\"zone_id\":\"$_domain_id\",\"zone_name\":\"$_domain\"}" - # if [ "$?" = "0" ]; then - # _info "Updated, OK" - # return 0 - # fi - # _err "Update error" - # return 1 - # fi } @@ -92,15 +89,10 @@ dns_cf_rm() { fulldomain=$1 txtvalue=$2 + CF_Token="${CF_Token:-$(_readaccountconf_mutable CF_Token)}" + CF_Account_ID="${CF_Account_ID:-$(_readaccountconf_mutable CF_Account_ID)}" CF_Key="${CF_Key:-$(_readaccountconf_mutable CF_Key)}" CF_Email="${CF_Email:-$(_readaccountconf_mutable CF_Email)}" - if [ -z "$CF_Key" ] || [ -z "$CF_Email" ]; then - CF_Key="" - CF_Email="" - _err "You didn't specify a Cloudflare api key and email yet." - _err "You can get yours from here https://dash.cloudflare.com/profile." - return 1 - fi _debug "First detect the root zone" if ! _get_root "$fulldomain"; then @@ -157,8 +149,14 @@ _get_root() { return 1 fi - if ! _cf_rest GET "zones?name=$h"; then - return 1 + if [ "$CF_Account_ID" ]; then + if ! _cf_rest GET "zones?name=$h&account.id=$CF_Account_ID"; then + return 1 + fi + else + if ! _cf_rest GET "zones?name=$h"; then + return 1 + fi fi if _contains "$response" "\"name\":\"$h\"" || _contains "$response" '"total_count":1'; then @@ -182,12 +180,17 @@ _cf_rest() { data="$3" _debug "$ep" - email_trimmed=$(echo $CF_Email | tr -d '"') - key_trimmed=$(echo $CF_Key | tr -d '"') + email_trimmed=$(echo "$CF_Email" | tr -d '"') + key_trimmed=$(echo "$CF_Key" | tr -d '"') + token_trimmed=$(echo "$CF_Token" | tr -d '"') - export _H1="X-Auth-Email: $email_trimmed" - export _H2="X-Auth-Key: $key_trimmed" - export _H3="Content-Type: application/json" + export _H1="Content-Type: application/json" + if [ "$token_trimmed" ]; then + export _H2="Authorization: Bearer $token_trimmed" + else + export _H2="X-Auth-Email: $email_trimmed" + export _H3="X-Auth-Key: $key_trimmed" + fi if [ "$m" != "GET" ]; then _debug data "$data" diff --git a/dnsapi/dns_hexonet.sh b/dnsapi/dns_hexonet.sh new file mode 100755 index 0000000..f150311 --- /dev/null +++ b/dnsapi/dns_hexonet.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env sh + +# +# Hexonet_Login="username!roleId" +# +# Hexonet_Password="rolePassword" + +Hexonet_Api="https://coreapi.1api.net/api/call.cgi" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_hexonet_add() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Login and Hexonet_Password" + return 1 + fi + + if ! _contains "$Hexonet_Login" "!"; then + _err "It seems that the Hexonet_Login=$Hexonet_Login is not a restrivteed user." + _err "Please check and retry." + return 1 + fi + + #save the username and password to the account conf file. + _saveaccountconf_mutable Hexonet_Login "$Hexonet_Login" + _saveaccountconf_mutable Hexonet_Password "$Hexonet_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" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + _info "Adding record" + if _hexonet_rest "command=UpdateDNSZone&dnszone=${_domain}.&addrr0=${_sub_domain}%20IN%20TXT%20${txtvalue}"; then + if _contains "$response" "CODE=200"; then + _info "Added, OK" + return 0 + else + _err "Add txt record error." + return 1 + fi + fi + _err "Add txt record error." + return 1 + +} + +#fulldomain txtvalue +dns_hexonet_rm() { + fulldomain=$1 + txtvalue=$2 + + Hexonet_Login="${Hexonet_Login:-$(_readaccountconf_mutable Hexonet_Login)}" + Hexonet_Password="${Hexonet_Password:-$(_readaccountconf_mutable Hexonet_Password)}" + if [ -z "$Hexonet_Login" ] || [ -z "$Hexonet_Password" ]; then + Hexonet_Login="" + Hexonet_Password="" + _err "You must export variables: Hexonet_Login and Hexonet_Password" + 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" + + _debug "Getting txt records" + _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}.&RRTYPE=TXT&RR=${txtvalue}" + + if ! _contains "$response" "CODE=200"; then + _err "Error" + return 1 + fi + + count=$(printf "%s\n" "$response" | _egrep_o "PROPERTY[TOTAL][0]=" | cut -d = -f 2) + _debug count "$count" + if [ "$count" = "0" ]; then + _info "Don't need to remove." + else + if ! _hexonet_rest "&command=UpdateDNSZone&dnszone=${_domain}.&delrr0='${_sub_domain}%20IN%20TXT%20\"${txtvalue}\""; then + _err "Delete record error." + return 1 + fi + _contains "$response" "CODE=200" + fi + +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + domain=$1 + i=1 + 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 ! _hexonet_rest "&command=QueryDNSZoneRRList&dnszone=${h}."; then + return 1 + fi + + if _contains "$response" "CODE=200"; 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 +} + +_hexonet_rest() { + query_params="$1" + _debug "$query_params" + + response="$(_get "${Hexonet_Api}?s_login=${Hexonet_Login}&s_pw=${Hexonet_Password}&${query_params}")" + + if [ "$?" != "0" ]; then + _err "error $query_params" + return 1 + fi + _debug2 response "$response" + return 0 +} diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index 6553deb..a82e12d 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -164,7 +164,7 @@ _namecheap_set_publicip() { _debug sourceip "$NAMECHEAP_SOURCEIP" ip=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') - addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https)://.*') + addr=$(echo "$NAMECHEAP_SOURCEIP" | _egrep_o '(http|https):\/\/.*') _debug2 ip "$ip" _debug2 addr "$addr" diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh index dc1a4fd..ed6d0e0 100755 --- a/dnsapi/dns_namesilo.sh +++ b/dnsapi/dns_namesilo.sh @@ -59,9 +59,14 @@ dns_namesilo_rm() { 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." + _record_id=$(echo "$response" | _egrep_o "([^<]*)TXT$fulldomain" | _egrep_o "([^<]*)" | sed -r "s/([^<]*)<\/record_id>/\1/" | tail -n 1) + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi else _err "Unable to retrieve the record id." return 1 diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh new file mode 100644 index 0000000..f15e7c4 --- /dev/null +++ b/dnsapi/dns_vultr.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env sh + +# +#VULTR_API_KEY=000011112222333344445555666677778888 + +VULTR_Api="https://api.vultr.com/v1" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_vultr_add() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY='' + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + if ! _vultr_rest POST 'dns/create_record' "domain=$_domain&name=$_sub_domain&data=\"$txtvalue\"&type=TXT"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#fulldomain txtvalue +dns_vultr_rm() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VULTR_API_KEY="${VULTR_API_KEY:-$(_readaccountconf_mutable VULTR_API_KEY)}" + if test -z "$VULTR_API_KEY"; then + VULTR_API_KEY="" + _err 'VULTR_API_KEY was not exported' + return 1 + fi + + _saveaccountconf_mutable VULTR_API_KEY "$VULTR_API_KEY" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + _vultr_rest GET "dns/records?domain=$_domain" + + if printf "%s\n" "$response" | grep "\"type\":\"TXT\",\"name\":\"$fulldomain\"" >/dev/null; then + _err 'Error' + return 1 + fi + + _record_id="$(echo "$response" | tr '{}' '\n' | grep '"TXT"' | grep "$txtvalue" | tr ',' '\n' | grep -i 'RECORDID' | cut -d : -f 2)" + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi + + if ! _vultr_rest POST 'dns/delete_record' "domain=$_domain&RECORDID=$_record_id"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +# _domain_id=sdjkglgdfewsdfg +_get_root() { + domain=$1 + i=1 + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + return 1 + fi + + if ! _vultr_rest GET "dns/list"; then + return 1 + fi + + if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then + if _contains "$response" "\"domain\":\"$_domain\""; then + _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" + _domain=$_domain + return 0 + else + _err 'Invalid domain' + return 1 + fi + else + _err "$response" + return 1 + fi + i=$(_math "$i" + 1) + done + + return 1 +} + +_vultr_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + + api_key_trimmed=$(echo $VULTR_API_KEY | tr -d '"') + + export _H1="Api-Key: $api_key_trimmed" + export _H2='Content-Type: application/x-www-form-urlencoded' + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$VULTR_Api/$ep" "" "$m")" + else + response="$(_get "$VULTR_Api/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "Error $ep" + return 1 + fi + + _debug2 response "$response" + return 0 +} diff --git a/notify/pushover.sh b/notify/pushover.sh new file mode 100644 index 0000000..a07cbd3 --- /dev/null +++ b/notify/pushover.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env sh + +#Support for pushover.net's api. Push notification platform for multiple platforms +#PUSHOVER_TOKEN="" Required, pushover application token +#PUSHOVER_USER="" Required, pushover userkey +#PUSHOVER_DEVICE="" Optional, Specific device or devices by hostnames, joining multiples with a comma (such as device=iphone,nexus5) +#PUSHOVER_PRIORITY="" Optional, Lowest Priority (-2), Low Priority (-1), Normal Priority (0), High Priority (1) + +PUSHOVER_URI="https://api.pushover.net/1/messages.json" + +pushover_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_statusCode" "$_statusCode" + + PUSHOVER_TOKEN="${PUSHOVER_TOKEN:-$(_readaccountconf_mutable PUSHOVER_TOKEN)}" + if [ -z "$PUSHOVER_TOKEN" ]; then + PUSHOVER_TOKEN="" + _err "You didn't specify a PushOver application token yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_TOKEN "$PUSHOVER_TOKEN" + + PUSHOVER_USER="${PUSHOVER_USER:-$(_readaccountconf_mutable PUSHOVER_USER)}" + if [ -z "$PUSHOVER_USER" ]; then + PUSHOVER_USER="" + _err "You didn't specify a PushOver UserKey yet." + return 1 + fi + _saveaccountconf_mutable PUSHOVER_USER "$PUSHOVER_USER" + + PUSHOVER_DEVICE="${PUSHOVER_DEVICE:-$(_readaccountconf_mutable PUSHOVER_DEVICE)}" + if [ "$PUSHOVER_DEVICE" ]; then + _saveaccountconf_mutable PUSHOVER_DEVICE "$PUSHOVER_DEVICE" + fi + + PUSHOVER_PRIORITY="${PUSHOVER_PRIORITY:-$(_readaccountconf_mutable PUSHOVER_PRIORITY)}" + if [ "$PUSHOVER_PRIORITY" ]; then + _saveaccountconf_mutable PUSHOVER_PRIORITY "$PUSHOVER_PRIORITY" + fi + + PUSHOVER_SOUND="${PUSHOVER_SOUND:-$(_readaccountconf_mutable PUSHOVER_SOUND)}" + if [ "$PUSHOVER_SOUND" ]; then + _saveaccountconf_mutable PUSHOVER_SOUND "$PUSHOVER_SOUND" + fi + + export _H1="Content-Type: application/json" + _content="$(printf "*%s*\n" "$_content" | _json_encode)" + _subject="$(printf "*%s*\n" "$_subject" | _json_encode)" + _data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}" + + response="" #just make shellcheck happy + if _post "$_data" "$PUSHOVER_URI"; then + if _contains "$response" "{\"status\":1"; then + _info "PUSHOVER send success." + return 0 + fi + fi + _err "PUSHOVER send error." + _err "$response" + return 1 +} From b9b2cd278b098b7e43143e58809878f1e8fbcf2b Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 29 Jul 2019 21:12:19 +0800 Subject: [PATCH 073/191] fix https://github.com/Neilpang/acme.sh/pull/2275 --- acme.sh | 4 ++-- dnsapi/dns_dp.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 891d2e1..66d9b7a 100755 --- a/acme.sh +++ b/acme.sh @@ -3623,7 +3623,7 @@ _ns_purge_cf() { _cf_d="$1" _cf_d_type="$2" _debug "Cloudflare purge $_cf_d_type record for domain $_cf_d" - _cf_purl="https://1.0.0.1/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" + _cf_purl="https://cloudflare-dns.com/api/v1/purge?domain=$_cf_d&type=$_cf_d_type" response="$(_post "" "$_cf_purl")" _debug2 response "$response" } @@ -3682,11 +3682,11 @@ _check_dns_entries() { fi _left=1 _info "Not valid yet, let's wait 10 seconds and check next one." - _sleep 10 __purge_txt "$txtdomain" if [ "$txtdomain" != "$aliasDomain" ]; then __purge_txt "$aliasDomain" fi + _sleep 10 done if [ "$_left" ]; then _info "Let's wait 10 seconds and check again". diff --git a/dnsapi/dns_dp.sh b/dnsapi/dns_dp.sh index 6bbf149..480c1f9 100755 --- a/dnsapi/dns_dp.sh +++ b/dnsapi/dns_dp.sh @@ -63,7 +63,7 @@ dns_dp_rm() { return 0 fi - record_id=$(echo "$response" | tr "{" "\n" | grep "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2) + record_id=$(echo "$response" | tr "{" "\n" | grep -- "$txtvalue" | grep '^"id"' | cut -d : -f 2 | cut -d '"' -f 2) _debug record_id "$record_id" if [ -z "$record_id" ]; then _err "Can not get record id." From 75191e71870abdb35365d057ffd746e8cd8d5b4f Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 31 Jul 2019 23:22:07 +0800 Subject: [PATCH 074/191] fix https://github.com/Neilpang/acme.sh/issues/2417 --- acme.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 66d9b7a..8bff2e8 100755 --- a/acme.sh +++ b/acme.sh @@ -5932,8 +5932,11 @@ _send_notify() { _send_err=0 for _n_hook in $(echo "$_nhooks" | tr ',' " "); do _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")" - _info "Found $_n_hook_file" - + _info "Found $_n_hook_file for $_n_hook" + if [ -z "$_n_hook_file" ]; then + _err "Can not find the hook file for $_n_hook" + continue + fi if ! ( if ! . "$_n_hook_file"; then _err "Load file $_n_hook_file error. Please check your api file and try again." From d42cf6daebfd134dca7af41b47aac34866b6a771 Mon Sep 17 00:00:00 2001 From: James Qian Date: Mon, 5 Aug 2019 21:35:03 +0800 Subject: [PATCH 075/191] dnsapi: fix typo in dns_desec.sh (#2427) Signed-off-by: James Qian --- dnsapi/dns_desec.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_desec.sh b/dnsapi/dns_desec.sh index 6488b7f..61d080b 100644 --- a/dnsapi/dns_desec.sh +++ b/dnsapi/dns_desec.sh @@ -25,8 +25,8 @@ dns_desec_add() { if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then DEDYN_TOKEN="" DEDYN_NAME="" - _err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." - _err "Please create you key and try again." + _err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet." + _err "Please create your key and try again." _err "e.g." _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" _err "export DEDYN_NAME=foobar.dedyn.io" @@ -92,8 +92,8 @@ dns_desec_rm() { if [ -z "$DEDYN_TOKEN" ] || [ -z "$DEDYN_NAME" ]; then DEDYN_TOKEN="" DEDYN_NAME="" - _err "You don't specify DEDYN_TOKEN and DEDYN_NAME yet." - _err "Please create you key and try again." + _err "You did not specify DEDYN_TOKEN and DEDYN_NAME yet." + _err "Please create your key and try again." _err "e.g." _err "export DEDYN_TOKEN=d41d8cd98f00b204e9800998ecf8427e" _err "export DEDYN_NAME=foobar.dedyn.io" From d74dfb1f5c333c911d150f7a7485ff99b8c984b4 Mon Sep 17 00:00:00 2001 From: lcdtyph Date: Mon, 5 Aug 2019 21:38:32 +0800 Subject: [PATCH 076/191] IFTTT Webhooks Notification (#2416) * IFTTT webhooks Notification * use sh instead of bash * don't save value that is not set --- notify/ifttt.sh | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 notify/ifttt.sh diff --git a/notify/ifttt.sh b/notify/ifttt.sh new file mode 100644 index 0000000..8a14f5f --- /dev/null +++ b/notify/ifttt.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env sh + +#Support ifttt.com webhooks api + +#IFTTT_API_KEY="xxxx" +#IFTTT_EVENT_NAME="yyyy" + +#IFTTT_SUBJECT_KEY="value1|value2|value3" #optional, use "value1" as default +#IFTTT_CONTENT_KEY="value1|value2|value3" #optional, use "value2" as default + +_IFTTT_AVAIL_MSG_KEYS="value1,value2,value3" + +# subject content statusCode +ifttt_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + IFTTT_API_KEY="${IFTTT_API_KEY:-$(_readaccountconf_mutable IFTTT_API_KEY)}" + if [ -z "$IFTTT_API_KEY" ]; then + IFTTT_API_KEY="" + _err "You didn't specify a ifttt webhooks api key IFTTT_API_KEY yet." + _err "You can get yours from https://ifttt.com" + return 1 + fi + _saveaccountconf_mutable IFTTT_API_KEY "$IFTTT_API_KEY" + + IFTTT_EVENT_NAME="${IFTTT_EVENT_NAME:-$(_readaccountconf_mutable IFTTT_EVENT_NAME)}" + if [ -z "$IFTTT_EVENT_NAME" ]; then + IFTTT_EVENT_NAME="" + _err "You didn't specify a ifttt webhooks event name IFTTT_EVENT_NAME yet." + return 1 + fi + _saveaccountconf_mutable IFTTT_EVENT_NAME "$IFTTT_EVENT_NAME" + + IFTTT_SUBJECT_KEY="${IFTTT_SUBJECT_KEY:-$(_readaccountconf_mutable IFTTT_SUBJECT_KEY)}" + if [ -z "$IFTTT_SUBJECT_KEY" ]; then + IFTTT_SUBJECT_KEY="value1" + _info "The IFTTT_SUBJECT_KEY is not set, so use the default value1 as key." + elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$IFTTT_SUBJECT_KEY"; then + _err "The IFTTT_SUBJECT_KEY \"$IFTTT_SUBJECT_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS" + IFTTT_SUBJECT_KEY="" + return 1 + else + _saveaccountconf_mutable IFTTT_SUBJECT_KEY "$IFTTT_SUBJECT_KEY" + fi + + IFTTT_CONTENT_KEY="${IFTTT_CONTENT_KEY:-$(_readaccountconf_mutable IFTTT_CONTENT_KEY)}" + if [ -z "$IFTTT_CONTENT_KEY" ]; then + IFTTT_CONTENT_KEY="value2" + _info "The IFTTT_CONTENT_KEY is not set, so use the default value2 as key." + elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$IFTTT_CONTENT_KEY"; then + _err "The IFTTT_CONTENT_KEY \"$IFTTT_CONTENT_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS" + IFTTT_CONTENT_KEY="" + return 1 + else + _saveaccountconf_mutable IFTTT_CONTENT_KEY "$IFTTT_CONTENT_KEY" + fi + + if [ "$IFTTT_SUBJECT_KEY" = "$IFTTT_CONTENT_KEY" ]; then + IFTTT_SUBJECT_KEY="" + IFTTT_CONTENT_KEY="" + _err "The IFTTT_SUBJECT_KEY must not be same as IFTTT_CONTENT_KEY." + return 1 + fi + + IFTTT_API_URL="https://maker.ifttt.com/trigger/$IFTTT_EVENT_NAME/with/key/$IFTTT_API_KEY" + + _content=$(echo "$_content" | _json_encode) + _subject=$(echo "$_subject" | _json_encode) + _data="{\"$IFTTT_SUBJECT_KEY\": \"$_subject\", \"$IFTTT_CONTENT_KEY\": \"$_content\"}" + + response="" #just make shellcheck happy + if _post "$_data" "$IFTTT_API_URL" "" "POST" "application/json"; then + if _contains "$response" "Congratulations"; then + _info "IFTTT webhooks event fired success." + return 0 + fi + fi + _err "IFTTT webhooks event fired error." + _err "$response" + return 1 +} From 143eac092ce8b8fe7c068809449ce7a5b71f3ea2 Mon Sep 17 00:00:00 2001 From: neilpang Date: Mon, 5 Aug 2019 22:03:56 +0800 Subject: [PATCH 077/191] fix notify message --- acme.sh | 5 +++-- notify/ifttt.sh | 12 ++++++------ notify/pushover.sh | 12 ++++++------ notify/sendgrid.sh | 12 ++++++------ 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/acme.sh b/acme.sh index 8bff2e8..8452d58 100755 --- a/acme.sh +++ b/acme.sh @@ -5932,7 +5932,8 @@ _send_notify() { _send_err=0 for _n_hook in $(echo "$_nhooks" | tr ',' " "); do _n_hook_file="$(_findHook "" $_SUB_FOLDER_NOTIFY "$_n_hook")" - _info "Found $_n_hook_file for $_n_hook" + _info "Sending via: $_n_hook" + _debug "Found $_n_hook_file for $_n_hook" if [ -z "$_n_hook_file" ]; then _err "Can not find the hook file for $_n_hook" continue @@ -5971,7 +5972,7 @@ _set_notify_hook() { _nhooks="$1" _test_subject="Hello, this is notification from $PROJECT_NAME" - _test_content="If you receive this email, your notification works." + _test_content="If you receive this message, your notification works." _send_notify "$_test_subject" "$_test_content" "$_nhooks" 0 diff --git a/notify/ifttt.sh b/notify/ifttt.sh index 8a14f5f..7b82963 100644 --- a/notify/ifttt.sh +++ b/notify/ifttt.sh @@ -73,13 +73,13 @@ ifttt_send() { _subject=$(echo "$_subject" | _json_encode) _data="{\"$IFTTT_SUBJECT_KEY\": \"$_subject\", \"$IFTTT_CONTENT_KEY\": \"$_content\"}" - response="" #just make shellcheck happy - if _post "$_data" "$IFTTT_API_URL" "" "POST" "application/json"; then - if _contains "$response" "Congratulations"; then - _info "IFTTT webhooks event fired success." - return 0 - fi + response="$(_post "$_data" "$IFTTT_API_URL" "" "POST" "application/json")" + + if [ "$?" = "0" ] && _contains "$response" "Congratulations"; then + _info "IFTTT webhooks event fired success." + return 0 fi + _err "IFTTT webhooks event fired error." _err "$response" return 1 diff --git a/notify/pushover.sh b/notify/pushover.sh index a07cbd3..0f99739 100644 --- a/notify/pushover.sh +++ b/notify/pushover.sh @@ -50,13 +50,13 @@ pushover_send() { _subject="$(printf "*%s*\n" "$_subject" | _json_encode)" _data="{\"token\": \"$PUSHOVER_TOKEN\",\"user\": \"$PUSHOVER_USER\",\"title\": \"$_subject\",\"message\": \"$_content\",\"sound\": \"$PUSHOVER_SOUND\", \"device\": \"$PUSHOVER_DEVICE\", \"priority\": \"$PUSHOVER_PRIORITY\"}" - response="" #just make shellcheck happy - if _post "$_data" "$PUSHOVER_URI"; then - if _contains "$response" "{\"status\":1"; then - _info "PUSHOVER send success." - return 0 - fi + response="$(_post "$_data" "$PUSHOVER_URI")" + + if [ "$?" = "0" ] && _contains "$response" "{\"status\":1"; then + _info "PUSHOVER send success." + return 0 fi + _err "PUSHOVER send error." _err "$response" return 1 diff --git a/notify/sendgrid.sh b/notify/sendgrid.sh index 5c5bfdb..0d5ea3b 100644 --- a/notify/sendgrid.sh +++ b/notify/sendgrid.sh @@ -42,13 +42,13 @@ sendgrid_send() { _content="$(echo "$_content" | _json_encode)" _data="{\"personalizations\": [{\"to\": [{\"email\": \"$SENDGRID_TO\"}]}],\"from\": {\"email\": \"$SENDGRID_FROM\"},\"subject\": \"$_subject\",\"content\": [{\"type\": \"text/plain\", \"value\": \"$_content\"}]}" - response="" #just make shellcheck happy - if _post "$_data" "https://api.sendgrid.com/v3/mail/send"; then - if [ -z "$response" ]; then - _info "sendgrid send sccess." - return 0 - fi + response="$(_post "$_data" "https://api.sendgrid.com/v3/mail/send")" + + if [ "$?" = "0" ] && [ -z "$response" ]; then + _info "sendgrid send sccess." + return 0 fi + _err "sendgrid send error." _err "$response" return 1 From 874bd093cb5076613f74aac84168952af0e27f9f Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Mon, 5 Aug 2019 22:35:40 +0800 Subject: [PATCH 078/191] fix https://github.com/Neilpang/acme.sh/issues/2409 (#2430) --- dnsapi/dns_he.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index df00c74..caa4d2c 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -134,9 +134,9 @@ _find_zone() { _zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&') _zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2) _debug2 "These are the zones on this HE account:" - _debug2 "$_zone_names" + _debug2 "_zone_names" "$_zone_names" _debug2 "And these are their respective IDs:" - _debug2 "$_zone_ids" + _debug2 "_zone_ids" "$_zone_ids" if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then _err "Can not get zone names." return 1 @@ -154,10 +154,14 @@ _find_zone() { _debug "Looking for zone \"${_attempted_zone}\"" - line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone" | cut -d : -f 1)" - + line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone\$" | _head_n 1 | cut -d : -f 1)" + _debug2 line_num "$line_num" if [ "$line_num" ]; then _zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p") + if [ -z "$_zone_id" ]; then + _err "Can not find zone id." + return 1 + fi _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." return 0 fi From c7849a43e13fd98f2452613165561e547b2606df Mon Sep 17 00:00:00 2001 From: mleo2003 Date: Tue, 6 Aug 2019 06:41:12 -0700 Subject: [PATCH 079/191] Add variable exports for Successful Post Hook and Renew Hook calls (#2431) --- acme.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/acme.sh b/acme.sh index 8452d58..4742e61 100755 --- a/acme.sh +++ b/acme.sh @@ -3265,6 +3265,11 @@ _on_issue_success() { if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_post_hook" ); then _err "Error when run post hook." @@ -3276,6 +3281,11 @@ _on_issue_success() { if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then _info "Run renew hook:'$_chk_renew_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_renew_hook" ); then _err "Error when run renew hook." From f82ff90f0670becb8b2c63e9eea591361a380ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=AF=D1=85=D0=B8=D0=BD?= Date: Sun, 11 Aug 2019 06:41:57 +0300 Subject: [PATCH 080/191] fixed json parse regex for support api gcore_cdn (#2381) --- deploy/gcore_cdn.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index e0921bc..bbda58e 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh # Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). -# Uses command line curl for send requests and jq for parse responses. # Returns 0 when success. # # Written by temoffey @@ -117,7 +116,7 @@ gcore_cdn_deploy() { _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" - _regex=".*\"sslData\":\([0-9]*\)}.*$" + _regex=".*\"sslData\":\([0-9]*\).*$" _debug _regex "$_regex" _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataNew "$_sslDataNew" From ee38cccad8f76b807206165324e7bf771aa981dc Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sun, 11 Aug 2019 11:56:59 +0800 Subject: [PATCH 081/191] sync (#2436) * fix https://github.com/Neilpang/acme.sh/issues/2409 (#2430) * Add variable exports for Successful Post Hook and Renew Hook calls (#2431) * fixed json parse regex for support api gcore_cdn (#2381) --- acme.sh | 10 ++++++++++ deploy/gcore_cdn.sh | 3 +-- dnsapi/dns_he.sh | 12 ++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 8452d58..4742e61 100755 --- a/acme.sh +++ b/acme.sh @@ -3265,6 +3265,11 @@ _on_issue_success() { if [ "$_chk_post_hook" ]; then _info "Run post hook:'$_chk_post_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_post_hook" ); then _err "Error when run post hook." @@ -3276,6 +3281,11 @@ _on_issue_success() { if [ "$IS_RENEW" ] && [ "$_chk_renew_hook" ]; then _info "Run renew hook:'$_chk_renew_hook'" if ! ( + export CERT_PATH + export CERT_KEY_PATH + export CA_CERT_PATH + export CERT_FULLCHAIN_PATH + export Le_Domain="$_main_domain" cd "$DOMAIN_PATH" && eval "$_chk_renew_hook" ); then _err "Error when run renew hook." diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index e0921bc..bbda58e 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -1,7 +1,6 @@ #!/usr/bin/env sh # Here is the script to deploy the cert to G-Core CDN service (https://gcorelabs.com/ru/) using the G-Core Labs API (https://docs.gcorelabs.com/cdn/). -# Uses command line curl for send requests and jq for parse responses. # Returns 0 when success. # # Written by temoffey @@ -117,7 +116,7 @@ gcore_cdn_deploy() { _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/resources/$_resourceId" '' "PUT") _debug _response "$_response" - _regex=".*\"sslData\":\([0-9]*\)}.*$" + _regex=".*\"sslData\":\([0-9]*\).*$" _debug _regex "$_regex" _sslDataNew=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataNew "$_sslDataNew" diff --git a/dnsapi/dns_he.sh b/dnsapi/dns_he.sh index df00c74..caa4d2c 100755 --- a/dnsapi/dns_he.sh +++ b/dnsapi/dns_he.sh @@ -134,9 +134,9 @@ _find_zone() { _zone_ids=$(echo "$_matches" | _egrep_o "hosted_dns_zoneid=[0-9]*&" | cut -d = -f 2 | tr -d '&') _zone_names=$(echo "$_matches" | _egrep_o "name=.*onclick" | cut -d '"' -f 2) _debug2 "These are the zones on this HE account:" - _debug2 "$_zone_names" + _debug2 "_zone_names" "$_zone_names" _debug2 "And these are their respective IDs:" - _debug2 "$_zone_ids" + _debug2 "_zone_ids" "$_zone_ids" if [ -z "$_zone_names" ] || [ -z "$_zone_ids" ]; then _err "Can not get zone names." return 1 @@ -154,10 +154,14 @@ _find_zone() { _debug "Looking for zone \"${_attempted_zone}\"" - line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone" | cut -d : -f 1)" - + line_num="$(echo "$_zone_names" | grep -n "^$_attempted_zone\$" | _head_n 1 | cut -d : -f 1)" + _debug2 line_num "$line_num" if [ "$line_num" ]; then _zone_id=$(echo "$_zone_ids" | sed -n "${line_num}p") + if [ -z "$_zone_id" ]; then + _err "Can not find zone id." + return 1 + fi _debug "Found relevant zone \"$_attempted_zone\" with id \"$_zone_id\" - will be used for domain \"$_domain\"." return 0 fi From 5bdfdfefbebd7ee4f95f7009947f56a25db07c4a Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 11 Aug 2019 14:07:36 +0800 Subject: [PATCH 082/191] start 2.8.3 Forbidden sudo --- acme.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4742e61..72bf070 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.2 +VER=2.8.3 PROJECT_NAME="acme.sh" @@ -135,6 +135,8 @@ _DNS_MANUAL_WIKI="https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode" _NOTIFY_WIKI="https://github.com/Neilpang/acme.sh/wiki/notify" +_SUDO_WIKI="https://github.com/Neilpang/acme.sh/wiki/sudo" + _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" @@ -6233,6 +6235,23 @@ _processAccountConf() { } +_checkSudo() { + if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then + if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then + #it's root using sudo, no matter it's using sudo or not, just fine + return 0 + fi + if [ "$SUDO_COMMAND" = "/bin/su" ]; then + #it's a normal user doing "sudo su" + #fine + return 0 + fi + #otherwise + return 1 + fi + return 0 +} + _process() { _CMD="" _domain="" @@ -6761,6 +6780,14 @@ _process() { done if [ "${_CMD}" != "install" ]; then + if [ "$__INTERACTIVE" ] && ! _checkSudo; then + if [ -z "$FORCE" ]; then + #Use "echo" here, instead of _info. it's too early + echo "It seems that you are using sudo, please read this link first:" + echo "$_SUDO_WIKI" + return 1 + fi + fi __initHome if [ "$_log" ]; then if [ -z "$_logfile" ]; then From a3361806ab49043fca46f81a0edc2357b7d3947c Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Sun, 11 Aug 2019 22:43:07 +0800 Subject: [PATCH 083/191] sync (#2437) * fix https://github.com/Neilpang/acme.sh/issues/2409 (#2430) * Add variable exports for Successful Post Hook and Renew Hook calls (#2431) * fixed json parse regex for support api gcore_cdn (#2381) * start 2.8.3 Forbidden sudo --- acme.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4742e61..72bf070 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.2 +VER=2.8.3 PROJECT_NAME="acme.sh" @@ -135,6 +135,8 @@ _DNS_MANUAL_WIKI="https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode" _NOTIFY_WIKI="https://github.com/Neilpang/acme.sh/wiki/notify" +_SUDO_WIKI="https://github.com/Neilpang/acme.sh/wiki/sudo" + _DNS_MANUAL_ERR="The dns manual mode can not renew automatically, you must issue it again manually. You'd better use the other modes instead." _DNS_MANUAL_WARN="It seems that you are using dns manual mode. please take care: $_DNS_MANUAL_ERR" @@ -6233,6 +6235,23 @@ _processAccountConf() { } +_checkSudo() { + if [ "$SUDO_GID" ] && [ "$SUDO_COMMAND" ] && [ "$SUDO_USER" ] && [ "$SUDO_UID" ]; then + if [ "$SUDO_USER" = "root" ] && [ "$SUDO_UID" = "0" ]; then + #it's root using sudo, no matter it's using sudo or not, just fine + return 0 + fi + if [ "$SUDO_COMMAND" = "/bin/su" ]; then + #it's a normal user doing "sudo su" + #fine + return 0 + fi + #otherwise + return 1 + fi + return 0 +} + _process() { _CMD="" _domain="" @@ -6761,6 +6780,14 @@ _process() { done if [ "${_CMD}" != "install" ]; then + if [ "$__INTERACTIVE" ] && ! _checkSudo; then + if [ -z "$FORCE" ]; then + #Use "echo" here, instead of _info. it's too early + echo "It seems that you are using sudo, please read this link first:" + echo "$_SUDO_WIKI" + return 1 + fi + fi __initHome if [ "$_log" ]; then if [ -z "$_logfile" ]; then From 9b173dcd7149dd9fa50d621bf21fe4a874616240 Mon Sep 17 00:00:00 2001 From: Sky Chen Date: Thu, 15 Aug 2019 14:23:12 +0800 Subject: [PATCH 084/191] fixed #2441: dns_namesilo.sh _get_root (#2442) fixed #2441: dns_namesilo.sh _get_root (#2442) --- dnsapi/dns_namesilo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_namesilo.sh b/dnsapi/dns_namesilo.sh index ed6d0e0..0b87b7f 100755 --- a/dnsapi/dns_namesilo.sh +++ b/dnsapi/dns_namesilo.sh @@ -110,7 +110,7 @@ _get_root() { return 1 fi - if _contains "$response" "$host"; then + if _contains "$response" "$host"; then _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) _domain="$host" return 0 From 0b2b8b960b07232edd92fed0124a35cbfd969a87 Mon Sep 17 00:00:00 2001 From: David Kerr Date: Fri, 16 Aug 2019 22:56:22 -0400 Subject: [PATCH 085/191] Replace grep -o with sed --- dnsapi/dns_freedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 8a48cf7..ee01366 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -305,7 +305,7 @@ _freedns_domain_id() { domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ - | grep -o "edit\.php?edit_domain_id=[0-9a-zA-Z]*" \ + | sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' \ | cut -d = -f 2)" # The above beauty extracts domain ID from the html page... # strip out all blank space and new lines. Then insert newlines @@ -352,7 +352,7 @@ _freedns_data_id() { data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ - | grep -o "edit\.php?data_id=[0-9a-zA-Z]*" \ + | sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' \ | cut -d = -f 2)" # The above beauty extracts data ID from the html page... # strip out all blank space and new lines. Then insert newlines From e0deca33d00f6e8dfd9473b1d2bbf83132fb2e72 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 14:27:23 +0200 Subject: [PATCH 086/191] Added Leaseweb API for dns-01 verification --- dnsapi/dns_leaseweb.sh | 130 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 dnsapi/dns_leaseweb.sh diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh new file mode 100644 index 0000000..3edf55f --- /dev/null +++ b/dnsapi/dns_leaseweb.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env sh + +#Author: Rolph Haspers +#Utilize leaseweb.com API to finish dns-01 verifications. +#Requires a Leaseweb API Key (export LSW_Key="Your Key") +######## Public functions ##################### + +LSW_API="https://api.leaseweb.com/hosting/v2/domains/" + +#Usage: dns_leaseweb_add _acme-challenge.www.domain.com +dns_leaseweb_add() { + fulldomain=$1 + txtvalue=$2 + + LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}" + if [ -z "$LSW_Key" ]; then + LSW_Key="" + _err "You don't specify Leaseweb 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 LSW_Key "$LSW_Key" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _root_domain "$_domain" + _debug _domain "$fulldomain" + + if _lsw_api "POST" "$_domain" "$fulldomain" "$txtvalue"; then + if [ "$_code" = "201" ]; then + _info "Added, OK" + return 0 + else + _err "Add txt record error, invalid code. Code: $_code" + return 1 + fi + fi + _err "Add txt record error." + + return 1 +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_leaseweb_rm() { + fulldomain=$1 + txtvalue=$2 + + LSW_Key="${LSW_Key:-$(_readaccountconf_mutable LSW_Key)}" + + _debug "First detect the root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug _root_domain "$_domain" + _debug _domain "$fulldomain" + + if _lsw_api "DELETE" "$_domain" "$fulldomain" "$txtvalue"; then + if [ "$_code" = "204" ]; then + _info "Deleted, OK" + return 0 + else + _err "Delete txt record error." + return 1 + fi + fi + _err "Delete txt record error." + + return 1 +} + + +#################### Private functions below ################################## +# _acme-challenge.www.domain.com +# returns +# _domain=domain.com +_get_root() { + domain=$1 + i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" + i=$(_math "$i" - 1) + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + if [ -z "$h" ]; then + return 1 + fi + _domain="$h" + return 0 + done + _debug "$domain not found" + return 1 +} + +_lsw_api() { + cmd=$1 + domain=$2 + fulldomain=$3 + txtvalue=$4 + + # Construct the HTTP Authorization header + export _H2="Content-Type: application/json" + export _H1="X-Lsw-Auth: ${LSW_Key}" + + if [ "$cmd" == "POST" ]; then + data="{\"name\": \"$fulldomain.\",\"type\": \"TXT\",\"content\": [\"$txtvalue\"],\"ttl\": 60}" + response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + _debug "http response code $_code" + _debug response "$response" + return 0 + fi + + if [ "$cmd" == "DELETE" ]; then + response="$(_post "" "$LSW_API/$domain/resourceRecordSets/$fulldomain/TXT" "" "DELETE")" + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + _debug "http response code $_code" + _debug response "$response" + return 0 + fi + + return 1 +} \ No newline at end of file From 54b38086e5abc37c48dcb55ffd2f3800098dd126 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 15:39:19 +0200 Subject: [PATCH 087/191] Fix style issues --- dnsapi/dns_leaseweb.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 3edf55f..6160991 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -32,7 +32,7 @@ dns_leaseweb_add() { _debug _root_domain "$_domain" _debug _domain "$fulldomain" - if _lsw_api "POST" "$_domain" "$fulldomain" "$txtvalue"; then + if _lsw_api "POST" "$_domain" "$fulldomain" "$txtvalue"; then if [ "$_code" = "201" ]; then _info "Added, OK" return 0 @@ -63,7 +63,7 @@ dns_leaseweb_rm() { _debug _root_domain "$_domain" _debug _domain "$fulldomain" - if _lsw_api "DELETE" "$_domain" "$fulldomain" "$txtvalue"; then + if _lsw_api "DELETE" "$_domain" "$fulldomain" "$txtvalue"; then if [ "$_code" = "204" ]; then _info "Deleted, OK" return 0 @@ -109,16 +109,16 @@ _lsw_api() { export _H2="Content-Type: application/json" export _H1="X-Lsw-Auth: ${LSW_Key}" - if [ "$cmd" == "POST" ]; then + if [ "$cmd" = "POST" ]; then data="{\"name\": \"$fulldomain.\",\"type\": \"TXT\",\"content\": [\"$txtvalue\"],\"ttl\": 60}" - response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" + response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" - _debug "http response code $_code" - _debug response "$response" + _debug "http response code $_code" + _debug response "$response" return 0 fi - if [ "$cmd" == "DELETE" ]; then + if [ "$cmd" = "DELETE" ]; then response="$(_post "" "$LSW_API/$domain/resourceRecordSets/$fulldomain/TXT" "" "DELETE")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" _debug "http response code $_code" @@ -127,4 +127,4 @@ _lsw_api() { fi return 1 -} \ No newline at end of file +} From 400c31d03162a596fcbb22330e38df26b960eac0 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 16:01:51 +0200 Subject: [PATCH 088/191] Fixed another styling issue (trailing spaces) --- dnsapi/dns_leaseweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 6160991..a792290 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -111,7 +111,7 @@ _lsw_api() { if [ "$cmd" = "POST" ]; then data="{\"name\": \"$fulldomain.\",\"type\": \"TXT\",\"content\": [\"$txtvalue\"],\"ttl\": 60}" - response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" + response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" _debug "http response code $_code" _debug response "$response" From 0ac37981cbd384ddfa7ccb890ccf4facb6c396ec Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 16:04:16 +0200 Subject: [PATCH 089/191] Styling, newline removed --- dnsapi/dns_leaseweb.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index a792290..17038f4 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -77,7 +77,6 @@ dns_leaseweb_rm() { return 1 } - #################### Private functions below ################################## # _acme-challenge.www.domain.com # returns From 4a81205e04f22f0de645d117e243794ba6ca403a Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 16:22:48 +0200 Subject: [PATCH 090/191] Styling, trailing space --- dnsapi/dns_leaseweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 17038f4..c9df4dc 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -59,7 +59,7 @@ dns_leaseweb_rm() { _err "invalid domain" return 1 fi - + _debug _root_domain "$_domain" _debug _domain "$fulldomain" From f0d6d46766c8484e32010b2dc624130650900a3c Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Mon, 19 Aug 2019 17:27:19 +0200 Subject: [PATCH 091/191] Added link to API docs --- dnsapi/dns_leaseweb.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index c9df4dc..976ad5a 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -3,6 +3,7 @@ #Author: Rolph Haspers #Utilize leaseweb.com API to finish dns-01 verifications. #Requires a Leaseweb API Key (export LSW_Key="Your Key") +#See http://developer.leaseweb.com for more information. ######## Public functions ##################### LSW_API="https://api.leaseweb.com/hosting/v2/domains/" From 0ca46774ac207517724eb48338c04a4dbde0728a Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Mon, 2 Sep 2019 10:36:10 +0800 Subject: [PATCH 092/191] Create FUNDING.yml --- .github/FUNDING.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..24be2c4 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: acmesh +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 1081d98bf9fac753a504a78af868eca444c73be1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 5 Sep 2019 22:05:54 +0800 Subject: [PATCH 093/191] support to specify the nginx or site conf for nginx mode. https://github.com/Neilpang/acme.sh/issues/2469 --- acme.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/acme.sh b/acme.sh index 72bf070..3ce377e 100755 --- a/acme.sh +++ b/acme.sh @@ -2799,6 +2799,11 @@ _setNginx() { _debug NGINX_CONF "$NGINX_CONF" NGINX_CONF="$(echo "$NGINX_CONF" | cut -d = -f 2)" _debug NGINX_CONF "$NGINX_CONF" + if [ -z "$NGINX_CONF" ]; then + _err "Can not find nginx conf." + NGINX_CONF="" + return 1 + fi if [ ! -f "$NGINX_CONF" ]; then _err "'$NGINX_CONF' doesn't exist." NGINX_CONF="" @@ -6503,6 +6508,10 @@ _process() { ;; --nginx) wvalue="$NGINX" + if [ "$2" ] && ! _startswith "$2" "-"; then + wvalue="$NGINX$2" + shift + fi if [ -z "$_webroot" ]; then _webroot="$wvalue" else From 6b817d4563494e64d40d403c90746c869a3db73a Mon Sep 17 00:00:00 2001 From: Phil Porada Date: Thu, 5 Sep 2019 10:15:28 -0400 Subject: [PATCH 094/191] Set TXT record TTL to minimum possible value (#2465) --- dnsapi/dns_linode_v4.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dnsapi/dns_linode_v4.sh b/dnsapi/dns_linode_v4.sh index c9a83c7..ee7ee89 100755 --- a/dnsapi/dns_linode_v4.sh +++ b/dnsapi/dns_linode_v4.sh @@ -31,7 +31,8 @@ dns_linode_v4_add() { _payload="{ \"type\": \"TXT\", \"name\": \"$_sub_domain\", - \"target\": \"$txtvalue\" + \"target\": \"$txtvalue\", + \"ttl_sec\": 300 }" if _rest POST "/$_domain_id/records" "$_payload" && [ -n "$response" ]; then From 80d63dbb7cef07b14ec05d6d017689c19aec40a2 Mon Sep 17 00:00:00 2001 From: Kent Varmedal Date: Thu, 5 Sep 2019 16:26:28 +0200 Subject: [PATCH 095/191] Add support for Domeneshop DNS API (#2458) * Add support for Domeneshop DNS API * Fix double quotes after build fail * Fixing formating errors --- dnsapi/dns_domeneshop.sh | 155 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 dnsapi/dns_domeneshop.sh diff --git a/dnsapi/dns_domeneshop.sh b/dnsapi/dns_domeneshop.sh new file mode 100644 index 0000000..9a3791f --- /dev/null +++ b/dnsapi/dns_domeneshop.sh @@ -0,0 +1,155 @@ +#!/usr/bin/env sh + +DOMENESHOP_Api_Endpoint="https://api.domeneshop.no/v0" + +##################### Public functions ##################### + +# Usage: dns_domeneshop_add +# Example: dns_domeneshop_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_domeneshop_add() { + fulldomain=$1 + txtvalue=$2 + + # Get token and secret + DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}" + DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}" + + if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then + DOMENESHOP_Token="" + DOMENESHOP_Secret="" + _err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret." + return 1 + fi + + # Save the api token and secret. + _saveaccountconf_mutable DOMENESHOP_Token "$DOMENESHOP_Token" + _saveaccountconf_mutable DOMENESHOP_Secret "$DOMENESHOP_Secret" + + # Get the domain name id + if ! _get_domainid "$fulldomain"; then + _err "Did not find domainname" + return 1 + fi + + # Create record + _domeneshop_rest POST "domains/$_domainid/dns" "{\"type\":\"TXT\",\"host\":\"$_sub_domain\",\"data\":\"$txtvalue\",\"ttl\":120}" +} + +# Usage: dns_domeneshop_rm +# Example: dns_domeneshop_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_domeneshop_rm() { + fulldomain=$1 + txtvalue=$2 + + # Get token and secret + DOMENESHOP_Token="${DOMENESHOP_Token:-$(_readaccountconf_mutable DOMENESHOP_Token)}" + DOMENESHOP_Secret="${DOMENESHOP_Secret:-$(_readaccountconf_mutable DOMENESHOP_Secret)}" + + if [ -z "$DOMENESHOP_Token" ] || [ -z "$DOMENESHOP_Secret" ]; then + DOMENESHOP_Token="" + DOMENESHOP_Secret="" + _err "You need to spesify a Domeneshop/Domainnameshop API Token and Secret." + return 1 + fi + + # Get the domain name id + if ! _get_domainid "$fulldomain"; then + _err "Did not find domainname" + return 1 + fi + + # Find record + if ! _get_recordid "$_domainid" "$_sub_domain" "$txtvalue"; then + _err "Did not find dns record" + return 1 + fi + + # Remove record + _domeneshop_rest DELETE "domains/$_domainid/dns/$_recordid" +} + +##################### Private functions ##################### + +_get_domainid() { + domain=$1 + + # Get domains + _domeneshop_rest GET "domains" + + if ! _contains "$response" "\"id\":"; then + _err "failed to get domain names" + 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" "\"$h\"" >/dev/null; then + # We have found the domain name. + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + _domainid=$(printf "%s" "$response" | _egrep_o "[^{]*\"domain\":\"$_domain\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2) + return 0 + fi + p=$i + i=$(_math "$i" + 1) + done + return 1 +} + +_get_recordid() { + domainid=$1 + subdomain=$2 + txtvalue=$3 + + # Get all dns records for the domainname + _domeneshop_rest GET "domains/$domainid/dns" + + if ! _contains "$response" "\"id\":"; then + _debug "No records in dns" + return 1 + fi + + if ! _contains "$response" "\"host\":\"$subdomain\""; then + _debug "Record does not exist" + return 1 + fi + + # Get the id of the record in question + _recordid=$(printf "%s" "$response" | _egrep_o "[^{]*\"host\":\"$subdomain\"[^}]*" | _egrep_o "[^{]*\"data\":\"$txtvalue\"[^}]*" | _egrep_o "\"id\":[0-9]+" | cut -d : -f 2) + if [ -z "$_recordid" ]; then + return 1 + fi + return 0 +} + +_domeneshop_rest() { + method=$1 + endpoint=$2 + data=$3 + + credentials=$(printf "%b" "$DOMENESHOP_Token:$DOMENESHOP_Secret" | _base64) + + export _H1="Authorization: Basic $credentials" + export _H2="Content-Type: application/json" + + if [ "$method" != "GET" ]; then + response="$(_post "$data" "$DOMENESHOP_Api_Endpoint/$endpoint" "" "$method")" + else + response="$(_get "$DOMENESHOP_Api_Endpoint/$endpoint")" + fi + + if [ "$?" != "0" ]; then + _err "error $endpoint" + return 1 + fi + + return 0 +} From b9994e52eb24e2389ca6e29fc79046d92e57e758 Mon Sep 17 00:00:00 2001 From: fgma <30936930+fgma@users.noreply.github.com> Date: Thu, 5 Sep 2019 16:28:47 +0200 Subject: [PATCH 096/191] Notify xmpp (#2407) * notify via xmpp (using sendxmpp) * fix formatting in notify/xmpp.sh * minor cleanup --- notify/xmpp.sh | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 notify/xmpp.sh diff --git a/notify/xmpp.sh b/notify/xmpp.sh new file mode 100644 index 0000000..580f471 --- /dev/null +++ b/notify/xmpp.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env sh + +#Support xmpp via sendxmpp + +#XMPP_BIN="/usr/bin/sendxmpp" +#XMPP_BIN_ARGS="-n -t --tls-ca-path=/etc/ssl/certs" +#XMPP_TO="zzzz@example.com" + +xmpp_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + XMPP_BIN="${XMPP_BIN:-$(_readaccountconf_mutable XMPP_BIN)}" + if [ -n "$XMPP_BIN" ] && ! _exists "$XMPP_BIN"; then + _err "It seems that the command $XMPP_BIN is not in path." + return 1 + fi + _XMPP_BIN=$(_xmpp_bin) + if [ -n "$XMPP_BIN" ]; then + _saveaccountconf_mutable XMPP_BIN "$XMPP_BIN" + else + _clearaccountconf "XMPP_BIN" + fi + + XMPP_BIN_ARGS="${XMPP_BIN_ARGS:-$(_readaccountconf_mutable XMPP_BIN_ARGS)}" + if [ -n "$XMPP_BIN_ARGS" ]; then + _saveaccountconf_mutable XMPP_BIN_ARGS "$XMPP_BIN_ARGS" + else + _clearaccountconf "XMPP_BIN_ARGS" + fi + + XMPP_TO="${XMPP_TO:-$(_readaccountconf_mutable XMPP_TO)}" + if [ -n "$XMPP_TO" ]; then + if ! _xmpp_valid "$XMPP_TO"; then + _err "It seems that the XMPP_TO=$XMPP_TO is not a valid xmpp address." + return 1 + fi + + _saveaccountconf_mutable XMPP_TO "$XMPP_TO" + fi + + result=$({ _xmpp_message | eval "$(_xmpp_cmnd)"; } 2>&1) + + # shellcheck disable=SC2181 + if [ $? -ne 0 ]; then + _debug "xmpp send error." + _err "$result" + return 1 + fi + + _debug "xmpp send success." + return 0 +} + +_xmpp_bin() { + if [ -n "$XMPP_BIN" ]; then + _XMPP_BIN="$XMPP_BIN" + elif _exists "sendxmpp"; then + _XMPP_BIN="sendxmpp" + else + _err "Please install sendxmpp first." + return 1 + fi + + echo "$_XMPP_BIN" +} + +_xmpp_cmnd() { + case $(basename "$_XMPP_BIN") in + sendxmpp) + echo "'$_XMPP_BIN' '$XMPP_TO' $XMPP_BIN_ARGS" + ;; + *) + _err "Command $XMPP_BIN is not supported, use sendxmpp." + return 1 + ;; + esac +} + +_xmpp_message() { + echo "$_subject" +} + +_xmpp_valid() { + _contains "$1" "@" +} From 815a3be48b5d389eae234a5ffefb5eaa56492813 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 14 Sep 2019 11:21:55 +0800 Subject: [PATCH 097/191] fix https://github.com/Neilpang/acme.sh/issues/2478 support `sudo -i` and `sudo -s` --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 3ce377e..980dc02 100755 --- a/acme.sh +++ b/acme.sh @@ -6246,8 +6246,8 @@ _checkSudo() { #it's root using sudo, no matter it's using sudo or not, just fine return 0 fi - if [ "$SUDO_COMMAND" = "/bin/su" ]; then - #it's a normal user doing "sudo su" + if [ "$SUDO_COMMAND" = "/bin/su" ] || [ "$SUDO_COMMAND" = "/bin/bash" ]; then + #it's a normal user doing "sudo su", or `sudo -i` or `sudo -s` #fine return 0 fi From 950d024a117665bcc31a2417360f7b941e16bad6 Mon Sep 17 00:00:00 2001 From: Boot Lee <82433808@qq.com> Date: Sat, 14 Sep 2019 23:06:25 +0800 Subject: [PATCH 098/191] fix grep error when txt value begin with - char (#2471) --- dnsapi/dns_ali.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_ali.sh b/dnsapi/dns_ali.sh index 543a0a5..0c2365d 100755 --- a/dnsapi/dns_ali.sh +++ b/dnsapi/dns_ali.sh @@ -185,7 +185,7 @@ _clean() { return 1 fi - record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)" + record_id="$(echo "$response" | tr '{' "\n" | grep "$_sub_domain" | grep -- "$txtvalue" | tr "," "\n" | grep RecordId | cut -d '"' -f 4)" _debug2 record_id "$record_id" if [ -z "$record_id" ]; then From 5723fd112fb9238cd09b6cb2737f328d35746eb7 Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 24 Sep 2019 20:00:21 +0800 Subject: [PATCH 099/191] fix HEAD request against the new LE CDN. curl is fixed --- README.md | 2 +- acme.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab3412c..6c6a743 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) -[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. - Support ACME v1 and ACME v2 diff --git a/acme.sh b/acme.sh index 980dc02..2cb577e 100755 --- a/acme.sh +++ b/acme.sh @@ -1697,6 +1697,9 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _CURL="$_CURL --insecure " fi + if [ "$httpmethod" = "HEAD" ]; then + _CURL="$_CURL -I " + fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then if [ "$_postContentType" ]; then From 51b4a9e3509906b86aae4c25a350f8bcdf2d3f8c Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 24 Sep 2019 20:50:24 +0800 Subject: [PATCH 100/191] fix HEAD request against the new LE CDN. wget is fixed --- acme.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/acme.sh b/acme.sh index 2cb577e..cfcd357 100755 --- a/acme.sh +++ b/acme.sh @@ -1727,6 +1727,9 @@ _post() { if [ "$HTTPS_INSECURE" ]; then _WGET="$_WGET --no-check-certificate " fi + if [ "$httpmethod" = "HEAD" ]; then + _WGET="$_WGET --read-timeout=3.0 --tries=2 " + fi _debug "_WGET" "$_WGET" if [ "$needbase64" ]; then if [ "$httpmethod" = "POST" ]; then @@ -1749,6 +1752,12 @@ _post() { else response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi + elif [ "$httpmethod" = "HEAD" ]; then + if [ "$_postContentType" ]; then + response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + else + response="$($_WGET --spider -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + fi else if [ "$_postContentType" ]; then response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" From 1ba4ab2bd1d200962b7d611e3aeefcf2d32cc48b Mon Sep 17 00:00:00 2001 From: neilpang Date: Tue, 24 Sep 2019 22:10:36 +0800 Subject: [PATCH 101/191] fix https://github.com/Neilpang/acme.sh/issues/2503 --- acme.sh | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/acme.sh b/acme.sh index cfcd357..39c07cb 100755 --- a/acme.sh +++ b/acme.sh @@ -1702,16 +1702,32 @@ _post() { fi _debug "_CURL" "$_CURL" if [ "$needbase64" ]; then - if [ "$_postContentType" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + if [ "$body" ]; then + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + fi else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url" | _base64)" + fi fi else - if [ "$_postContentType" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + if [ "$body" ]; then + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + fi else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" "$_post_url")" + fi fi fi _ret="$?" @@ -1890,7 +1906,7 @@ _send_signed_request() { if [ "$ACME_NEW_NONCE" ]; then _debug2 "Get nonce with HEAD. ACME_NEW_NONCE" "$ACME_NEW_NONCE" nonceurl="$ACME_NEW_NONCE" - if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type"; then + if _post "" "$nonceurl" "" "HEAD" "$__request_conent_type" >/dev/null; then _headers="$(cat "$HTTP_HEADER")" _debug2 _headers "$_headers" _CACHED_NONCE="$(echo "$_headers" | grep -i "Replay-Nonce:" | _head_n 1 | tr -d "\r\n " | cut -d ':' -f 2)" From be0df07dfbe5e5fda893450305b9bd1cce3c7ff9 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 28 Sep 2019 10:54:31 +0800 Subject: [PATCH 102/191] fix list() performance https://github.com/Neilpang/acme.sh/issues/2296 --- acme.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/acme.sh b/acme.sh index 39c07cb..0ff24c9 100755 --- a/acme.sh +++ b/acme.sh @@ -4974,18 +4974,14 @@ list() { if [ "$_raw" ]; then printf "%s\n" "Main_Domain${_sep}KeyLength${_sep}SAN_Domains${_sep}Created${_sep}Renew" for di in "${CERT_HOME}"/*.*/; do - if ! [ -d "$di" ]; then - _debug "Not directory, skip: $di" - continue - fi d=$(basename "$di") _debug d "$d" ( if _endswith "$d" "$ECC_SUFFIX"; then - _isEcc=$(echo "$d" | cut -d "$ECC_SEP" -f 2) + _isEcc="ecc" d=$(echo "$d" | cut -d "$ECC_SEP" -f 1) fi - _initpath "$d" "$_isEcc" + DOMAIN_CONF="$di/$d.conf" if [ -f "$DOMAIN_CONF" ]; then . "$DOMAIN_CONF" printf "%s\n" "$Le_Domain${_sep}\"$Le_Keylength\"${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr" From 10eec7d48c11e91e988be335f5bd4989b628aadb Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 3 Oct 2019 20:37:46 +0800 Subject: [PATCH 103/191] support google dns --- acme.sh | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 0ff24c9..bded4ad 100755 --- a/acme.sh +++ b/acme.sh @@ -90,6 +90,9 @@ DEBUG_LEVEL_3=3 DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1 DEBUG_LEVEL_NONE=0 +DOH_CLOUDFLARE=1 +DOH_GOOGLE=2 + HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)" SYSLOG_ERROR="user.error" @@ -3636,7 +3639,7 @@ __trigger_validation() { } #endpoint domain type -_ns_lookup() { +_ns_lookup_impl() { _ns_ep="$1" _ns_domain="$2" _ns_type="$3" @@ -3660,7 +3663,7 @@ _ns_lookup_cf() { _cf_ld="$1" _cf_ld_type="$2" _cf_ep="https://cloudflare-dns.com/dns-query" - _ns_lookup "$_cf_ep" "$_cf_ld" "$_cf_ld_type" + _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type" } #domain, type @@ -3673,6 +3676,44 @@ _ns_purge_cf() { _debug2 response "$response" } +#checks if cf server is available +_ns_is_available_cf() { + if _get "https://cloudflare-dns.com"; then + return 0 + else + return 1 + fi +} + +#domain, type +_ns_lookup_google() { + _cf_ld="$1" + _cf_ld_type="$2" + _cf_ep="https://dns.google/resolve" + _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type" +} + +#domain, type +_ns_lookup() { + if [ -z "$DOH_USE" ]; then + _debug "Detect dns server first." + if _ns_is_available_cf; then + _debug "Use cloudflare doh server" + export DOH_USE=$DOH_CLOUDFLARE + else + _debug "Use google doh server" + export DOH_USE=$DOH_GOOGLE + fi + fi + + if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then + _ns_lookup_cf "$@" + else + _ns_lookup_google "$@" + fi + +} + #txtdomain, alias, txt __check_txt() { _c_txtdomain="$1" @@ -3681,7 +3722,7 @@ __check_txt() { _debug "_c_txtdomain" "$_c_txtdomain" _debug "_c_aliasdomain" "$_c_aliasdomain" _debug "_c_txt" "$_c_txt" - _answers="$(_ns_lookup_cf "$_c_aliasdomain" TXT)" + _answers="$(_ns_lookup "$_c_aliasdomain" TXT)" _contains "$_answers" "$_c_txt" } @@ -3690,7 +3731,13 @@ __check_txt() { __purge_txt() { _p_txtdomain="$1" _debug _p_txtdomain "$_p_txtdomain" - _ns_purge_cf "$_p_txtdomain" "TXT" + if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then + _ns_purge_cf "$_p_txtdomain" "TXT" + else + _debug "no purge api for google dns api, just sleep 5 secs" + _sleep 5 + fi + } #wait and check each dns entries From b4a62bfa300b0d4c95a3f279b626227e50a8f0a5 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 3 Oct 2019 20:51:06 +0800 Subject: [PATCH 104/191] let's start 2.8.4 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index bded4ad..041b5b4 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.3 +VER=2.8.4 PROJECT_NAME="acme.sh" From 477a04760c8558d5385736a6537f3313f2f11b96 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Thu, 3 Oct 2019 21:00:30 +0800 Subject: [PATCH 105/191] support google public dns (#2522) * support google dns * let's start 2.8.4 --- acme.sh | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/acme.sh b/acme.sh index 0ff24c9..041b5b4 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.8.3 +VER=2.8.4 PROJECT_NAME="acme.sh" @@ -90,6 +90,9 @@ DEBUG_LEVEL_3=3 DEBUG_LEVEL_DEFAULT=$DEBUG_LEVEL_1 DEBUG_LEVEL_NONE=0 +DOH_CLOUDFLARE=1 +DOH_GOOGLE=2 + HIDDEN_VALUE="[hidden](please add '--output-insecure' to see this value)" SYSLOG_ERROR="user.error" @@ -3636,7 +3639,7 @@ __trigger_validation() { } #endpoint domain type -_ns_lookup() { +_ns_lookup_impl() { _ns_ep="$1" _ns_domain="$2" _ns_type="$3" @@ -3660,7 +3663,7 @@ _ns_lookup_cf() { _cf_ld="$1" _cf_ld_type="$2" _cf_ep="https://cloudflare-dns.com/dns-query" - _ns_lookup "$_cf_ep" "$_cf_ld" "$_cf_ld_type" + _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type" } #domain, type @@ -3673,6 +3676,44 @@ _ns_purge_cf() { _debug2 response "$response" } +#checks if cf server is available +_ns_is_available_cf() { + if _get "https://cloudflare-dns.com"; then + return 0 + else + return 1 + fi +} + +#domain, type +_ns_lookup_google() { + _cf_ld="$1" + _cf_ld_type="$2" + _cf_ep="https://dns.google/resolve" + _ns_lookup_impl "$_cf_ep" "$_cf_ld" "$_cf_ld_type" +} + +#domain, type +_ns_lookup() { + if [ -z "$DOH_USE" ]; then + _debug "Detect dns server first." + if _ns_is_available_cf; then + _debug "Use cloudflare doh server" + export DOH_USE=$DOH_CLOUDFLARE + else + _debug "Use google doh server" + export DOH_USE=$DOH_GOOGLE + fi + fi + + if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then + _ns_lookup_cf "$@" + else + _ns_lookup_google "$@" + fi + +} + #txtdomain, alias, txt __check_txt() { _c_txtdomain="$1" @@ -3681,7 +3722,7 @@ __check_txt() { _debug "_c_txtdomain" "$_c_txtdomain" _debug "_c_aliasdomain" "$_c_aliasdomain" _debug "_c_txt" "$_c_txt" - _answers="$(_ns_lookup_cf "$_c_aliasdomain" TXT)" + _answers="$(_ns_lookup "$_c_aliasdomain" TXT)" _contains "$_answers" "$_c_txt" } @@ -3690,7 +3731,13 @@ __check_txt() { __purge_txt() { _p_txtdomain="$1" _debug _p_txtdomain "$_p_txtdomain" - _ns_purge_cf "$_p_txtdomain" "TXT" + if [ "$DOH_USE" = "$DOH_CLOUDFLARE" ] || [ -z "$DOH_USE" ]; then + _ns_purge_cf "$_p_txtdomain" "TXT" + else + _debug "no purge api for google dns api, just sleep 5 secs" + _sleep 5 + fi + } #wait and check each dns entries From 683592fa867ef034afea8036096f96f4f58ea00a Mon Sep 17 00:00:00 2001 From: jess Date: Thu, 3 Oct 2019 06:01:05 -0700 Subject: [PATCH 106/191] Added financial contributors to the README (#2513) --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c6a743..faaf9aa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) - [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. - Support ACME v1 and ACME v2 @@ -451,6 +451,36 @@ TODO: 2. ACME protocol: https://github.com/ietf-wg-acme/acme +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)] + +#### Individuals + + + +#### Organizations + +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)] + + + + + + + + + + + + # 19. License & Others License is GPLv3 From 8ef5daa8070d8c8e2d71b366f14d498c27a74261 Mon Sep 17 00:00:00 2001 From: neilpang Date: Thu, 3 Oct 2019 21:14:11 +0800 Subject: [PATCH 107/191] minor, update link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index faaf9aa..d5012d6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) - [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. - Support ACME v1 and ACME v2 From 54143ae6d40ba8845de392ab8350c525f47a9417 Mon Sep 17 00:00:00 2001 From: neil <8305679+Neilpang@users.noreply.github.com> Date: Thu, 3 Oct 2019 21:15:32 +0800 Subject: [PATCH 108/191] sync (#2523) sync --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index faaf9aa..d5012d6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) - [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. - Support ACME v1 and ACME v2 From 72d800ed1098a77f565c14691f164be8ccab969b Mon Sep 17 00:00:00 2001 From: Michael Braunoeder Date: Sat, 5 Oct 2019 05:47:57 +0200 Subject: [PATCH 109/191] [DNSAPI] add dns_rcode0.sh - Support for https://my.rcodezero.at/api-doc (#2489) * first version dns_rcode0.sh * fixed URLs for ACME calls * fixed challenge remove * read & write Token/URL at rm too * make info messages debug * typos fixed * update rrset only if existing challenge is found * polish error messages and make "detect root zone" scaleable * fixed formating issues * code cleanup, remove some unneeded functions * removed empty lines * save rcode0 url only if not default --- dnsapi/dns_rcode0.sh | 224 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100755 dnsapi/dns_rcode0.sh diff --git a/dnsapi/dns_rcode0.sh b/dnsapi/dns_rcode0.sh new file mode 100755 index 0000000..9ed20e6 --- /dev/null +++ b/dnsapi/dns_rcode0.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env sh + +#Rcode0 API Integration +#https://my.rcodezero.at/api-doc +# +# log into https://my.rcodezero.at/enableapi and get your ACME API Token (the ACME API token has limited +# access to the REST calls needed for acme.sh only) +# +#RCODE0_URL="https://my.rcodezero.at" +#RCODE0_API_TOKEN="0123456789ABCDEF" +#RCODE0_TTL=60 + +DEFAULT_RCODE0_URL="https://my.rcodezero.at" +DEFAULT_RCODE0_TTL=60 + +######## Public functions ##################### +#Usage: add _acme-challenge.www.domain.com "123456789ABCDEF0000000000000000000000000000000000000" +#fulldomain +#txtvalue +dns_rcode0_add() { + fulldomain=$1 + txtvalue=$2 + + RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}" + RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}" + RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}" + + if [ -z "$RCODE0_URL" ]; then + RCODE0_URL="$DEFAULT_RCODE0_URL" + fi + + if [ -z "$RCODE0_API_TOKEN" ]; then + RCODE0_API_TOKEN="" + _err "Missing Rcode0 ACME API Token." + _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again." + return 1 + fi + + if [ -z "$RCODE0_TTL" ]; then + RCODE0_TTL="$DEFAULT_RCODE0_TTL" + fi + + #save the token to the account conf file. + _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN" + + if [ "$RCODE0_URL" != "$DEFAULT_RCODE0_URL" ]; then + _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL" + fi + + if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then + _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL" + fi + + _debug "Detect root zone" + if ! _get_root "$fulldomain"; then + _err "No 'MASTER' zone for $fulldomain found at RcodeZero Anycast." + return 1 + fi + _debug _domain "$_domain" + + _debug "Adding record" + + _record_string="" + _build_record_string "$txtvalue" + _list_existingchallenges + for oldchallenge in $_existing_challenges; do + _build_record_string "$oldchallenge" + done + + _debug "Challenges: $_existing_challenges" + + if [ -z "$_existing_challenges" ]; then + if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"add\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then + _err "Add txt record error." + return 1 + fi + else + # try update in case a records exists (need for wildcard certs) + if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then + _err "Set txt record error." + return 1 + fi + fi + + return 0 +} + +#fulldomain txtvalue +dns_rcode0_rm() { + fulldomain=$1 + txtvalue=$2 + + RCODE0_API_TOKEN="${RCODE0_API_TOKEN:-$(_readaccountconf_mutable RCODE0_API_TOKEN)}" + RCODE0_URL="${RCODE0_URL:-$(_readaccountconf_mutable RCODE0_URL)}" + RCODE0_TTL="${RCODE0_TTL:-$(_readaccountconf_mutable RCODE0_TTL)}" + + if [ -z "$RCODE0_URL" ]; then + RCODE0_URL="$DEFAULT_RCODE0_URL" + fi + + if [ -z "$RCODE0_API_TOKEN" ]; then + RCODE0_API_TOKEN="" + _err "Missing Rcode0 API Token." + _err "Please login and create your token at httsp://my.rcodezero.at/enableapi and try again." + return 1 + fi + + #save the api addr and key to the account conf file. + _saveaccountconf_mutable RCODE0_URL "$RCODE0_URL" + _saveaccountconf_mutable RCODE0_API_TOKEN "$RCODE0_API_TOKEN" + + if [ "$RCODE0_TTL" != "$DEFAULT_RCODE0_TTL" ]; then + _saveaccountconf_mutable RCODE0_TTL "$RCODE0_TTL" + fi + + if [ -z "$RCODE0_TTL" ]; then + RCODE0_TTL="$DEFAULT_RCODE0_TTL" + fi + + _debug "Detect root zone" + if ! _get_root "$fulldomain"; then + _err "invalid domain" + return 1 + fi + + _debug "Remove record" + + #Enumerate existing acme challenges + _list_existingchallenges + + if _contains "$_existing_challenges" "$txtvalue"; then + #Delete all challenges (PowerDNS API does not allow to delete content) + if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"delete\", \"name\": \"$fulldomain.\", \"type\": \"TXT\"}]"; then + _err "Delete txt record error." + return 1 + fi + _record_string="" + #If the only existing challenge was the challenge to delete: nothing to do + if ! [ "$_existing_challenges" = "$txtvalue" ]; then + for oldchallenge in $_existing_challenges; do + #Build up the challenges to re-add, ommitting the one what should be deleted + if ! [ "$oldchallenge" = "$txtvalue" ]; then + _build_record_string "$oldchallenge" + fi + done + #Recreate the existing challenges + if ! _rcode0_rest "PATCH" "/api/v1/acme/zones/$_domain/rrsets" "[{\"changetype\": \"update\", \"name\": \"$fulldomain.\", \"type\": \"TXT\", \"ttl\": $RCODE0_TTL, \"records\": [$_record_string]}]"; then + _err "Set txt record error." + return 1 + fi + fi + else + _info "Record not found, nothing to remove" + fi + + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _domain=domain.com +_get_root() { + domain=$1 + i=1 + + while true; do + h=$(printf "%s" "$domain" | cut -d . -f $i-100) + + _debug "try to find: $h" + if _rcode0_rest "GET" "/api/v1/acme/zones/$h"; then + if [ "$response" = "[\"found\"]" ]; then + _domain="$h" + if [ -z "$h" ]; then + _domain="=2E" + fi + return 0 + elif [ "$response" = "[\"not a master domain\"]" ]; then + return 1 + fi + fi + + if [ -z "$h" ]; then + return 1 + fi + i=$(_math $i + 1) + done + _debug "no matching domain for $domain found" + + return 1 +} + +_rcode0_rest() { + method=$1 + ep=$2 + data=$3 + + export _H1="Authorization: Bearer $RCODE0_API_TOKEN" + + if [ ! "$method" = "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$RCODE0_URL$ep" "" "$method")" + else + response="$(_get "$RCODE0_URL$ep")" + fi + + if [ "$?" != "0" ]; then + _err "error $ep" + return 1 + fi + _debug2 response "$response" + + return 0 +} + +_build_record_string() { + _record_string="${_record_string:+${_record_string}, }{\"content\": \"\\\"${1}\\\"\", \"disabled\": false}" +} + +_list_existingchallenges() { + _rcode0_rest "GET" "/api/v1/acme/zones/$_domain/rrsets" + _existing_challenges=$(echo "$response" | _normalizeJson | _egrep_o "\"name\":\"${fulldomain}[^]]*}" | _egrep_o 'content\":\"\\"[^\\]*' | sed -n 's/^content":"\\"//p') + _debug2 "$_existing_challenges" +} From 1e7534b9d7a4e629f46c6eb9995db20fcf962d80 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 5 Oct 2019 11:59:04 +0800 Subject: [PATCH 110/191] fix https://github.com/Neilpang/acme.sh/issues/2518#issuecomment-538474232 --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 041b5b4..cac16d3 100755 --- a/acme.sh +++ b/acme.sh @@ -3678,7 +3678,7 @@ _ns_purge_cf() { #checks if cf server is available _ns_is_available_cf() { - if _get "https://cloudflare-dns.com"; then + if _get "https://cloudflare-dns.com" >/dev/null 2>&1; then return 0 else return 1 From ac9f6e3a4135dbc008c4e22af7f649d139690918 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sat, 5 Oct 2019 21:06:58 +0800 Subject: [PATCH 111/191] Remove trailing spaces in text files This issue in the shell scripts will also be detected in the stable version of shfmt(we are currently using an ancient pre-release of shfmt) --- .github/ISSUE_TEMPLATE.md | 2 +- .travis.yml | 4 ++-- acme.sh | 4 ++-- deploy/qiniu.sh | 2 +- deploy/routeros.sh | 2 +- deploy/vault_cli.sh | 4 ++-- dnsapi/dns_da.sh | 2 +- dnsapi/dns_doapi.sh | 6 +++--- dnsapi/dns_durabledns.sh | 10 +++++----- dnsapi/dns_euserv.sh | 2 +- dnsapi/dns_freedns.sh | 2 +- dnsapi/dns_me.sh | 2 +- dnsapi/dns_namecheap.sh | 6 +++--- dnsapi/dns_nsupdate.sh | 4 ++-- dnsapi/dns_rcode0.sh | 2 +- 15 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 189155e..53112c6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -5,7 +5,7 @@ 如何调试 https://github.com/Neilpang/acme.sh/wiki/How-to-debug-acme.sh If it is a bug report: -- make sure you are able to repro it on the latest released version. +- make sure you are able to repro it on the latest released version. You can install the latest version by: `acme.sh --upgrade` - Search the existing issues. diff --git a/.travis.yml b/.travis.yml index 04de193..e77eb32 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,5 +34,5 @@ script: matrix: fast_finish: true - - + + diff --git a/acme.sh b/acme.sh index cac16d3..e060e33 100755 --- a/acme.sh +++ b/acme.sh @@ -178,7 +178,7 @@ _printargs() { printf -- "%s" "$1='$2'" fi printf "\n" - # return the saved exit status + # return the saved exit status return "$_exitstatus" } @@ -6215,7 +6215,7 @@ Parameters: --branch, -b Only valid for '--upgrade' command, specifies the branch name to upgrade to. --notify-level 0|1|2|3 Set the notification level: Default value is $NOTIFY_LEVEL_DEFAULT. - 0: disabled, no notification will be sent. + 0: disabled, no notification will be sent. 1: send notifications only when there is an error. 2: send notifications when a cert is successfully renewed, or there is an error. 3: send notifications when a cert is skipped, renewed, or error. diff --git a/deploy/qiniu.sh b/deploy/qiniu.sh index e46e6fb..13b0965 100644 --- a/deploy/qiniu.sh +++ b/deploy/qiniu.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -# Script to create certificate to qiniu.com +# Script to create certificate to qiniu.com # # This deployment required following variables # export QINIU_AK="QINIUACCESSKEY" diff --git a/deploy/routeros.sh b/deploy/routeros.sh index 21c9196..70fe70a 100644 --- a/deploy/routeros.sh +++ b/deploy/routeros.sh @@ -85,7 +85,7 @@ routeros_deploy() { scp "$_ckey" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.key" _info "Trying to push cert '$_cfullchain' to router" scp "$_cfullchain" "$ROUTER_OS_USERNAME@$ROUTER_OS_HOST:$_cdomain.cer" - DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive + DEPLOY_SCRIPT_CMD="/system script add name=\"LE Cert Deploy - $_cdomain\" owner=admin policy=ftp,read,write,password,sensitive source=\"## generated by routeros deploy script in acme.sh \n/certificate remove [ find name=$_cdomain.cer_0 ] \n/certificate remove [ find name=$_cdomain.cer_1 ] diff --git a/deploy/vault_cli.sh b/deploy/vault_cli.sh index b93fdd5..5395d87 100644 --- a/deploy/vault_cli.sh +++ b/deploy/vault_cli.sh @@ -2,10 +2,10 @@ # Here is a script to deploy cert to hashicorp vault # (https://www.vaultproject.io/) -# +# # it requires the vault binary to be available in PATH, and the following # environment variables: -# +# # VAULT_PREFIX - this contains the prefix path in vault # VAULT_ADDR - vault requires this to find your vault server # diff --git a/dnsapi/dns_da.sh b/dnsapi/dns_da.sh index 7755c7e..4e9c4ef 100755 --- a/dnsapi/dns_da.sh +++ b/dnsapi/dns_da.sh @@ -9,7 +9,7 @@ # # User must provide login data and URL to DirectAdmin incl. port. # You can create login key, by using the Login Keys function -# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to +# ( https://da.example.com:8443/CMD_LOGIN_KEYS ), which only has access to # - CMD_API_DNS_CONTROL # - CMD_API_SHOW_DOMAINS # diff --git a/dnsapi/dns_doapi.sh b/dnsapi/dns_doapi.sh index 135f0b0..a001d52 100755 --- a/dnsapi/dns_doapi.sh +++ b/dnsapi/dns_doapi.sh @@ -1,11 +1,11 @@ #!/usr/bin/env sh # Official Let's Encrypt API for do.de / Domain-Offensive -# +# # This is different from the dns_do adapter, because dns_do is only usable for enterprise customers # This API is also available to private customers/individuals -# -# Provide the required LetsEncrypt token like this: +# +# Provide the required LetsEncrypt token like this: # DO_LETOKEN="FmD408PdqT1E269gUK57" DO_API="https://www.do.de/api/letsencrypt" diff --git a/dnsapi/dns_durabledns.sh b/dnsapi/dns_durabledns.sh index 9a05eb3..677ae24 100644 --- a/dnsapi/dns_durabledns.sh +++ b/dnsapi/dns_durabledns.sh @@ -147,11 +147,11 @@ _dd_soap() { # build SOAP XML _xml=' - '"$body"' ' diff --git a/dnsapi/dns_euserv.sh b/dnsapi/dns_euserv.sh index 3810156..cfb4b81 100644 --- a/dnsapi/dns_euserv.sh +++ b/dnsapi/dns_euserv.sh @@ -127,7 +127,7 @@ dns_euserv_rm() { else # find XML block where txtvalue is in. The record_id is allways prior this line! _endLine=$(echo "$response" | grep -n '>dns_record_content<.*>'"$txtvalue"'<' | cut -d ':' -f 1) - # record_id is the last Tag with a number before the row _endLine, identified by + # record_id is the last Tag with a number before the row _endLine, identified by _record_id=$(echo "$response" | sed -n '1,'"$_endLine"'p' | grep '' | _tail_n 1 | sed 's/.*\([0-9]*\)<\/name>.*/\1/') _info "Deleting record" _euserv_delete_record "$_record_id" diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e76e649..29b1892 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -359,7 +359,7 @@ _freedns_data_id() { # before each table row # search for the record type withing each row (e.g. TXT) # search for the domain within each row (which is within a - # anchor. And finally extract the domain ID. + # anchor. And finally extract the domain ID. if [ -n "$data_id" ]; then printf "%s" "$data_id" return 0 diff --git a/dnsapi/dns_me.sh b/dnsapi/dns_me.sh index 382eeed..98a5841 100644 --- a/dnsapi/dns_me.sh +++ b/dnsapi/dns_me.sh @@ -2,7 +2,7 @@ # bug reports to dev@1e.ca -# ME_Key=qmlkdjflmkqdjf +# ME_Key=qmlkdjflmkqdjf # ME_Secret=qmsdlkqmlksdvnnpae ME_Api=https://api.dnsmadeeasy.com/V2.0/dns/managed diff --git a/dnsapi/dns_namecheap.sh b/dnsapi/dns_namecheap.sh index a82e12d..2e38926 100755 --- a/dnsapi/dns_namecheap.sh +++ b/dnsapi/dns_namecheap.sh @@ -3,10 +3,10 @@ # Namecheap API # https://www.namecheap.com/support/api/intro.aspx # -# Requires Namecheap API key set in -#NAMECHEAP_API_KEY, +# Requires Namecheap API key set in +#NAMECHEAP_API_KEY, #NAMECHEAP_USERNAME, -#NAMECHEAP_SOURCEIP +#NAMECHEAP_SOURCEIP # Due to Namecheap's API limitation all the records of your domain will be read and re applied, make sure to have a backup of your records you could apply if any issue would arise. ######## Public functions ##################### diff --git a/dnsapi/dns_nsupdate.sh b/dnsapi/dns_nsupdate.sh index dfb3672..cd4b714 100755 --- a/dnsapi/dns_nsupdate.sh +++ b/dnsapi/dns_nsupdate.sh @@ -27,7 +27,7 @@ dns_nsupdate_add() { [ -n "$DEBUG" ] && [ "$DEBUG" -ge "$DEBUG_LEVEL_2" ] && nsdebug="-D" if [ -z "${NSUPDATE_ZONE}" ]; then nsupdate -k "${NSUPDATE_KEY}" $nsdebug < Date: Sat, 5 Oct 2019 21:13:23 +0800 Subject: [PATCH 112/191] Use shallow clone to speed up git clone on Travis CI Shallow clone is faster than a normal one, there is no need to clone the whole history of a repository when we only needs its latest or certain state of commit. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 04de193..5f0ce0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ script: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -V ; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then shellcheck -e SC2181 **/*.sh && echo "shellcheck OK" ; fi - cd .. - - git clone https://github.com/Neilpang/acmetest.git && cp -r acme.sh acmetest/ && cd acmetest + - git clone --depth 1 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" ./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 From bc396e7a90e05704eddf12d728809e72ce77d5dd Mon Sep 17 00:00:00 2001 From: Vadim Kalinnikov Date: Sun, 6 Oct 2019 14:38:26 +0300 Subject: [PATCH 113/191] Small fix in dns_vultr.sh --- dnsapi/dns_vultr.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh index f15e7c4..0dce2ca 100644 --- a/dnsapi/dns_vultr.sh +++ b/dnsapi/dns_vultr.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env sh +#!/bin/bash # #VULTR_API_KEY=000011112222333344445555666677778888 @@ -106,9 +106,9 @@ _get_root() { domain=$1 i=1 while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) - _debug h "$h" - if [ -z "$h" ]; then + _domain=$(printf "%s" "$domain" | cut -d . -f $i-100) + _debug h "$_domain" + if [ -z "$_domain" ]; then return 1 fi @@ -119,11 +119,9 @@ _get_root() { if printf "%s\n" "$response" | grep '^\[.*\]' >/dev/null; then if _contains "$response" "\"domain\":\"$_domain\""; then _sub_domain="$(echo "$fulldomain" | sed "s/\\.$_domain\$//")" - _domain=$_domain return 0 else - _err 'Invalid domain' - return 1 + _debug "Go to next level of $_domain" fi else _err "$response" From e484f32b1abe2fea12a4b5fb9d13e7eb23996f9a Mon Sep 17 00:00:00 2001 From: Vadim Kalinnikov Date: Sun, 6 Oct 2019 14:40:57 +0300 Subject: [PATCH 114/191] - Return shell detect via env --- dnsapi/dns_vultr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_vultr.sh b/dnsapi/dns_vultr.sh index 0dce2ca..c7b52e8 100644 --- a/dnsapi/dns_vultr.sh +++ b/dnsapi/dns_vultr.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env sh # #VULTR_API_KEY=000011112222333344445555666677778888 From 65c950e1a41562b9eca0b23b04a260b18c3335d4 Mon Sep 17 00:00:00 2001 From: MooSE <32853697+moose-kazan@users.noreply.github.com> Date: Sun, 6 Oct 2019 15:02:48 +0300 Subject: [PATCH 115/191] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5012d6..faaf9aa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) - [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - An ACME protocol client written purely in Shell (Unix shell) language. - Full ACME protocol implementation. - Support ACME v1 and ACME v2 From f500c7abcba29d19b2d49d8e3b25d9c6d5e2f726 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 15:47:39 +0200 Subject: [PATCH 116/191] dnsapi/dns_miab.sh MIAB DNS-01 Validation Know I'm new to contorting to this project. I i've broke conventions please let me know what I've screwed up and I'll set it right as quickly as possible. Propose this as a new DNS-01 validation script to dynamically add challenge DNS records to MailinaBox (MIAB) DNS. MIAB uses a custom DNS API to manage external DNS records. The script was originally written by Darven Dissek and can be found in his repository: https://framagit.org/DarvenDissek/acme.sh-MIAB-DNS-API/). This has been forked and some slight cleanup applied and change shebang to UNIx shell. The forked repository can be found here: https://github.com/billgertz/MIAB_dns_api. Wrote to Darven but received no reply. Support for this script has been submitted to the OPNsense project via this pull request: https://github.com/opnsense/plugins/pull/1531 --- dnsapi/dns_miab.sh | 273 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 dnsapi/dns_miab.sh diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh new file mode 100644 index 0000000..b0a52d7 --- /dev/null +++ b/dnsapi/dns_miab.sh @@ -0,0 +1,273 @@ +#!/usr/bin/env sh + +#Name: dns_miab.sh +# +#Authors: +# Darven Dissek 2018 +# William Gertz 2019 +# +# Thanks to Neil Pang for the code reused from acme.sh from HTTP-01 validation +# used to communicate with the MailintheBox Custom DNS API +#Report Bugs here: +# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh) +# https://github.com/Neilpang/acme.sh (for acme.sh) +# +######## Public functions ##################### + +#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_miab_add() { + fulldomain=$1 + txtvalue=$2 + _info "Using miab" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}" + MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}" + MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}" + + #debug log the environmental variables + _debug MIAB_Username "$MIAB_Username" + _debug MIAB_Password "$MIAB_Password" + _debug MIAB_Server "$MIAB_Server" + + if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then + MIAB_Username="" + MIAB_Password="" + MIAB_Server="" + _err "You didn't specify MIAB_Username or MIAB_Password or MIAB_Server." + _err "Please try again." + return 1 + fi + + #save the credentials to the account conf file. + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + + baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" + + #Add the challenge record + result="$(_miab_post "$txtvalue" "$baseurl" "" "POST" "" "$MIAB_Username" "$MIAB_Password")" + + _debug result "$result" + + #check if result was good + if _contains "$result" "updated DNS"; then + _info "Successfully created the txt record" + return 0 + else + _err "Error encountered during record addition" + _err "$result" + return 1 + fi + +} + +#Usage: fulldomain txtvalue +#Remove the txt record after validation. +dns_miab_rm() { + fulldomain=$1 + txtvalue=$2 + _info "Using miab" + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}" + MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}" + MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}" + + #debug log the environmental variables + _debug MIAB_Username "$MIAB_Username" + _debug MIAB_Password "$MIAB_Password" + _debug MIAB_Server "$MIAB_Server" + + if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then + MIAB_Username="" + MIAB_Password="" + MIAB_Server="" + _err "You didn't specify MIAB_Username or MIAB_Password or MIAB_Server." + _err "Please try again." + return 1 + fi + + #save the credentials to the account conf file. + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + + baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" + + #Remove the challenge record + result="$(_miab_post "$txtvalue" "$baseurl" "" "DELETE" "" "$MIAB_Username" "$MIAB_Password")" + + _debug result $result + + #check if result was good + if _contains "$result" "updated DNS"; then + _info "Successfully created the txt record" + return 0 + else + _err "Error encountered during record addition" + _err "$result" + return 1 + 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 _contains "$response" "\"name\":\"$h\"" >/dev/null; 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 +} + +# post changes to MIAB dns (taken from acme.sh) +_miab_post() { + body="$1" + _post_url="$2" + needbase64="$3" + httpmethod="$4" + _postContentType="$5" + username="$6" + password="$7" + + if [ -z "$httpmethod" ]; then + httpmethod="POST" + fi + + _debug $httpmethod + _debug "_post_url" "$_post_url" + _debug2 "body" "$body" + _debug2 "_postContentType" "$_postContentType" + + _inithttp + + if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then + _CURL="$_ACME_CURL" + + if [ "$HTTPS_INSECURE" ]; then + _CURL="$_CURL --insecure " + fi + + _debug "_CURL" "$_CURL" + + if [ "$needbase64" ]; then + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" + fi + else + if [ "$_postContentType" ]; then + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + else + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" + fi + fi + + _ret="$?" + + if [ "$_ret" != "0" ]; then + _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret" + if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then + _err "Here is the curl dump log:" + _err "$(cat "$_CURL_DUMP")" + fi + fi + + elif [ "$_ACME_WGET" ]; then + _WGET="$_ACME_WGET" + + if [ "$HTTPS_INSECURE" ]; then + _WGET="$_WGET --no-check-certificate " + fi + + _debug "_WGET" "$_WGET" + + if [ "$needbase64" ]; then + + if [ "$httpmethod" = "POST" ]; then + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + fi + else + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" + fi + fi + + else + + if [ "$httpmethod" = "POST" ]; then + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + fi + else + if [ "$_postContentType" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + else + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" + fi + fi + + fi + + _ret="$?" + + if [ "$_ret" = "8" ]; then + _ret=0 + _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later." + fi + + if [ "$_ret" != "0" ]; then + _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" + fi + + _sed_i "s/^ *//g" "$HTTP_HEADER" + + else + _ret="$?" + _err "Neither curl nor wget was found, cannot do $httpmethod." + fi + + _debug "_ret" "$_ret" + printf "%s" "$response" + return $_ret +} From 47c33d0344208d0bb47f173d64672e69fc18ac37 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 16:29:23 +0200 Subject: [PATCH 117/191] Cleanup/ removed private function _get_root Function _get_root() copied from acme.sh and is not needed here. Other cleanup as recommended by acme.sh test bot. --- dnsapi/dns_miab.sh | 71 +++++++++++----------------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index b0a52d7..b68f670 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -1,16 +1,16 @@ #!/usr/bin/env sh -#Name: dns_miab.sh +# Name: dns_miab.sh # -#Authors: -# Darven Dissek 2018 -# William Gertz 2019 +# Authors: +# Darven Dissek 2018 +# William Gertz 2019 # -# Thanks to Neil Pang for the code reused from acme.sh from HTTP-01 validation -# used to communicate with the MailintheBox Custom DNS API -#Report Bugs here: -# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh) -# https://github.com/Neilpang/acme.sh (for acme.sh) +# Thanks to Neil Pang for the code reused from acme.sh from HTTP-01 validation +# used to communicate with the MailintheBox Custom DNS API +# Report Bugs here: +# https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh) +# https://github.com/Neilpang/acme.sh (for acme.sh) # ######## Public functions ##################### @@ -41,9 +41,9 @@ dns_miab_add() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" @@ -61,7 +61,6 @@ dns_miab_add() { _err "$result" return 1 fi - } #Usage: fulldomain txtvalue @@ -92,16 +91,16 @@ dns_miab_rm() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" #Remove the challenge record result="$(_miab_post "$txtvalue" "$baseurl" "" "DELETE" "" "$MIAB_Username" "$MIAB_Password")" - _debug result $result + _debug result "$result" #check if result was good if _contains "$result" "updated DNS"; then @@ -115,43 +114,7 @@ dns_miab_rm() { } #################### 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 _contains "$response" "\"name\":\"$h\"" >/dev/null; 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 -} - +# # post changes to MIAB dns (taken from acme.sh) _miab_post() { body="$1" From a4ec9f8b44a0ae2a22c4af44d423b58e73fa6fdf Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 16:34:56 +0200 Subject: [PATCH 118/191] Fixed weird spacing on line 180 Um, fixed. --- dnsapi/dns_miab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index b68f670..c91bf3c 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -180,7 +180,7 @@ _miab_post() { if [ "$needbase64" ]; then - if [ "$httpmethod" = "POST" ]; then + if [ "$httpmethod" = "POST" ]; then if [ "$_postContentType" ]; then response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" else From 835f9aad91e9995e688b1be8e827f0a6443af746 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 16:47:32 +0200 Subject: [PATCH 119/191] Um that's a wee bit of nit pick. 'Errant' space removed on blank line on line 147. --- dnsapi/dns_miab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index c91bf3c..8786634 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -144,7 +144,7 @@ _miab_post() { fi _debug "_CURL" "$_CURL" - + if [ "$needbase64" ]; then if [ "$_postContentType" ]; then response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" From c06ec7c6bae0cc40daede2121d006b764e73cb47 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 18:15:16 +0200 Subject: [PATCH 120/191] Removed parameters and unused code for _miab_post Ok, should have noticed earlier that the calls to the private function _miab_post() never used the _needbase64_ or the __postContentType parameters. Parameters and code to handle them has been factored out. --- dnsapi/dns_miab.sh | 70 +++++++++------------------------------------- 1 file changed, 13 insertions(+), 57 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index 8786634..df2ca6e 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -48,7 +48,7 @@ dns_miab_add() { baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" #Add the challenge record - result="$(_miab_post "$txtvalue" "$baseurl" "" "POST" "" "$MIAB_Username" "$MIAB_Password")" + result="$(_miab_post "$txtvalue" "$baseurl" "POST" "$MIAB_Username" "$MIAB_Password")" _debug result "$result" @@ -91,14 +91,14 @@ dns_miab_rm() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" #Remove the challenge record - result="$(_miab_post "$txtvalue" "$baseurl" "" "DELETE" "" "$MIAB_Username" "$MIAB_Password")" + result="$(_miab_post "$txtvalue" "$baseurl" "DELETE" "$MIAB_Username" "$MIAB_Password")" _debug result "$result" @@ -119,11 +119,9 @@ dns_miab_rm() { _miab_post() { body="$1" _post_url="$2" - needbase64="$3" - httpmethod="$4" - _postContentType="$5" - username="$6" - password="$7" + httpmethod="$3" + username="$4" + password="$5" if [ -z "$httpmethod" ]; then httpmethod="POST" @@ -144,21 +142,7 @@ _miab_post() { fi _debug "_CURL" "$_CURL" - - if [ "$needbase64" ]; then - if [ "$_postContentType" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" - else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url" | _base64)" - fi - else - if [ "$_postContentType" ]; then - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "Content-Type: $_postContentType" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" - else - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" - fi - fi - + response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" _ret="$?" if [ "$_ret" != "0" ]; then @@ -178,40 +162,12 @@ _miab_post() { _debug "_WGET" "$_WGET" - if [ "$needbase64" ]; then - - if [ "$httpmethod" = "POST" ]; then - if [ "$_postContentType" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" - else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" - fi - else - if [ "$_postContentType" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" - else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER" | _base64)" - fi - fi - + if [ "$httpmethod" = "POST" ]; then + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" else - - if [ "$httpmethod" = "POST" ]; then - if [ "$_postContentType" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - fi - else - if [ "$_postContentType" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --header "Content-Type: $_postContentType" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - fi - fi - + response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi - + _ret="$?" if [ "$_ret" = "8" ]; then From f323ced4ca0d46c4119a8c4ac3ce67125edce149 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 18:24:14 +0200 Subject: [PATCH 121/191] Style issues and orphan _postContentType debug fix Fixed spacing and removed unneeded debug for _postContenetType --- dnsapi/dns_miab.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index df2ca6e..e2f4d59 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -91,9 +91,9 @@ dns_miab_rm() { fi #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" @@ -130,8 +130,7 @@ _miab_post() { _debug $httpmethod _debug "_post_url" "$_post_url" _debug2 "body" "$body" - _debug2 "_postContentType" "$_postContentType" - + _inithttp if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then @@ -167,7 +166,7 @@ _miab_post() { else response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" fi - + _ret="$?" if [ "$_ret" = "8" ]; then From f64b061a28bf06f7f1586048615cef090b9c09e9 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Tue, 8 Oct 2019 18:46:35 +0200 Subject: [PATCH 122/191] Style issue Spaces on blank line on line 133. --- dnsapi/dns_miab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index e2f4d59..d17a1f7 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -130,7 +130,7 @@ _miab_post() { _debug $httpmethod _debug "_post_url" "$_post_url" _debug2 "body" "$body" - + _inithttp if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then From ba7db3edda2c3d4e8265d2c5302b973d6541afd8 Mon Sep 17 00:00:00 2001 From: David Robles Date: Wed, 9 Oct 2019 08:08:05 -0700 Subject: [PATCH 123/191] Use more widely supported options for the "tr" command line utility by removing the use of the character class representation option. Fixes #2536 --- dnsapi/dns_freedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index e76e649..bb80dc4 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -303,7 +303,7 @@ _freedns_domain_id() { return 1 fi - domain_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ + domain_id="$(echo "$htmlpage" | tr -d "\r\n" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ | _egrep_o "edit\.php\?edit_domain_id=[0-9a-zA-Z]+" \ | cut -d = -f 2)" @@ -349,7 +349,7 @@ _freedns_data_id() { return 1 fi - data_id="$(echo "$htmlpage" | tr -d "[:space:]" | sed 's//@/g' | tr '@' '\n' \ + data_id="$(echo "$htmlpage" | tr -d "\r\n" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ | _egrep_o "edit\.php\?data_id=[0-9a-zA-Z]+" \ From 252a21e2ae715885e5c45044fe19538e6b009399 Mon Sep 17 00:00:00 2001 From: temoffey Date: Thu, 10 Oct 2019 00:36:34 +0300 Subject: [PATCH 124/191] fixed json parse regex for support api gcore_cdn --- deploy/gcore_cdn.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deploy/gcore_cdn.sh b/deploy/gcore_cdn.sh index bbda58e..a2a35f7 100644 --- a/deploy/gcore_cdn.sh +++ b/deploy/gcore_cdn.sh @@ -77,15 +77,15 @@ gcore_cdn_deploy() { _debug _regex "$_regex" _resource=$(echo "$_response" | sed 's/},{/},\n{/g' | _egrep_o "$_regex") _debug _resource "$_resource" - _regex=".*\"id\":\([0-9]*\),.*$" + _regex=".*\"id\":\([0-9]*\).*\"rules\".*$" _debug _regex "$_regex" _resourceId=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _resourceId "$_resourceId" - _regex=".*\"sslData\":\([0-9]*\)}.*$" + _regex=".*\"sslData\":\([0-9]*\).*$" _debug _regex "$_regex" _sslDataOld=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _sslDataOld "$_sslDataOld" - _regex=".*\"originGroup\":\([0-9]*\),.*$" + _regex=".*\"originGroup\":\([0-9]*\).*$" _debug _regex "$_regex" _originGroup=$(echo "$_resource" | sed -n "s/$_regex/\1/p") _debug _originGroup "$_originGroup" @@ -101,7 +101,7 @@ gcore_cdn_deploy() { _debug _request "$_request" _response=$(_post "$_request" "https://api.gcdn.co/sslData") _debug _response "$_response" - _regex=".*\"id\":\([0-9]*\),.*$" + _regex=".*\"id\":\([0-9]*\).*$" _debug _regex "$_regex" _sslDataAdd=$(echo "$_response" | sed -n "s/$_regex/\1/p") _debug _sslDataAdd "$_sslDataAdd" From aa6112482d90e17b19127b71d5f12d097e13c485 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Sun, 13 Oct 2019 19:56:04 +0200 Subject: [PATCH 125/191] Rewrite to conform to Dev guide Created _get_root() that tests the requested host is a subdomain to the domains hosted on MailinaBox (MIAB) DNS Server. Created common _miab_rest() used with dns_miab_add(), dns_miab_rm() and _get_root(). Also created barbaric _is_json() to test the response given by the MIAB Custom DNS API at least looks like a JSON file. We should add a hint to use _normalizeJson with JSON responses so _startswith, _endswith won't perplexingly fail. --- dnsapi/dns_miab.sh | 257 ++++++++++++++++++++++++--------------------- 1 file changed, 139 insertions(+), 118 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index d17a1f7..313e4eb 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -6,186 +6,207 @@ # Darven Dissek 2018 # William Gertz 2019 # -# Thanks to Neil Pang for the code reused from acme.sh from HTTP-01 validation -# used to communicate with the MailintheBox Custom DNS API +# Thanks to Neil Pang and other developers here for code reused from acme.sh from DNS-01 +# used to communicate with the MailinaBox Custom DNS API # Report Bugs here: # https://github.com/billgertz/MIAB_dns_api (for dns_miab.sh) # https://github.com/Neilpang/acme.sh (for acme.sh) # ######## Public functions ##################### -#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +#Usage: dns_miab_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_miab_add() { fulldomain=$1 txtvalue=$2 - _info "Using miab" + _info "Using miab challange add" _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" - MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}" - MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}" - MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}" - - #debug log the environmental variables - _debug MIAB_Username "$MIAB_Username" - _debug MIAB_Password "$MIAB_Password" - _debug MIAB_Server "$MIAB_Server" - - if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then - MIAB_Username="" - MIAB_Password="" - MIAB_Server="" - _err "You didn't specify MIAB_Username or MIAB_Password or MIAB_Server." - _err "Please try again." + #retrieve MIAB environemt vars + if ! _retrieve_miab_env; then + return 1 + fi + + #check domain and seperate into doamin and host + if ! _get_root "$fulldomain"; then + _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" return 1 fi - #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" - - baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" - - #Add the challenge record - result="$(_miab_post "$txtvalue" "$baseurl" "POST" "$MIAB_Username" "$MIAB_Password")" + _debug2 _sub_domain "$_sub_domain" + _debug2 _domain "$_domain" - _debug result "$result" + #add the challenge record + _api_path="custom/${fulldomain}/txt" + _miab_rest "$txtvalue" "$_api_path" "POST" #check if result was good - if _contains "$result" "updated DNS"; then + if _contains "$response" "updated DNS"; then _info "Successfully created the txt record" return 0 else - _err "Error encountered during record addition" - _err "$result" + _err "Error encountered during record add" + _err "$response" return 1 fi } -#Usage: fulldomain txtvalue -#Remove the txt record after validation. +#Usage: dns_miab_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_miab_rm() { fulldomain=$1 txtvalue=$2 - _info "Using miab" + + _info "Using miab challage delete" _debug fulldomain "$fulldomain" _debug txtvalue "$txtvalue" - MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}" - MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}" - MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}" - - #debug log the environmental variables - _debug MIAB_Username "$MIAB_Username" - _debug MIAB_Password "$MIAB_Password" - _debug MIAB_Server "$MIAB_Server" + #retrieve MIAB environemt vars + if ! _retrieve_miab_env; then + return 1 + fi - if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then - MIAB_Username="" - MIAB_Password="" - MIAB_Server="" - _err "You didn't specify MIAB_Username or MIAB_Password or MIAB_Server." - _err "Please try again." + #check domain and seperate into doamin and host + if ! _get_root "$fulldomain"; then + _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" return 1 fi - #save the credentials to the account conf file. - _saveaccountconf_mutable MIAB_Username "$MIAB_Username" - _saveaccountconf_mutable MIAB_Password "$MIAB_Password" - _saveaccountconf_mutable MIAB_Server "$MIAB_Server" - - baseurl="https://$MIAB_Server/admin/dns/custom/$fulldomain/txt" + _debug2 _sub_domain "$_sub_domain" + _debug2 _domain "$_domain" #Remove the challenge record - result="$(_miab_post "$txtvalue" "$baseurl" "DELETE" "$MIAB_Username" "$MIAB_Password")" - - _debug result "$result" + _api_path="custom/${fulldomain}/txt" + _miab_rest "$txtvalue" "$_api_path" "DELETE" #check if result was good - if _contains "$result" "updated DNS"; then - _info "Successfully created the txt record" + if _contains "$response" "updated DNS"; then + _info "Successfully removed the txt record" return 0 else - _err "Error encountered during record addition" - _err "$result" + _err "Error encountered during record remove" + _err "$response" return 1 fi } #################### Private functions below ################################## # -# post changes to MIAB dns (taken from acme.sh) -_miab_post() { - body="$1" - _post_url="$2" - httpmethod="$3" - username="$4" - password="$5" - - if [ -z "$httpmethod" ]; then - httpmethod="POST" +#Usage: _get_root _acme-challenge.www.domain.com +#Returns: +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + _passed_domain=$1 + _debug _passed_domain "$_passed_domain" + _i=2 + _p=1 + + #get the zones hosed on MIAB server, must be a json stream + _miab_rest "" "zones" "GET" + + _info "_startswith test:$(_startswith "test" "t")" + _info "_endstest test:$(_endswith "test" "t")" + + if ! _is_json "$response"; then + _err "ERROR fetching domain list" + _err "$response" + return 1 fi - _debug $httpmethod - _debug "_post_url" "$_post_url" - _debug2 "body" "$body" - - _inithttp - - if [ "$_ACME_CURL" ] && [ "${ACME_USE_WGET:-0}" = "0" ]; then - _CURL="$_ACME_CURL" + #cycle through the passed domain seperating out a test domain discarding + # the subdomain by marching thorugh the dots + while true; do + _test_domain=$(printf "%s" "$_passed_domain" | cut -d . -f ${_i}-100) + _debug _test_domain "$_test_domain" - if [ "$HTTPS_INSECURE" ]; then - _CURL="$_CURL --insecure " + if [ -z "$_test_domain" ]; then + return 1 fi - _debug "_CURL" "$_CURL" - response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod --user "$username:$password" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" -H "$_H5" --data "$body" "$_post_url")" - _ret="$?" - - if [ "$_ret" != "0" ]; then - _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret" - if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ]; then - _err "Here is the curl dump log:" - _err "$(cat "$_CURL_DUMP")" - fi + #report found if the test domain is in the json response and + # report the subdomain + if _contains "$response" "\"$_test_domain\""; then + _sub_domain=$(printf "%s" "$_passed_domain" | cut -d . -f 1-${_p}) + _domain=${_test_domain} + return 0 fi - elif [ "$_ACME_WGET" ]; then - _WGET="$_ACME_WGET" + #cycle to the next dot in the passed domain + _p=${_i} + _i=$(_math "$_i" + 1) + done - if [ "$HTTPS_INSECURE" ]; then - _WGET="$_WGET --no-check-certificate " - fi + return 1 +} - _debug "_WGET" "$_WGET" +#Usage: _retrieve_miab_env +#Returns (from store or environment variables): +# MIAB_Username +# MIAB_Password +# MIAB_Server +#retrieve MIAB environment variables, report errors and quit if problems +_retrieve_miab_env() { + MIAB_Username="${MIAB_Username:-$(_readaccountconf_mutable MIAB_Username)}" + MIAB_Password="${MIAB_Password:-$(_readaccountconf_mutable MIAB_Password)}" + MIAB_Server="${MIAB_Server:-$(_readaccountconf_mutable MIAB_Server)}" - if [ "$httpmethod" = "POST" ]; then - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - else - response="$($_WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$_post_url" 2>"$HTTP_HEADER")" - fi + #debug log the environmental variables + _debug MIAB_Username "$MIAB_Username" + _debug MIAB_Password "$MIAB_Password" + _debug MIAB_Server "$MIAB_Server" - _ret="$?" + #check if MIAB environemt vars set and quit if not + if [ -z "$MIAB_Username" ] || [ -z "$MIAB_Password" ] || [ -z "$MIAB_Server" ]; then + _err "You didn't specify one or more of MIAB_Username, MIAB_Password or MIAB_Server." + _err "Please check these environment variables and try again." + return 1 + fi - if [ "$_ret" = "8" ]; then - _ret=0 - _debug "wget returns 8, the server returns a 'Bad request' response, lets process the response later." - fi + #save the credentials to the account conf file. + _saveaccountconf_mutable MIAB_Username "$MIAB_Username" + _saveaccountconf_mutable MIAB_Password "$MIAB_Password" + _saveaccountconf_mutable MIAB_Server "$MIAB_Server" +} - if [ "$_ret" != "0" ]; then - _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret" - fi +#Useage: _miab_rest "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" "custom/_acme-challenge.www.domain.com/txt "POST" +#Returns: "updated DNS: domain.com" +#rest interface MIAB dns +_miab_rest() { + _data="$1" + _api_path="$2" + _httpmethod="$3" + + #encode username and password for url + _username="$(printf "%s" "$MIAB_Username" | _url_encode)" + _password="$(printf "%s" "$MIAB_Password" | _url_encode)" + _url="https://${_username}:${_password}@${MIAB_Server}/admin/dns/${_api_path}" + + _debug2 _data "$_data" + _debug _api_path "$_api_path" + _debug2 _url "$_url" + _debug _httpmethod "$_httpmethod" + + if [ "$_httpmethod" = "GET" ]; then + response="$(_get "$_url")" + else + response="$(_post "$_data" "$_url" "" "$_httpmethod")" + fi - _sed_i "s/^ *//g" "$HTTP_HEADER" + _retcode="$?" - else - _ret="$?" - _err "Neither curl nor wget was found, cannot do $httpmethod." + if [ "$_retcode" != "0" ]; then + _err "MAAB REST authentication failed on $_httpmethod" + return 1 fi - _debug "_ret" "$_ret" - printf "%s" "$response" - return $_ret + _debug response "$response" + return 0 +} + +#Usage: _is_json "\[\n "mydomain.com"\n]" +#Reurns "\[\n "mydomain.com"\n]" +#returns the string if it begins and ends with square braces +_is_json() { + _str="$(echo "$1" | _normalizeJson)" + echo "$_str" | grep '^\[.*\]$' >/dev/null 2>&1 } From 7ec52145e807fc15dfb6c1e501183f14b58f3d80 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Sun, 13 Oct 2019 20:02:03 +0200 Subject: [PATCH 126/191] Space style changes. Local copy of shellcheck somehow missed these, odd. --- dnsapi/dns_miab.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index 313e4eb..7630a74 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -23,10 +23,10 @@ dns_miab_add() { _debug txtvalue "$txtvalue" #retrieve MIAB environemt vars - if ! _retrieve_miab_env; then - return 1 - fi - + if ! _retrieve_miab_env; then + return 1 + fi + #check domain and seperate into doamin and host if ! _get_root "$fulldomain"; then _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" @@ -61,9 +61,9 @@ dns_miab_rm() { _debug txtvalue "$txtvalue" #retrieve MIAB environemt vars - if ! _retrieve_miab_env; then - return 1 - fi + if ! _retrieve_miab_env; then + return 1 + fi #check domain and seperate into doamin and host if ! _get_root "$fulldomain"; then @@ -76,7 +76,7 @@ dns_miab_rm() { #Remove the challenge record _api_path="custom/${fulldomain}/txt" - _miab_rest "$txtvalue" "$_api_path" "DELETE" + _miab_rest "$txtvalue" "$_api_path" "DELETE" #check if result was good if _contains "$response" "updated DNS"; then From 9af85f5a7eedb7d3fd36a01834492e50e8c65138 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Mon, 14 Oct 2019 00:01:25 +0200 Subject: [PATCH 127/191] Updated to use _H1 Authorization: Basic Updated to use suggested export _H1 env var to supply Authorization Basic credentials. This undocumented support for Basic Authorization, ContentType, etc. needs to be documented in DNSAPI Dev Guide. Removed two stray debugging lines. --- dnsapi/dns_miab.sh | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index 7630a74..25a8ffc 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -26,7 +26,7 @@ dns_miab_add() { if ! _retrieve_miab_env; then return 1 fi - + #check domain and seperate into doamin and host if ! _get_root "$fulldomain"; then _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" @@ -104,9 +104,6 @@ _get_root() { #get the zones hosed on MIAB server, must be a json stream _miab_rest "" "zones" "GET" - _info "_startswith test:$(_startswith "test" "t")" - _info "_endstest test:$(_endswith "test" "t")" - if ! _is_json "$response"; then _err "ERROR fetching domain list" _err "$response" @@ -176,14 +173,15 @@ _miab_rest() { _api_path="$2" _httpmethod="$3" - #encode username and password for url - _username="$(printf "%s" "$MIAB_Username" | _url_encode)" - _password="$(printf "%s" "$MIAB_Password" | _url_encode)" - _url="https://${_username}:${_password}@${MIAB_Server}/admin/dns/${_api_path}" + #encode username and password for basic authentication + _credentials="$(printf "%s" "$MIAB_Username:$MIAB_Password" | _base64)" + export _H1="Authorization: Basic $_credentials" + _url="https://${MIAB_Server}/admin/dns/${_api_path}" _debug2 _data "$_data" _debug _api_path "$_api_path" _debug2 _url "$_url" + _debug2 _credentails "$_credentials" _debug _httpmethod "$_httpmethod" if [ "$_httpmethod" = "GET" ]; then @@ -195,7 +193,7 @@ _miab_rest() { _retcode="$?" if [ "$_retcode" != "0" ]; then - _err "MAAB REST authentication failed on $_httpmethod" + _err "MIAB REST authentication failed on $_httpmethod" return 1 fi From 933d49b0b09cc886402c59e08de1651e8121d822 Mon Sep 17 00:00:00 2001 From: Bill Gertz Date: Mon, 14 Oct 2019 00:06:08 +0200 Subject: [PATCH 128/191] Style space change Extra space on empty line 27. --- dnsapi/dns_miab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_miab.sh b/dnsapi/dns_miab.sh index 25a8ffc..23ff6ce 100644 --- a/dnsapi/dns_miab.sh +++ b/dnsapi/dns_miab.sh @@ -26,7 +26,7 @@ dns_miab_add() { if ! _retrieve_miab_env; then return 1 fi - + #check domain and seperate into doamin and host if ! _get_root "$fulldomain"; then _err "Cannot find any part of ${fulldomain} is hosted on ${MIAB_Server}" From dc5c220e8fc0d605a9c4434b421b9d33960b149c Mon Sep 17 00:00:00 2001 From: rserpent <53250916+rserpent@users.noreply.github.com> Date: Wed, 16 Oct 2019 15:12:21 +0500 Subject: [PATCH 129/191] dns_nic init --- dnsapi/dns_nic.sh | 185 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 dnsapi/dns_nic.sh diff --git a/dnsapi/dns_nic.sh b/dnsapi/dns_nic.sh new file mode 100644 index 0000000..277cc2d --- /dev/null +++ b/dnsapi/dns_nic.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +#NIC_Token="sdfsdfsdfljlbjkljlkjsdfoiwjedfglgkdlfgkfgldfkg" +# +#NIC_Username="000000/NIC-D" + +#NIC_Password="xxxxxxx" + +NIC_Api="https://api.nic.ru" + +dns_nic_add() { + fulldomain="${1}" + txtvalue="${2}" + + NIC_Token="${NIC_Token:-$(_readaccountconf_mutable NIC_Token)}" + NIC_Username="${NIC_Username:-$(_readaccountconf_mutable NIC_Username)}" + NIC_Password="${NIC_Password:-$(_readaccountconf_mutable NIC_Password)}" + if [ -z "$NIC_Token" ] || [ -z "$NIC_Username" ] || [ -z "$NIC_Password" ]; then + NIC_Token="" + NIC_Username="" + NIC_Password="" + _err "You must export variables: NIC_Token, NIC_Username and NIC_Password" + return 1 + fi + + _saveaccountconf_mutable NIC_Customer "$NIC_Token" + _saveaccountconf_mutable NIC_Username "$NIC_Username" + _saveaccountconf_mutable NIC_Password "$NIC_Password" + + if ! _nic_get_authtoken "$NIC_Username" "$NIC_Password" "$NIC_Token"; then + _err "get NIC auth token failed" + 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" + _debug _service "$_service" + + _info "Adding record" + if ! _nic_rest PUT "services/$_service/zones/$_domain/records" "$_sub_domainTXT$txtvalue"; then + _err "Add TXT record error" + return 1 + fi + + if ! _nic_rest POST "services/$_service/zones/$_domain/commit" ""; then + return 1 + fi + _info "Added, OK" +} + +dns_nic_rm() { + fulldomain="${1}" + txtvalue="${2}" + + NIC_Token="${NIC_Token:-$(_readaccountconf_mutable NIC_Token)}" + NIC_Username="${NIC_Username:-$(_readaccountconf_mutable NIC_Username)}" + NIC_Password="${NIC_Password:-$(_readaccountconf_mutable NIC_Password)}" + if [ -z "$NIC_Token" ] || [ -z "$NIC_Username" ] || [ -z "$NIC_Password" ]; then + NIC_Token="" + NIC_Username="" + NIC_Password="" + _err "You must export variables: NIC_Token, NIC_Username and NIC_Password" + return 1 + fi + + if ! _nic_get_authtoken "$NIC_Username" "$NIC_Password" "$NIC_Token"; then + _err "get NIC auth token failed" + return 1 + fi + + if ! _get_root "$fulldomain"; then + _err "Invalid domain" + return 1 + fi + + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + _debug _service "$_service" + + if ! _nic_rest GET "services/$_service/zones/$_domain/records"; then + _err "Get records error" + return 1 + fi + + _domain_id=$(printf "%s" "$response" | grep "$_sub_domain" | grep "$txtvalue" | sed -r "s/.*"; then + error=$(printf "%s" "$response" | grep "error code" | sed -r "s/.*(.*)<\/error>/\1/g") + _err "Error: $error" + return 1 + fi + + if ! _contains "$response" "success"; then + return 1 + fi + _debug2 response "$response" + return 0 +} From e00f0b4cf1df691c4baf0293d49d380bf98b5e94 Mon Sep 17 00:00:00 2001 From: rserpent <53250916+rserpent@users.noreply.github.com> Date: Wed, 16 Oct 2019 15:31:50 +0500 Subject: [PATCH 130/191] Update dns_nic.sh --- dnsapi/dns_nic.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_nic.sh b/dnsapi/dns_nic.sh index 277cc2d..b92d2ac 100644 --- a/dnsapi/dns_nic.sh +++ b/dnsapi/dns_nic.sh @@ -113,7 +113,7 @@ _nic_get_authtoken() { export _H1="Authorization: Basic $token" export _H2="Content-Type: application/x-www-form-urlencoded" - res="$(_post "grant_type=password&username=$username&password=$password&scope=%28GET%7CPUT%7CPOST%7CDELETE%29%3A%2Fdns-master%2F.%2B" "$NIC_Api/oauth/token" "" "POST")" + res=$(_post "grant_type=password&username=$username&password=$password&scope=%28GET%7CPUT%7CPOST%7CDELETE%29%3A%2Fdns-master%2F.%2B" "$NIC_Api/oauth/token" "" "POST") if _contains "$res" "access_token"; then _auth_token=$(printf "%s" "$res" | cut -d , -f2 | tr -d "\"" | sed "s/access_token://") _info "Token received" From ffa5472b31b69cedae6e29bc10edf689176d54a0 Mon Sep 17 00:00:00 2001 From: rserpent <53250916+rserpent@users.noreply.github.com> Date: Wed, 16 Oct 2019 16:25:38 +0500 Subject: [PATCH 131/191] fix whitespaces --- dnsapi/dns_nic.sh | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/dnsapi/dns_nic.sh b/dnsapi/dns_nic.sh index b92d2ac..493b05b 100644 --- a/dnsapi/dns_nic.sh +++ b/dnsapi/dns_nic.sh @@ -79,7 +79,7 @@ dns_nic_rm() { _err "Invalid domain" return 1 fi - + _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" _debug _service "$_service" @@ -129,28 +129,28 @@ _get_root() { p=1 if ! _nic_rest GET "zones"; then - return 1 + return 1 fi _all_domains=$(printf "%s" "$response" | grep "idn-name" | sed -r "s/.*idn-name=\"(.*)\" name=.*/\1/g") _debug2 _all_domains "$_all_domains" while true; do - h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) - _debug h "$h" - - if [ -z "$h" ]; then - return 1 - fi - - if _contains "$_all_domains" "^$h$"; then - _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) - _domain=$h - _service=$(printf "%s" "$response" | grep "$_domain" | sed -r "s/.*service=\"(.*)\".*$/\1/") - return 0 - fi - p="$i" - i=$(_math "$i" + 1) + h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + _debug h "$h" + + if [ -z "$h" ]; then + return 1 + fi + + if _contains "$_all_domains" "^$h$"; then + _sub_domain=$(printf "%s" "$domain" | cut -d . -f 1-$p) + _domain=$h + _service=$(printf "%s" "$response" | grep "$_domain" | sed -r "s/.*service=\"(.*)\".*$/\1/") + return 0 + fi + p="$i" + i=$(_math "$i" + 1) done return 1 } @@ -165,20 +165,20 @@ _nic_rest() { export _H2="Authorization: Bearer $_auth_token" if [ "$m" != "GET" ]; then - _debug data "$data" - response=$(_post "$data" "$NIC_Api/dns-master/$ep" "" "$m") + _debug data "$data" + response=$(_post "$data" "$NIC_Api/dns-master/$ep" "" "$m") else - response=$(_get "$NIC_Api/dns-master/$ep") + response=$(_get "$NIC_Api/dns-master/$ep") fi if _contains "$response" ""; then - error=$(printf "%s" "$response" | grep "error code" | sed -r "s/.*(.*)<\/error>/\1/g") - _err "Error: $error" - return 1 + error=$(printf "%s" "$response" | grep "error code" | sed -r "s/.*(.*)<\/error>/\1/g") + _err "Error: $error" + return 1 fi if ! _contains "$response" "success"; then - return 1 + return 1 fi _debug2 response "$response" return 0 From 573c8f3b13e002cc948adfee840005cc18982098 Mon Sep 17 00:00:00 2001 From: David Robles Date: Wed, 23 Oct 2019 07:20:01 -0700 Subject: [PATCH 132/191] Use more widely supported options for the "tr" command line utility by removing the use of the character class representation option. [:space:] => "\t\r\n\v\f" --- dnsapi/dns_freedns.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 81b1de5..6fac0c2 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -303,7 +303,7 @@ _freedns_domain_id() { return 1 fi - domain_id="$(echo "$htmlpage" | tr -d "\r\n" | sed 's//@/g' | tr '@' '\n' \ + domain_id="$(echo "$htmlpage" | tr -d "\t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ | sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' \ | cut -d = -f 2)" @@ -349,7 +349,7 @@ _freedns_data_id() { return 1 fi - data_id="$(echo "$htmlpage" | tr -d "\r\n" | sed 's//@/g' | tr '@' '\n' \ + data_id="$(echo "$htmlpage" | tr -d "\t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ | sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' \ From 18ad01533b3b5d0cf51f9e72464940e7a080d880 Mon Sep 17 00:00:00 2001 From: neil Date: Thu, 24 Oct 2019 09:19:18 +0800 Subject: [PATCH 133/191] add space. fix https://github.com/Neilpang/acme.sh/pull/2553 --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 6fac0c2..32d240f 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -303,7 +303,7 @@ _freedns_domain_id() { return 1 fi - domain_id="$(echo "$htmlpage" | tr -d "\t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ + domain_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ | grep "$search_domain\|$search_domain(.*)" \ | sed -n 's/.*\(edit\.php?edit_domain_id=[0-9a-zA-Z]*\).*/\1/p' \ | cut -d = -f 2)" From 1d1f61613c539eaa0eddf8b10e8a1dea47824b8a Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 09:25:29 +0200 Subject: [PATCH 134/191] Check for root domain via API --- dnsapi/dns_leaseweb.sh | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 976ad5a..6a75ef3 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -88,14 +88,24 @@ _get_root() { i=$(_math "$i" - 1) while true; do - h=$(printf "%s" "$domain" | cut -d . -f "$i"-100) + h=$(printf "%s" "$domain" | cut -d . -f $i-100) if [ -z "$h" ]; then - return 1 + return 1 #not valid domain + fi + + #Check API if domain exists + if _lsw_api "GET" "$h"; then + if [ "$_code" = "200"]; then + _domain="$h" + return 0 + fi + fi + i=$(_math "$i" - 1) + if (( $i < 1)); then + return 1 #not found fi - _domain="$h" - return 0 done - _debug "$domain not found" + return 1 } @@ -109,6 +119,14 @@ _lsw_api() { export _H2="Content-Type: application/json" export _H1="X-Lsw-Auth: ${LSW_Key}" + if [ "$cmd" = "GET" ]; then + response="$(_get "$LSW_API/$domain")" + _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" + _debug "http response code $_code" + _debug response "$response" + return 0 + fi + if [ "$cmd" = "POST" ]; then data="{\"name\": \"$fulldomain.\",\"type\": \"TXT\",\"content\": [\"$txtvalue\"],\"ttl\": 60}" response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" From e10f447b5b6b56c8742136f1c288dce32c392f41 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 11:42:15 +0200 Subject: [PATCH 135/191] Fixed some bugs, tested and working --- dnsapi/dns_leaseweb.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 6a75ef3..cb49ce7 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -83,19 +83,20 @@ dns_leaseweb_rm() { # returns # _domain=domain.com _get_root() { - domain=$1 - i="$(echo "$fulldomain" | tr '.' ' ' | wc -w)" + rdomain=$1 + i="$(echo "$rdomain" | tr '.' ' ' | wc -w)" i=$(_math "$i" - 1) while true; do - h=$(printf "%s" "$domain" | cut -d . -f $i-100) + h=$(printf "%s" "$rdomain" | cut -d . -f $i-100) + _debug h "$h" if [ -z "$h" ]; then return 1 #not valid domain fi #Check API if domain exists if _lsw_api "GET" "$h"; then - if [ "$_code" = "200"]; then + if [ "$_code" = "200" ]; then _domain="$h" return 0 fi @@ -111,16 +112,16 @@ _get_root() { _lsw_api() { cmd=$1 - domain=$2 - fulldomain=$3 - txtvalue=$4 + data=$2 + fd=$3 + tvalue=$4 # Construct the HTTP Authorization header export _H2="Content-Type: application/json" export _H1="X-Lsw-Auth: ${LSW_Key}" if [ "$cmd" = "GET" ]; then - response="$(_get "$LSW_API/$domain")" + response="$(_get "$LSW_API/$d")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" _debug "http response code $_code" _debug response "$response" @@ -128,8 +129,8 @@ _lsw_api() { fi if [ "$cmd" = "POST" ]; then - data="{\"name\": \"$fulldomain.\",\"type\": \"TXT\",\"content\": [\"$txtvalue\"],\"ttl\": 60}" - response="$(_post "$data" "$LSW_API/$domain/resourceRecordSets" "$data" "POST")" + data="{\"name\": \"$fd.\",\"type\": \"TXT\",\"content\": [\"$tvalue\"],\"ttl\": 60}" + response="$(_post "$data" "$LSW_API/$d/resourceRecordSets" "$data" "POST")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" _debug "http response code $_code" _debug response "$response" @@ -137,7 +138,7 @@ _lsw_api() { fi if [ "$cmd" = "DELETE" ]; then - response="$(_post "" "$LSW_API/$domain/resourceRecordSets/$fulldomain/TXT" "" "DELETE")" + response="$(_post "" "$LSW_API/$d/resourceRecordSets/$fd/TXT" "" "DELETE")" _code="$(grep "^HTTP" "$HTTP_HEADER" | _tail_n 1 | cut -d " " -f 2 | tr -d "\\r\\n")" _debug "http response code $_code" _debug response "$response" From 14f6f9ec94a5d0e68f495fe485610db82b6eefc0 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 11:56:27 +0200 Subject: [PATCH 136/191] Fixed wrong assignement of var --- dnsapi/dns_leaseweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index cb49ce7..31446be 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -112,7 +112,7 @@ _get_root() { _lsw_api() { cmd=$1 - data=$2 + d=$2 fd=$3 tvalue=$4 From 6d62ae226a82c8c42129a1bae560495790e092d4 Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 12:14:53 +0200 Subject: [PATCH 137/191] Small fix --- dnsapi/dns_leaseweb.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 31446be..72f53b2 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -102,8 +102,8 @@ _get_root() { fi fi i=$(_math "$i" - 1) - if (( $i < 1)); then - return 1 #not found + if (( i < 2 )); then + return 1 #not found, no need to check _acme-challenge.sub.domain in leaseweb api. fi done From 58642286c95fa42d3e78754a9f0253fa70f529bb Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 13:22:19 +0200 Subject: [PATCH 138/191] Fix for SC2039/SC2086 --- dnsapi/dns_leaseweb.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 72f53b2..0fd8dcc 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -88,7 +88,7 @@ _get_root() { i=$(_math "$i" - 1) while true; do - h=$(printf "%s" "$rdomain" | cut -d . -f $i-100) + h=$(printf "%s" "$rdomain" | cut -d . -f "$i"-100) _debug h "$h" if [ -z "$h" ]; then return 1 #not valid domain @@ -102,7 +102,7 @@ _get_root() { fi fi i=$(_math "$i" - 1) - if (( i < 2 )); then + if $(( i < 2 )); then return 1 #not found, no need to check _acme-challenge.sub.domain in leaseweb api. fi done From e48daffad99af7cd09b0c5860b439de2895541ca Mon Sep 17 00:00:00 2001 From: Rolph Haspers Date: Fri, 25 Oct 2019 13:46:10 +0200 Subject: [PATCH 139/191] Fixed error --- dnsapi/dns_leaseweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_leaseweb.sh b/dnsapi/dns_leaseweb.sh index 0fd8dcc..a1d9e74 100644 --- a/dnsapi/dns_leaseweb.sh +++ b/dnsapi/dns_leaseweb.sh @@ -102,7 +102,7 @@ _get_root() { fi fi i=$(_math "$i" - 1) - if $(( i < 2 )); then + if [ "$i" -lt 2 ]; then return 1 #not found, no need to check _acme-challenge.sub.domain in leaseweb api. fi done From d04c6dd3ac03ff6031cbb0d8a2d86645fe9adb20 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 25 Oct 2019 22:31:36 +0800 Subject: [PATCH 140/191] fix https://github.com/Neilpang/acme.sh/issues/2557 and https://github.com/Neilpang/acme.sh/issues/2544 --- acme.sh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index e060e33..37ce15a 100755 --- a/acme.sh +++ b/acme.sh @@ -4047,7 +4047,18 @@ $_authorizations_map" fi if [ "$ACME_VERSION" = "2" ]; then - response="$(echo "$_authorizations_map" | grep "^$(_idn "$d")," | sed "s/$d,//")" + _idn_d="$(_idn "$d")" + _candindates="$(echo "$_authorizations_map" | grep "^$_idn_d,")" + _debug2 _candindates "$_candindates" + if [ "$(echo "$_candindates" | wc -l)" -gt 1 ]; then + for _can in $_candindates; do + if _startswith "$(echo "$_can" | tr '.' '|')" "$(echo "$_idn_d" | tr '.' '|'),"; then + _candindates="$_can" + break + fi + done + fi + response="$(echo "$_candindates" | sed "s/$_idn_d,//")" _debug2 "response" "$response" if [ -z "$response" ]; then _err "get to authz error." From 2a2877231268cce11ccc624c007ecf3fc2c8dea6 Mon Sep 17 00:00:00 2001 From: neilpang Date: Fri, 25 Oct 2019 22:34:33 +0800 Subject: [PATCH 141/191] fix https://github.com/Neilpang/acme.sh/pull/2553#issuecomment-546173277 --- dnsapi/dns_freedns.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_freedns.sh b/dnsapi/dns_freedns.sh index 32d240f..6a0b58a 100755 --- a/dnsapi/dns_freedns.sh +++ b/dnsapi/dns_freedns.sh @@ -349,7 +349,7 @@ _freedns_data_id() { return 1 fi - data_id="$(echo "$htmlpage" | tr -d "\t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ + data_id="$(echo "$htmlpage" | tr -d " \t\r\n\v\f" | sed 's//@/g' | tr '@' '\n' \ | grep "$record_type" \ | grep "$search_domain" \ | sed -n 's/.*\(edit\.php?data_id=[0-9a-zA-Z]*\).*/\1/p' \ From df3575217acc5c50d45145672a3008cdb5895cdc Mon Sep 17 00:00:00 2001 From: scottkof Date: Fri, 25 Oct 2019 12:05:15 -0700 Subject: [PATCH 142/191] Avoid API throttling errors in AWS DNS plugin --- dnsapi/dns_aws.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 246f477..54b1bb3 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -6,6 +6,8 @@ #AWS_SECRET_ACCESS_KEY="xxxxxxx" #This is the Amazon Route53 api wrapper for acme.sh +#All `sleep` commands are included to avoid Route53 throttling, see +#https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests AWS_HOST="route53.amazonaws.com" AWS_URL="https://$AWS_HOST" @@ -43,6 +45,7 @@ dns_aws_add() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" + sleep 1 return 1 fi _debug _domain_id "$_domain_id" @@ -51,6 +54,7 @@ dns_aws_add() { _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + sleep 1 return 1 fi @@ -63,6 +67,7 @@ dns_aws_add() { if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then _info "The TXT record already exists. Skipping." + sleep 1 return 0 fi @@ -72,9 +77,10 @@ dns_aws_add() { if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "TXT record updated successfully." + sleep 1 return 0 fi - + sleep 1 return 1 } @@ -93,6 +99,7 @@ dns_aws_rm() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" + sleep 1 return 1 fi _debug _domain_id "$_domain_id" @@ -101,6 +108,7 @@ dns_aws_rm() { _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then + sleep 1 return 1 fi @@ -109,6 +117,7 @@ dns_aws_rm() { _debug "_resource_record" "$_resource_record" else _debug "no records exist, skip" + sleep 1 return 0 fi @@ -116,9 +125,10 @@ dns_aws_rm() { if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "TXT record deleted successfully." + sleep 1 return 0 fi - + sleep 1 return 1 } From bba5376a3693dc19186edd6ffc42277d45dfa2fb Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Tue, 15 Oct 2019 14:37:38 -0700 Subject: [PATCH 143/191] Improve debug capabilities when using bash When calling the _debug3() function will print the filename, function name, and line number when running under bash --- acme.sh | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 041b5b4..46df2ed 100755 --- a/acme.sh +++ b/acme.sh @@ -265,6 +265,37 @@ _usage() { printf "\n" >&2 } +__debug_bash_helper() { + # At this point only do for --debug 3 + if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -lt "$DEBUG_LEVEL_3" ]; then + echo "" + return + fi + # Return extra debug info when running with bash, otherwise return empty + # string. + if [ -z "${BASH_VERSION}" ]; then + echo "" + return + fi + # We are a bash shell at this point, return the filename, function name, and + # line number as a string + _dbh_saveIFS=$IFS + IFS=" " + # Must use eval or syntax error happens under dash + # Use 'caller 1' as we want one level up the stack as we should be called + # by one of the _debug* functions + eval "_dbh_called=($(caller 1))" + IFS=$_dbh_saveIFS + _dbh_file=${_dbh_called[2]} + if [ -n "${_script_home}" ]; then + # Trim off the _script_home directory name + _dbh_file=${_dbh_file#$_script_home/} + fi + _dbh_function=${_dbh_called[1]} + _dbh_lineno=${_dbh_called[0]} + printf "%-40s " "$_dbh_file:${_dbh_function}:${_dbh_lineno}" +} + _debug() { if [ "${LOG_LEVEL:-$DEFAULT_LOG_LEVEL}" -ge "$LOG_LEVEL_1" ]; then _log "$@" @@ -273,7 +304,8 @@ _debug() { _syslog "$SYSLOG_DEBUG" "$@" fi if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_1" ]; then - _printargs "$@" >&2 + _bash_debug=$(__debug_bash_helper) + _printargs "${_bash_debug}$@" >&2 fi } @@ -306,7 +338,8 @@ _debug2() { _syslog "$SYSLOG_DEBUG" "$@" fi if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_2" ]; then - _printargs "$@" >&2 + _bash_debug=$(__debug_bash_helper) + _printargs "${_bash_debug}$@" >&2 fi } @@ -338,7 +371,8 @@ _debug3() { _syslog "$SYSLOG_DEBUG" "$@" fi if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -ge "$DEBUG_LEVEL_3" ]; then - _printargs "$@" >&2 + _bash_debug=$(__debug_bash_helper) + _printargs "${_bash_debug}$@" >&2 fi } From ed9e196bf68d9eb4494fbd33826eaa2e2a9366bf Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 00:58:33 +0100 Subject: [PATCH 144/191] Update list of DNS providers for Plesk XML API --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c8bebc6..32d3014 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,7 @@ You don't have to do anything manually! 1. acme-dns (https://github.com/joohoi/acme-dns) 1. TELE3 (https://www.tele3.cz) 1. EUSERV.EU (https://www.euserv.eu) +1. Plesk XML API (https://www.plesk.com) And: From 1339b9422d0a7ad0f8c57b549ddb50eff7c18d05 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:04:08 +0100 Subject: [PATCH 145/191] Update for dns pleskxml --- dnsapi/README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/dnsapi/README.md b/dnsapi/README.md index 1f394f9..568baba 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -897,6 +897,26 @@ acme.sh --issue --dns dns_euserv -d example.com -d *.example.com --insecure The `EUSERV_Username` and `EUSERV_Password` will be saved in `~/.acme.sh/account.conf` and will be reused when needed. Please report any issues to https://github.com/initit/acme.sh or to + +## 47. Use Plesk XML API to automatically issue cert + +The plesk plugin uses an XML API to add and remove dns records. +The Plesk API URI (URL), and the user name and password for logging in, must be configured. + +``` +export pleskxml_uri="https://YOUR_PLESK_URI_HERE:8443/enterprise/control/agent.php" + (or probably something similar) +export pleskxml_user="plesk username" +export pleskxml_pass="plesk password" +``` + +Ok, let's issue a cert now: +``` +acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com +``` + +The `pleskxml_user`, `pleskxml_pass` and `pleskxml_uri` will be saved in `~/.acme.sh/account.conf` and are reused when needed. + # Use custom API If your API is not supported yet, you can write your own DNS API. @@ -917,4 +937,4 @@ See: https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide # Use lexicon DNS API -https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api \ No newline at end of file +https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api From 274393ac64fbccf994fd240498cb863348388da3 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:09:50 +0100 Subject: [PATCH 146/191] Create DNS 01 module for Plesk XML API --- dnsapi/dns_pleskxml | 402 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 dnsapi/dns_pleskxml diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml new file mode 100644 index 0000000..a8a7472 --- /dev/null +++ b/dnsapi/dns_pleskxml @@ -0,0 +1,402 @@ +#!/usr/bin/env sh + +## Name: dns_pleskxml.sh +## Created by Stilez. +## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) + +## This DNS01 method uses the Plesk XML API described at: +## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709 +## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784 + +## Note: a DNS ID with host = empty string is OK for this API, see +## https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 +## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action. +## So this API module can handle such a request, if needed. + +## The plesk plugin uses the xml api to add and remvoe the dns records. Therefore the url, username +## and password have to be configured by the user before this module is called. +## +## ``` +## export pleskxml_uri="https://YOUR_PLESK_URI_HERE:8443/enterprise/control/agent.php" +## (or probably something similar) +## export pleskxml_user="plesk username" +## export pleskxml_pass="plesk password" +## ``` + +## Ok, let's issue a cert now: +## ``` +## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com +## ``` +## +## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed. + + +#################### INTERNAL VARIABLES + NEWLINE ################################## + +pleskxml_init_checks_done=0 + +# Variable containing bare newline - not a style issue +# shellcheck disable=SC1004 +NEWLINE='\ +' + + +#################### API Templates ################################## + +pleskxml_tplt_get_domains="" + # Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh + # Also used to test credentials and URI. + # No args. +pleskxml_tplt_get_dns_records="%s" + # Get all DNS records for a Plesk domain ID. + # ARG = Plesk domain id to query +pleskxml_tplt_add_txt_record="%sTXT%s%s" + # Add a TXT record to a domain. + # ARGS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value +pleskxml_tplt_rmv_dns_record="%s" + # Add a TXT record to a domain. + # ARG = the Plesk internal ID for the dns record to be deleted + + +#################### Public functions ################################## + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_pleskxml_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Entering dns_pleskxml_add() to add TXT record '$2' to domain '$1'..." + + # Get credentials if not already checked, and confirm we can log in to Plesk XML API + if ! _credential_check; then + return 1 + fi + + # Get root and subdomain details, and Plesk domain ID + if ! _pleskxml_get_root_domain "$fulldomain"; then + return 1 + fi + + _debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record' + + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$( printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue" )" + if ! _call_api "$request"; then + return 1 + fi + + # OK, we should have added a TXT record. Let's check and return success if so. + # All that should be left in the result, is one section, containing okNEW_DNS_RECORD_ID + + results="$( _api_response_split "$pleskxml_prettyprint_result" 'result' '' )" + + if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + # Error - doesn't contain expected string. Something's wrong. + _err 'Error when calling Plesk XML API.' + _err 'The result did not contain the expected XXXXX section, or contained other values as well.' + _err 'This is unexpected: something has gone wrong.' + _err 'The full response was:\n' "$pleskxml_prettyprint_result" + return 1 + fi + + recid="$( _value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/' )" + + _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." + + return 0 +} + +#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_pleskxml_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Entering dns_pleskxml_rm() to remove TXT record '$2' from domain '$1'..." + + # Get credentials if not already checked, and confirm we can log in to Plesk XML API + if ! _credential_check; then + return 1 + fi + + # Get root and subdomain details, and Plesk domain ID + if ! _pleskxml_get_root_domain "$fulldomain"; then + return 1 + fi + + _debug 'Credentials OK, and domain identified. Calling Plesk XML API to get list of TXT records and their IDs' + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$( printf "$pleskxml_tplt_get_dns_records" "$root_domain_id" )" + if ! _call_api "$request"; then + return 1 + fi + + # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) + reclist="$( _api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | \ + grep "${root_domain_id}" | \ + grep -E '[0-9]+' | \ + grep 'TXT' \ + )" + + if [ -z "$reclist" ]; then + _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting." + return 1 + fi + + _debug "Got list of DNS TXT records for root domain '$root_domain_name'"':\n'"$reclist" + + recid="$( _value "$reclist" | \ + grep "$1." | \ + grep "$txtvalue" | \ + sed -E 's/(^.*|<\/id>.*$)//g' \ + )" + + _debug "List of DNS TXT records for host:"'\n'"$( _value "$reclist" | grep "$1." )" + + + if ! _value "$recid" | grep -Eq '^[0-9]+$'; then + _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" + _err "Cannot delete TXT record. Exiting." + return 1 + fi + + _debug "Found Plesk record ID for target text string '${txtvalue}': ID=${recid}" + _debug 'Calling Plesk XML API to remove TXT record' + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$( printf "$pleskxml_tplt_rmv_dns_record" "$recid" )" + if ! _call_api "$request"; then + return 1 + fi + + # OK, we should have removed a TXT record. Let's check and return success if so. + # All that should be left in the result, is one section, containing okPLESK_DELETED_DNS_RECORD_ID + + results="$( _api_response_split "$pleskxml_prettyprint_result" 'result' '' )" + + if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + # Error - doesn't contain expected string. Something's wrong. + _err 'Error when calling Plesk XML API.' + _err 'The result did not contain the expected XXXXX section, or contained other values as well.' + _err 'This is unexpected: something has gone wrong.' + _err 'The full response was:\n' "$pleskxml_prettyprint_result" + return 1 + fi + + _info "Success. TXT record appears to be correctly removed. Exiting dns_pleskxml_rm()." + return 0 +} + + + +#################### Private functions below ################################## + +# Outputs value of a variable +_value() { + printf '%s' "$1" +} + + +# Outputs value of a variable (FQDN) and cuts it at 2 delimiters +# $1, $2 = where to cut +# $3 = FQDN +_valuecut() { + printf '%s' "$3" | cut -d . -f "${1}-${2}" +} + + +# Cleans up an API response, splits it "per item" and greps for a string to validate useful lines +# $1 - result string from API +# $2 - tag to resplit on (usually "result" or "domain") +# $3 - regex to recognise useful return lines +_api_response_split() { + printf '%s' "$1" | \ + sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' | \ + tr -d '\n\r' | \ + sed -E "s/<\/?$2>/${NEWLINE}/g" | \ + grep -E "$3" +} + + +# Calls Plesk XML API, and checks results for obvious issues +_call_api() { + request="$1" + errtext='' + + _debug 'Entered _call_api(). Calling Plesk XML API with request:\n' "'${request}'" + + export _H1="HTTP_AUTH_LOGIN: $pleskxml_user" + export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass" + export _H3="content-Type: text/xml" + export _H4="HTTP_PRETTY_PRINT: true" + pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")" + pleskxml_retcode="$?" + _debug "acme _post() returned retcode=$pleskxml_retcode. Literal response:" '\n' "'${pleskxml_prettyprint_result}'" + + # Error handling + + # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. + # Also detect if there simply aren't any status lines (null result?) and report that, as well. + + statuslines="$( echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$' )" + + if _value "$statuslines" | grep -qv 'ok'; then + + # We have some status lines that aren't "ok". Get the details + errtext="$( \ + _value "$pleskxml_prettyprint_result" | \ + grep -iE "(||)" | \ + sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' | \ + sed -E 's/^<([a-z]+)>/\1: /' \ + )" + + elif ! _value "$statuslines" | grep -q 'ok'; then + + # We have no status lines at all. Results are empty + errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.' + + fi + + if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then + _err "The Plesk XML API call failed." + _err "The return code for the POST request was $pleskxml_retcode (0=success)." + if [ "$errtext" != "" ]; then + _err 'Status and error messages received from the Plesk server:\n' "$errtext" + else + _err "No additional error messages were received back from the Plesk server" + fi + return 1 + fi + + _debug "Leaving _call_api(). Successful call." + + return 0 +} + + +_credential_check() { + # Startup checks (credentials, URI) + + _debug "Checking Plesk XML API login credentials and URI..." + + if [ "$pleskxml_init_checks_done" -eq 1 ]; then + _debug "Initial checks already done, no need to repeat. Skipped." + return 0 + fi + + + pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}" + pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}" + pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}" + + _debug "Credentials - User: '${pleskxml_user}' Passwd: ****** URI: '${pleskxml_uri}'" + + if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then + pleskxml_user="" + pleskxml_pass="" + pleskxml_uri="" + _err "You didn't specify one or more of the Plesk XML API username, password, or URI." + _err "Please create these and try again." + _err "Instructions are in the module source code." + return 1 + fi + + # Test the API is usable, by trying to read the list of managed domains... + _call_api "$pleskxml_tplt_get_domains" + if [ "$pleskxml_retcode" -ne 0 ]; then + _err '\nFailed to access Plesk XML API.' + _err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again." + return 1 + fi + + _saveaccountconf_mutable pleskxml_uri "$pleskxml_uri" + _saveaccountconf_mutable pleskxml_user "$pleskxml_user" + _saveaccountconf_mutable pleskxml_pass "$pleskxml_pass" + + _debug "Test login to Plesk XML API successful. Login credentials and URI successfully saved to the acme.sh configuration file for future use." + + pleskxml_init_checks_done=1 + + return 0 +} + + +# For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any. +_pleskxml_get_root_domain() { + _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." + + # test if the domain is valid for splitting. + + if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then + ### COMMENTED OUT ALSO FOR SAME REASON + ### _err "Invalid domain. The ACME domain must contain at least three parts (aa.bb.tld) to identify a host, domain, and tld for the TXT record." + _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." + return 1 + fi + + _debug "Querying Plesk server for list of managed domains..." + + _call_api "$pleskxml_tplt_get_domains" + if [ "$pleskxml_retcode" -ne 0 ]; then + return 1 + fi + + # Generate a hacked list of domains known to this Plesk account. + # We convert tags to so it'll flag on a hit with either or fields, + # for non-Western character sets. + # Output will be one line per known domain, containing 1 or 2 tages and an tag + # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. + output="$( _api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '' )" + + _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" + + # loop and test if domain, or any parent domain, is managed by Plesk + # Loop until we don't have any '.' in the sring we're testing as a root domain + + root_domain_name="$1" + doneloop=0 + + while _contains "$root_domain_name" '\.'; do + + _debug "Checking if '$root_domain_name' is managed by the Plesk server..." + + root_domain_id="$( _value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/' )" + + if [ -n "$root_domain_id" ]; then + # Found a match + # Note that a result with host = empty string is OK for this API, see + # https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 + # See notes at top of this file + sub_domain_name="$( _value "$1" | sed -E "s/\.?${root_domain_name}"'$//' )" + _info "Matched host '$1' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." + return 0 + fi + + # No match, try next parent up (if any)... + + if _contains "$root_domain_name" '\.[^.]+\.'; then + _debug "No match, trying next parent up..." + else + _debug "No match,and next parent would be a TLD..." + fi + root_domain_name="$( _valuecut 2 1000 "$root_domain_name" )" + doneloop=1 + + done + + # if we get here, we failed to find a root domain match in the list of domains managed by Plesk. + # if we never ran the loop a first time, $1 wasn't at least a 2 level domain (domain.tld) and wasn't valid anyway + + if [ -z $doneloop ]; then + _err "'$1' isn't a valid domain for ACME DNS. Exiting." + else + _err "Cannot find '$1' or any parent domain of it, in Plesk." + _err "Are you sure that this domain is managed by this Plesk server?" + fi + + return 1 +} From a00300f88a7e9d85883616e55819f51052e64b7e Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:23:14 +0100 Subject: [PATCH 147/191] revert changes to this file --- README.md | 536 ++++++++++++++---------------------------------------- 1 file changed, 134 insertions(+), 402 deletions(-) diff --git a/README.md b/README.md index 32d3014..613fff7 100644 --- a/README.md +++ b/README.md @@ -1,503 +1,235 @@ -# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) - -[![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -- An ACME protocol client written purely in Shell (Unix shell) language. -- Full ACME protocol implementation. -- Support ACME v1 and ACME v2 -- Support ACME v2 wildcard certs -- Simple, powerful and very easy to use. You only need 3 minutes to learn it. -- Bash, dash and sh compatible. -- Simplest shell script for Let's Encrypt free certificate client. -- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. -- Just one script to issue, renew and install your certificates automatically. -- DOES NOT require `root/sudoer` access. -- Docker friendly -- IPv6 support - -It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. - -Wiki: https://github.com/Neilpang/acme.sh/wiki - -For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) - -Twitter: [@neilpangxa](https://twitter.com/neilpangxa) - - -# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) - -# Who: -- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) -- [ruby-china.org](https://ruby-china.org/topics/31983) -- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) -- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89) -- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) -- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) -- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) -- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) -- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) -- [archlinux](https://aur.archlinux.org/packages/acme.sh-git/) -- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) -- [CentOS Web Panel](http://centos-webpanel.com/) -- [lnmp.org](https://lnmp.org/) -- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) - -# Tested OS - -| NO | Status| Platform| -|----|-------|---------| -|1|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu -|2|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian -|3|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS -|4|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included) -|5|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD -|6|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense -|7|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE -|8|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl) -|9|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux -|10|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora -|11|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux -|12|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux -|13|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh -|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111 -|15|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD -|16|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia -|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) -|18|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris -|19|[![](https://cdn.rawgit.com/Neilpang/acmetest/master/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux -|20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX - -For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest): - -https://github.com/Neilpang/acmetest - - -# Supported modes - -- Webroot mode -- Standalone mode -- Apache mode -- Nginx mode -- DNS mode -- [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) -- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) - - -# 1. How to install - -### 1. Install online - -Check this project: https://github.com/Neilpang/get.acme.sh - -```bash -curl https://get.acme.sh | sh -``` - -Or: - -```bash -wget -O - https://get.acme.sh | sh -``` - - -### 2. Or, Install from git - -Clone this project and launch installation: - -```bash -git clone https://github.com/Neilpang/acme.sh.git -cd ./acme.sh -./acme.sh --install -``` - -You `don't have to be root` then, although `it is recommended`. - -Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install - -The installer will perform 3 actions: - -1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`. -All certs will be placed in this folder too. -2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`. -3. Create daily cron job to check and renew the certs if needed. - -Cron entry example: - -```bash -0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null -``` - -After the installation, you must close the current terminal and reopen it to make the alias take effect. - -Ok, you are ready to issue certs now. -Show help message: +Skip to content +Pull requests +Issues +Marketplace +Explore +@stilez +Learn Git and GitHub without any code! -```sh -root@v1:~# acme.sh -h -``` +Using the Hello World guide, you’ll start a branch, write comments, and open a pull request. -# 2. Just issue a cert +395 +14.7k -**Example 1:** Single domain. + 1.9k -```bash -acme.sh --issue -d example.com -w /home/wwwroot/example.com -``` +Neilpang/acme.sh +Code +Issues 415 +Pull requests 110 +Actions +Projects 0 +Wiki +Security +Insights +You’re editing a file in a project you don’t have write access to. Submitting a change to this file will write it to a new branch in your fork stilez/acme.sh, so you can send a pull request. +acme.sh/ -or: +1 -```bash -acme.sh --issue -d example.com -w /home/username/public_html -``` - -or: - -```bash -acme.sh --issue -d example.com -w /var/www/html -``` - -**Example 2:** Multiple domains in the same cert. - -```bash -acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com -``` - -The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder. - -Second argument **"example.com"** is the main domain you want to issue the cert for. -You must have at least one domain there. - -You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. - -The certs will be placed in `~/.acme.sh/example.com/` - -The certs will be renewed automatically every **60** days. - -More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert - - -# 3. Install the cert to Apache/Nginx etc. - -After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers. -You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future. - -**Apache** example: -```bash -acme.sh --install-cert -d example.com \ ---cert-file /path/to/certfile/in/apache/cert.pem \ ---key-file /path/to/keyfile/in/apache/key.pem \ ---fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \ ---reloadcmd "service apache2 force-reload" -``` - -**Nginx** example: -```bash -acme.sh --install-cert -d example.com \ ---key-file /path/to/keyfile/in/nginx/key.pem \ ---fullchain-file /path/to/fullchain/nginx/cert.pem \ ---reloadcmd "service nginx force-reload" -``` - -Only the domain is required, all the other parameters are optional. - -The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission. - -Install/copy the cert/key to the production Apache or Nginx path. - -The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. - - -**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.** - -# 4. Use Standalone server to issue cert - -**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** - -Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. - -```bash -acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com -``` - -More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert - - -# 5. Use Apache mode - -**(requires you to be root/sudoer, since it is required to interact with Apache server)** - -If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. - -Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder. - -Just set string "apache" as the second argument and it will force use of apache plugin automatically. - -```sh -acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com -``` - -**This apache mode is only to issue the cert, it will not change your apache config files. -You will need to configure your website config files to use the cert by yourself. -We don't want to mess your apache server, don't worry.** +# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) -More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert +2 -# 6. Use Nginx mode +​ -**(requires you to be root/sudoer, since it is required to interact with Nginx server)** +3 -If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder. +4 -Just set string "nginx" as the second argument. +- An ACME protocol client written purely in Shell (Unix shell) language. -It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version. +5 -So, the config is not changed. +- Full ACME protocol implementation. -```sh -acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com -``` +6 -**This nginx mode is only to issue the cert, it will not change your nginx config files. -You will need to configure your website config files to use the cert by yourself. -We don't want to mess your nginx server, don't worry.** +- Support ACME v1 and ACME v2 -More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert +7 -# 7. Automatic DNS API integration +- Support ACME v2 wildcard certs -If your DNS provider supports API access, we can use that API to automatically issue the certs. +8 -You don't have to do anything manually! +- Simple, powerful and very easy to use. You only need 3 minutes to learn it. -### Currently acme.sh supports: +9 -1. CloudFlare.com API -1. DNSPod.cn API -1. CloudXNS.com API -1. GoDaddy.com API -1. PowerDNS.com API -1. OVH, kimsufi, soyoustart and runabove API -1. nsupdate API -1. LuaDNS.com API -1. DNSMadeEasy.com API -1. AWS Route 53 -1. aliyun.com(阿里云) API -1. ISPConfig 3.1 API -1. Alwaysdata.com API -1. Linode.com API -1. FreeDNS (https://freedns.afraid.org/) -1. cyon.ch -1. Domain-Offensive/Resellerinterface/Domainrobot API -1. Gandi LiveDNS API -1. Knot DNS API -1. DigitalOcean API (native) -1. ClouDNS.net API -1. Infoblox NIOS API (https://www.infoblox.com/) -1. VSCALE (https://vscale.io/) -1. Dynu API (https://www.dynu.com) -1. DNSimple API -1. NS1.com API -1. DuckDNS.org API -1. Name.com API -1. Dyn Managed DNS API -1. Yandex PDD API (https://pdd.yandex.ru) -1. Hurricane Electric DNS service (https://dns.he.net) -1. UnoEuro API (https://www.unoeuro.com/) -1. INWX (https://www.inwx.de/) -1. Servercow (https://servercow.de) -1. Namesilo (https://www.namesilo.com) -1. InternetX autoDNS API (https://internetx.com) -1. Azure DNS -1. selectel.com(selectel.ru) DNS API -1. zonomi.com DNS API -1. DreamHost.com API -1. DirectAdmin API -1. KingHost (https://www.kinghost.com.br/) -1. Zilore (https://zilore.com) -1. Loopia.se API -1. acme-dns (https://github.com/joohoi/acme-dns) -1. TELE3 (https://www.tele3.cz) -1. EUSERV.EU (https://www.euserv.eu) -1. Plesk XML API (https://www.plesk.com) +- Bash, dash and sh compatible. -And: +10 -**lexicon DNS API: https://github.com/Neilpang/acme.sh/wiki/How-to-use-lexicon-dns-api - (DigitalOcean, DNSimple, DNSMadeEasy, DNSPark, EasyDNS, Namesilo, NS1, PointHQ, Rage4 and Vultr etc.)** +- Simplest shell script for Let's Encrypt free certificate client. +11 -**More APIs coming soon...** +- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. -If your DNS provider is not on the supported list above, you can write your own DNS API script easily. If you do, please consider submitting a [Pull Request](https://github.com/Neilpang/acme.sh/pulls) and contribute it to the project. +12 -For more details: [How to use DNS API](dnsapi) +- Just one script to issue, renew and install your certificates automatically. -# 8. Use DNS manual mode: +13 -See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. +- DOES NOT require `root/sudoer` access. -If your dns provider doesn't support any api access, you can add the txt record by your hand. +14 -```bash -acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com -``` +- Docker friendly -You should get an output like below: +15 -```sh -Add the following txt record: -Domain:_acme-challenge.example.com -Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c +- IPv6 support -Add the following txt record: -Domain:_acme-challenge.www.example.com -Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +16 -Please add those txt records to the domains. Waiting for the dns to take effect. -``` +- Cron job notifications for renewal or error etc. -Then just rerun with `renew` argument: +17 -```bash -acme.sh --renew -d example.com -``` +​ -Ok, it's done. +18 -**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** +It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. -**Please use dns api mode instead.** +19 -# 9. Issue ECC certificates +​ -`Let's Encrypt` can now issue **ECDSA** certificates. +20 -And we support them too! +Wiki: https://github.com/Neilpang/acme.sh/wiki -Just set the `keylength` parameter with a prefix `ec-`. +21 -For example: +​ -### Single domain ECC certificate +22 -```bash -acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 -``` +For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) -### SAN multi domain ECC certificate +23 -```bash -acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 -``` +​ -Please look at the `keylength` parameter above. +24 -Valid values are: +Twitter: [@neilpangxa](https://twitter.com/neilpangxa) -1. **ec-256 (prime256v1, "ECDSA P-256")** -2. **ec-384 (secp384r1, "ECDSA P-384")** -3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** +25 +​ +26 -# 10. Issue Wildcard certificates +​ -It's simple, just give a wildcard domain as the `-d` parameter. +27 -```sh -acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf -``` +# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) +28 +​ -# 11. How to renew the certs +29 -No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. +# Who: -However, you can also force to renew a cert: +30 -```sh -acme.sh --renew -d example.com --force -``` +- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) -or, for ECC cert: +31 -```sh -acme.sh --renew -d example.com --force --ecc -``` +- [ruby-china.org](https://ruby-china.org/topics/31983) +32 -# 12. How to stop cert renewal +- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) -To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: +33 -```sh -acme.sh --remove -d example.com [--ecc] -``` +- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89) -The cert/key file is not removed from the disk. +34 -You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. +- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) +35 -# 13. How to upgrade `acme.sh` +- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) -acme.sh is in constant development, so it's strongly recommended to use the latest code. +36 -You can update acme.sh to the latest code: +- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) -```sh -acme.sh --upgrade -``` +37 -You can also enable auto upgrade: +- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) -```sh -acme.sh --upgrade --auto-upgrade -``` +38 -Then **acme.sh** will be kept up to date automatically. +- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) -Disable auto upgrade: +39 -```sh -acme.sh --upgrade --auto-upgrade 0 -``` +- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh) +40 -# 14. Issue a cert from an existing CSR +- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) -https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR +41 +- [CentOS Web Panel](http://centos-webpanel.com/) -# 15. Under the Hood +42 -Speak ACME language using shell, directly to "Let's Encrypt". +- [lnmp.org](https://lnmp.org/) -TODO: +43 +- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) -# 16. Acknowledgments +44 -1. Acme-tiny: https://github.com/diafygi/acme-tiny -2. ACME protocol: https://github.com/ietf-wg-acme/acme +​ +45 -# 17. License & Others +# Tested OS -License is GPLv3 +46 -Please Star and Fork me. +​ -[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. +47 +| NO | Status| Platform| -# 18. Donate -Your donation makes **acme.sh** better: +@stilez +Propose file change +Commit summary +Optional extended description + + © 2019 GitHub, Inc. + Terms + Privacy + Security + Status + Help + + Contact GitHub + Pricing + API + Training + Blog + About -1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) - -[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) From 4c9d99040c822b35eefbcee46b88bdb766af32a5 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:31:17 +0100 Subject: [PATCH 148/191] Fix (revert) edited .md file --- README.md | 501 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 407 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 6c69288..d5012d6 100644 --- a/README.md +++ b/README.md @@ -1,185 +1,498 @@ +# An ACME Shell script: acme.sh [![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh) -Skip to content -Pull requests -Issues -Marketplace -Explore -@stilez -Learn Git and GitHub without any code! + [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +- An ACME protocol client written purely in Shell (Unix shell) language. +- Full ACME protocol implementation. +- Support ACME v1 and ACME v2 +- Support ACME v2 wildcard certs +- Simple, powerful and very easy to use. You only need 3 minutes to learn it. +- Bash, dash and sh compatible. +- Simplest shell script for Let's Encrypt free certificate client. +- Purely written in Shell with no dependencies on python or the official Let's Encrypt client. +- Just one script to issue, renew and install your certificates automatically. +- DOES NOT require `root/sudoer` access. +- Docker friendly +- IPv6 support +- Cron job notifications for renewal or error etc. -Using the Hello World guide, you’ll start a branch, write comments, and open a pull request. +It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. -395 -14.7k +Wiki: https://github.com/Neilpang/acme.sh/wiki - 1.9k +For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) -Neilpang/acme.sh -Code -Issues 415 -Pull requests 110 -Actions -Projects 0 -Wiki -Security -Insights -You’re editing a file in a project you don’t have write access to. Submitting a change to this file will write it to a new branch in your fork stilez/acme.sh, so you can send a pull request. -acme.sh/ +Twitter: [@neilpangxa](https://twitter.com/neilpangxa) -1 +# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) -2 +# Who: +- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) +- [ruby-china.org](https://ruby-china.org/topics/31983) +- [Proxmox](https://pve.proxmox.com/wiki/HTTPS_Certificate_Configuration_(Version_4.x_and_newer)) +- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89) +- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) +- [Loadbalancer.org](https://www.loadbalancer.org/blog/loadbalancer-org-with-lets-encrypt-quick-and-dirty) +- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) +- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) +- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) +- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh) +- [opnsense.org](https://github.com/opnsense/plugins/tree/master/security/acme-client/src/opnsense/scripts/OPNsense/AcmeClient) +- [CentOS Web Panel](http://centos-webpanel.com/) +- [lnmp.org](https://lnmp.org/) +- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) +# Tested OS -3 +| NO | Status| Platform| +|----|-------|---------| +|1|[![](https://neilpang.github.io/acmetest/status/ubuntu-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Ubuntu +|2|[![](https://neilpang.github.io/acmetest/status/debian-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Debian +|3|[![](https://neilpang.github.io/acmetest/status/centos-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|CentOS +|4|[![](https://neilpang.github.io/acmetest/status/windows-cygwin.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Windows (cygwin with curl, openssl and crontab included) +|5|[![](https://neilpang.github.io/acmetest/status/freebsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|FreeBSD +|6|[![](https://neilpang.github.io/acmetest/status/pfsense.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|pfsense +|7|[![](https://neilpang.github.io/acmetest/status/opensuse-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|openSUSE +|8|[![](https://neilpang.github.io/acmetest/status/alpine-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Alpine Linux (with curl) +|9|[![](https://neilpang.github.io/acmetest/status/base-archlinux.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Archlinux +|10|[![](https://neilpang.github.io/acmetest/status/fedora-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|fedora +|11|[![](https://neilpang.github.io/acmetest/status/kalilinux-kali-linux-docker.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Kali Linux +|12|[![](https://neilpang.github.io/acmetest/status/oraclelinux-latest.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Oracle Linux +|13|[![](https://neilpang.github.io/acmetest/status/proxmox.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)| Proxmox https://pve.proxmox.com/wiki/HTTPSCertificateConfiguration#Let.27s_Encrypt_using_acme.sh +|14|-----| Cloud Linux https://github.com/Neilpang/le/issues/111 +|15|[![](https://neilpang.github.io/acmetest/status/openbsd.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|OpenBSD +|16|[![](https://neilpang.github.io/acmetest/status/mageia.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Mageia +|17|-----| OpenWRT: Tested and working. See [wiki page](https://github.com/Neilpang/acme.sh/wiki/How-to-run-on-OpenWRT) +|18|[![](https://neilpang.github.io/acmetest/status/solaris.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|SunOS/Solaris +|19|[![](https://neilpang.github.io/acmetest/status/gentoo-stage3-amd64.svg)](https://github.com/Neilpang/letest#here-are-the-latest-status)|Gentoo Linux +|20|[![Build Status](https://travis-ci.org/Neilpang/acme.sh.svg?branch=master)](https://travis-ci.org/Neilpang/acme.sh)|Mac OSX - [![Join the chat at https://gitter.im/acme-sh/Lobby](https://badges.gitter.im/acme-sh/Lobby.svg)](https://gitter.im/acme-sh/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +For all build statuses, check our [weekly build project](https://github.com/Neilpang/acmetest): -4 +https://github.com/Neilpang/acmetest -- An ACME protocol client written purely in Shell (Unix shell) language. +# Supported CA -5 +- Letsencrypt.org CA(default) +- [BuyPass.com CA](https://github.com/Neilpang/acme.sh/wiki/BuyPass.com-CA) +- [Pebble strict Mode](https://github.com/letsencrypt/pebble) -- Full ACME protocol implementation. +# Supported modes -6 +- Webroot mode +- Standalone mode +- Standalone tls-alpn mode +- Apache mode +- Nginx mode +- DNS mode +- [DNS alias mode](https://github.com/Neilpang/acme.sh/wiki/DNS-alias-mode) +- [Stateless mode](https://github.com/Neilpang/acme.sh/wiki/Stateless-Mode) -7 +# 1. How to install +### 1. Install online -8 +Check this project: https://github.com/Neilpang/get.acme.sh -- Simple, powerful and very easy to use. You only need 3 minutes to learn it. +```bash +curl https://get.acme.sh | sh +``` -- DOES NOT require `root/sudoer` access. +Or: -14 +```bash +wget -O - https://get.acme.sh | sh +``` -- Docker friendly -15 +### 2. Or, Install from git -- IPv6 support +Clone this project and launch installation: -16 +```bash +git clone https://github.com/Neilpang/acme.sh.git +cd ./acme.sh +./acme.sh --install +``` -- Cron job notifications for renewal or error etc. +You `don't have to be root` then, although `it is recommended`. -17 +Advanced Installation: https://github.com/Neilpang/acme.sh/wiki/How-to-install -​ +The installer will perform 3 actions: -18 +1. Create and copy `acme.sh` to your home dir (`$HOME`): `~/.acme.sh/`. +All certs will be placed in this folder too. +2. Create alias for: `acme.sh=~/.acme.sh/acme.sh`. +3. Create daily cron job to check and renew the certs if needed. -It's probably the `easiest & smartest` shell script to automatically issue & renew the free certificates from Let's Encrypt. +Cron entry example: -19 +```bash +0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null +``` +After the installation, you must close the current terminal and reopen it to make the alias take effect. -20 +Ok, you are ready to issue certs now. -Wiki: https://github.com/Neilpang/acme.sh/wiki +Show help message: -21 +```sh +root@v1:~# acme.sh -h +``` -​ +# 2. Just issue a cert -22 +**Example 1:** Single domain. -For Docker Fans: [acme.sh :two_hearts: Docker ](https://github.com/Neilpang/acme.sh/wiki/Run-acme.sh-in-docker) +```bash +acme.sh --issue -d example.com -w /home/wwwroot/example.com +``` -23 +or: -​ +```bash +acme.sh --issue -d example.com -w /home/username/public_html +``` -24 +or: -Twitter: [@neilpangxa](https://twitter.com/neilpangxa) +```bash +acme.sh --issue -d example.com -w /var/www/html +``` -25 +**Example 2:** Multiple domains in the same cert. -​ +```bash +acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com +``` -26 +The parameter `/home/wwwroot/example.com` or `/home/username/public_html` or `/var/www/html` is the web root folder where you host your website files. You **MUST** have `write access` to this folder. +Second argument **"example.com"** is the main domain you want to issue the cert for. +You must have at least one domain there. -27 +You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. -# [中文说明](https://github.com/Neilpang/acme.sh/wiki/%E8%AF%B4%E6%98%8E) +The certs will be placed in `~/.acme.sh/example.com/` -28 +The certs will be renewed automatically every **60** days. -​ +More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -# Who: +# 3. Install the cert to Apache/Nginx etc. -30 +After the cert is generated, you probably want to install/copy the cert to your Apache/Nginx or other servers. +You **MUST** use this command to copy the certs to the target files, **DO NOT** use the certs files in **~/.acme.sh/** folder, they are for internal use only, the folder structure may change in the future. -- [FreeBSD.org](https://blog.crashed.org/letsencrypt-in-freebsd-org/) +**Apache** example: +```bash +acme.sh --install-cert -d example.com \ +--cert-file /path/to/certfile/in/apache/cert.pem \ +--key-file /path/to/keyfile/in/apache/key.pem \ +--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \ +--reloadcmd "service apache2 force-reload" +``` -31 +**Nginx** example: +```bash +acme.sh --install-cert -d example.com \ +--key-file /path/to/keyfile/in/nginx/key.pem \ +--fullchain-file /path/to/fullchain/nginx/cert.pem \ +--reloadcmd "service nginx force-reload" +``` -- [ruby-china.org](https://ruby-china.org/topics/31983) +Only the domain is required, all the other parameters are optional. -32 +The ownership and permission info of existing files are preserved. You can pre-create the files to define the ownership and permission. +Install/copy the cert/key to the production Apache or Nginx path. -33 +The cert will be renewed every **60** days by default (which is configurable). Once the cert is renewed, the Apache/Nginx service will be reloaded automatically by the command: `service apache2 force-reload` or `service nginx force-reload`. -- [pfsense](https://github.com/pfsense/FreeBSD-ports/pull/89) -34 +**Please take care: The reloadcmd is very important. The cert can be automatically renewed, but, without a correct 'reloadcmd' the cert may not be flushed to your server(like nginx or apache), then your website will not be able to show renewed cert in 60 days.** -- [webfaction](https://community.webfaction.com/questions/19988/using-letsencrypt) +# 4. Use Standalone server to issue cert -35 +**(requires you to be root/sudoer or have permission to listen on port 80 (TCP))** +Port `80` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. -36 +```bash +acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com +``` -- [discourse.org](https://meta.discourse.org/t/setting-up-lets-encrypt/40709) +More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -37 +# 5. Use Standalone ssl server to issue cert -- [Centminmod](https://centminmod.com/letsencrypt-acmetool-https.html) +**(requires you to be root/sudoer or have permission to listen on port 443 (TCP))** -38 +Port `443` (TCP) **MUST** be free to listen on, otherwise you will be prompted to free it and try again. -- [splynx](https://forum.splynx.com/t/free-ssl-cert-for-splynx-lets-encrypt/297) +```bash +acme.sh --issue --alpn -d example.com -d www.example.com -d cp.example.com +``` -39 +More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert -- [archlinux](https://www.archlinux.org/packages/community/any/acme.sh) -40 +# 6. Use Apache mode +**(requires you to be root/sudoer, since it is required to interact with Apache server)** -41 +If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. -- [CentOS Web Panel](http://centos-webpanel.com/) +Particularly, if you are running an Apache server, you can use Apache mode instead. This mode doesn't write any files to your web root folder. +Just set string "apache" as the second argument and it will force use of apache plugin automatically. -- [lnmp.org](https://lnmp.org/) +```sh +acme.sh --issue --apache -d example.com -d www.example.com -d cp.example.com +``` -43 +**This apache mode is only to issue the cert, it will not change your apache config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your apache server, don't worry.** -- [more...](https://github.com/Neilpang/acme.sh/wiki/Blogs-and-tutorials) +More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert +# 7. Use Nginx mode -​ +**(requires you to be root/sudoer, since it is required to interact with Nginx server)** -45 +If you are running a web server, Apache or Nginx, it is recommended to use the `Webroot mode`. +Particularly, if you are running an nginx server, you can use nginx mode instead. This mode doesn't write any files to your web root folder. -46 +Just set string "nginx" as the second argument. -​ +It will configure nginx server automatically to verify the domain and then restore the nginx config to the original version. -47 +So, the config is not changed. -| NO | Status| Platform| +```sh +acme.sh --issue --nginx -d example.com -d www.example.com -d cp.example.com +``` + +**This nginx mode is only to issue the cert, it will not change your nginx config files. +You will need to configure your website config files to use the cert by yourself. +We don't want to mess your nginx server, don't worry.** + +More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert + +# 8. Automatic DNS API integration + +If your DNS provider supports API access, we can use that API to automatically issue the certs. + +You don't have to do anything manually! + +### Currently acme.sh supports most of the dns providers: + +https://github.com/Neilpang/acme.sh/wiki/dnsapi + +# 9. Use DNS manual mode: + +See: https://github.com/Neilpang/acme.sh/wiki/dns-manual-mode first. + +If your dns provider doesn't support any api access, you can add the txt record by your hand. + +```bash +acme.sh --issue --dns -d example.com -d www.example.com -d cp.example.com +``` + +You should get an output like below: + +```sh +Add the following txt record: +Domain:_acme-challenge.example.com +Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c + +Add the following txt record: +Domain:_acme-challenge.www.example.com +Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +Please add those txt records to the domains. Waiting for the dns to take effect. +``` + +Then just rerun with `renew` argument: + +```bash +acme.sh --renew -d example.com +``` + +Ok, it's done. + +**Take care, this is dns manual mode, it can not be renewed automatically. you will have to add a new txt record to your domain by your hand when you renew your cert.** + +**Please use dns api mode instead.** + +# 10. Issue ECC certificates + +`Let's Encrypt` can now issue **ECDSA** certificates. + +And we support them too! + +Just set the `keylength` parameter with a prefix `ec-`. + +For example: + +### Single domain ECC certificate + +```bash +acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 +``` + +### SAN multi domain ECC certificate + +```bash +acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 +``` + +Please look at the `keylength` parameter above. + +Valid values are: + +1. **ec-256 (prime256v1, "ECDSA P-256")** +2. **ec-384 (secp384r1, "ECDSA P-384")** +3. **ec-521 (secp521r1, "ECDSA P-521", which is not supported by Let's Encrypt yet.)** + + + +# 11. Issue Wildcard certificates + +It's simple, just give a wildcard domain as the `-d` parameter. + +```sh +acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf +``` + + + +# 12. How to renew the certs + +No, you don't need to renew the certs manually. All the certs will be renewed automatically every **60** days. + +However, you can also force to renew a cert: + +```sh +acme.sh --renew -d example.com --force +``` + +or, for ECC cert: + +```sh +acme.sh --renew -d example.com --force --ecc +``` + + +# 13. How to stop cert renewal + +To stop renewal of a cert, you can execute the following to remove the cert from the renewal list: + +```sh +acme.sh --remove -d example.com [--ecc] +``` + +The cert/key file is not removed from the disk. + +You can remove the respective directory (e.g. `~/.acme.sh/example.com`) by yourself. + + +# 14. How to upgrade `acme.sh` + +acme.sh is in constant development, so it's strongly recommended to use the latest code. + +You can update acme.sh to the latest code: + +```sh +acme.sh --upgrade +``` + +You can also enable auto upgrade: + +```sh +acme.sh --upgrade --auto-upgrade +``` + +Then **acme.sh** will be kept up to date automatically. + +Disable auto upgrade: + +```sh +acme.sh --upgrade --auto-upgrade 0 +``` + + +# 15. Issue a cert from an existing CSR + +https://github.com/Neilpang/acme.sh/wiki/Issue-a-cert-from-existing-CSR + + +# 16. Send notifications in cronjob + +https://github.com/Neilpang/acme.sh/wiki/notify + + +# 17. Under the Hood + +Speak ACME language using shell, directly to "Let's Encrypt". + +TODO: + + +# 18. Acknowledgments + +1. Acme-tiny: https://github.com/diafygi/acme-tiny +2. ACME protocol: https://github.com/ietf-wg-acme/acme + + +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/acmesh/contribute)] + +#### Individuals + + + +#### Organizations + +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/acmesh/contribute)] + + + + + + + + + + + + +# 19. License & Others + +License is GPLv3 + +Please Star and Fork me. + +[Issues](https://github.com/Neilpang/acme.sh/issues) and [pull requests](https://github.com/Neilpang/acme.sh/pulls) are welcome. + + +# 20. Donate +Your donation makes **acme.sh** better: + +1. PayPal/Alipay(支付宝)/Wechat(微信): [https://donate.acme.sh/](https://donate.acme.sh/) +[Donate List](https://github.com/Neilpang/acme.sh/wiki/Donate-list) From a6614abd24600618a23ee390470eea7af912b9f4 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:00:59 +0000 Subject: [PATCH 149/191] Formatting fixes for Travis --- dnsapi/dns_pleskxml | 102 +++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml index a8a7472..bed1b26 100644 --- a/dnsapi/dns_pleskxml +++ b/dnsapi/dns_pleskxml @@ -44,18 +44,21 @@ NEWLINE='\ #################### API Templates ################################## pleskxml_tplt_get_domains="" - # Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh - # Also used to test credentials and URI. - # No args. +# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh +# Also used to test credentials and URI. +# No args. + pleskxml_tplt_get_dns_records="%s" - # Get all DNS records for a Plesk domain ID. - # ARG = Plesk domain id to query +# Get all DNS records for a Plesk domain ID. +# ARG = Plesk domain id to query + pleskxml_tplt_add_txt_record="%sTXT%s%s" - # Add a TXT record to a domain. - # ARGS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value +# Add a TXT record to a domain. +# ARGS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value + pleskxml_tplt_rmv_dns_record="%s" - # Add a TXT record to a domain. - # ARG = the Plesk internal ID for the dns record to be deleted +# Add a TXT record to a domain. +# ARG = the Plesk internal ID for the dns record to be deleted #################### Public functions ################################## @@ -82,7 +85,7 @@ dns_pleskxml_add() { # printf using template in a variable - not a style issue # shellcheck disable=SC2059 - request="$( printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue" )" + request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")" if ! _call_api "$request"; then return 1 fi @@ -90,7 +93,7 @@ dns_pleskxml_add() { # OK, we should have added a TXT record. Let's check and return success if so. # All that should be left in the result, is one section, containing okNEW_DNS_RECORD_ID - results="$( _api_response_split "$pleskxml_prettyprint_result" 'result' '' )" + results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then # Error - doesn't contain expected string. Something's wrong. @@ -101,7 +104,7 @@ dns_pleskxml_add() { return 1 fi - recid="$( _value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/' )" + recid="$(_value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -129,16 +132,16 @@ dns_pleskxml_rm() { # printf using template in a variable - not a style issue # shellcheck disable=SC2059 - request="$( printf "$pleskxml_tplt_get_dns_records" "$root_domain_id" )" + request="$(printf "$pleskxml_tplt_get_dns_records" "$root_domain_id")" if ! _call_api "$request"; then return 1 fi # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) - reclist="$( _api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' | \ - grep "${root_domain_id}" | \ - grep -E '[0-9]+' | \ - grep 'TXT' \ + reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ + | grep "${root_domain_id}" \ + | grep -E '[0-9]+' \ + | grep 'TXT' \ )" if [ -z "$reclist" ]; then @@ -148,14 +151,13 @@ dns_pleskxml_rm() { _debug "Got list of DNS TXT records for root domain '$root_domain_name'"':\n'"$reclist" - recid="$( _value "$reclist" | \ - grep "$1." | \ - grep "$txtvalue" | \ - sed -E 's/(^.*|<\/id>.*$)//g' \ - )" - - _debug "List of DNS TXT records for host:"'\n'"$( _value "$reclist" | grep "$1." )" + recid="$(_value "$reclist" \ + | grep "$1." \ + | grep "$txtvalue" \ + | sed -E 's/(^.*|<\/id>.*$)//g' \ + )" + _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" if ! _value "$recid" | grep -Eq '^[0-9]+$'; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" @@ -168,7 +170,7 @@ dns_pleskxml_rm() { # printf using template in a variable - not a style issue # shellcheck disable=SC2059 - request="$( printf "$pleskxml_tplt_rmv_dns_record" "$recid" )" + request="$(printf "$pleskxml_tplt_rmv_dns_record" "$recid")" if ! _call_api "$request"; then return 1 fi @@ -176,7 +178,7 @@ dns_pleskxml_rm() { # OK, we should have removed a TXT record. Let's check and return success if so. # All that should be left in the result, is one section, containing okPLESK_DELETED_DNS_RECORD_ID - results="$( _api_response_split "$pleskxml_prettyprint_result" 'result' '' )" + results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then # Error - doesn't contain expected string. Something's wrong. @@ -214,11 +216,11 @@ _valuecut() { # $2 - tag to resplit on (usually "result" or "domain") # $3 - regex to recognise useful return lines _api_response_split() { - printf '%s' "$1" | \ - sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' | \ - tr -d '\n\r' | \ - sed -E "s/<\/?$2>/${NEWLINE}/g" | \ - grep -E "$3" + printf '%s' "$1" \ + | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ + | tr -d '\n\r' \ + | sed -E "s/<\/?$2>/${NEWLINE}/g" \ + | grep -E "$3" } @@ -242,17 +244,16 @@ _call_api() { # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. - statuslines="$( echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$' )" + statuslines="$(echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$')" if _value "$statuslines" | grep -qv 'ok'; then # We have some status lines that aren't "ok". Get the details - errtext="$( \ - _value "$pleskxml_prettyprint_result" | \ - grep -iE "(||)" | \ - sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' | \ - sed -E 's/^<([a-z]+)>/\1: /' \ - )" + errtext="$( _value "$pleskxml_prettyprint_result" \ + | grep -iE "(||)" \ + | sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' \ + | sed -E 's/^<([a-z]+)>/\1: /' \ + )" elif ! _value "$statuslines" | grep -q 'ok'; then @@ -326,14 +327,17 @@ _credential_check() { # For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any. + +# IMPORTANT NOTE: a result with host = empty string is OK for this API, see +# https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 +# See notes at top of this file + _pleskxml_get_root_domain() { _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." # test if the domain is valid for splitting. - if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then - ### COMMENTED OUT ALSO FOR SAME REASON - ### _err "Invalid domain. The ACME domain must contain at least three parts (aa.bb.tld) to identify a host, domain, and tld for the TXT record." + if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." return 1 fi @@ -350,7 +354,8 @@ _pleskxml_get_root_domain() { # for non-Western character sets. # Output will be one line per known domain, containing 1 or 2 tages and an tag # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. - output="$( _api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '' )" + + output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" @@ -364,14 +369,13 @@ _pleskxml_get_root_domain() { _debug "Checking if '$root_domain_name' is managed by the Plesk server..." - root_domain_id="$( _value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/' )" + root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" if [ -n "$root_domain_id" ]; then # Found a match - # Note that a result with host = empty string is OK for this API, see - # https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 - # See notes at top of this file - sub_domain_name="$( _value "$1" | sed -E "s/\.?${root_domain_name}"'$//' )" + # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. + # SO WE HANDLE IT AND DON'T PREVENT IT + sub_domain_name="$(_value "$1" | sed -E "s/\.?${root_domain_name}"'$//')" _info "Matched host '$1' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." return 0 fi @@ -379,11 +383,11 @@ _pleskxml_get_root_domain() { # No match, try next parent up (if any)... if _contains "$root_domain_name" '\.[^.]+\.'; then - _debug "No match, trying next parent up..." + _debug "No match, trying next parent up..." else _debug "No match,and next parent would be a TLD..." fi - root_domain_name="$( _valuecut 2 1000 "$root_domain_name" )" + root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" doneloop=1 done From 9299a83b175c77feec281a8f8754283e8c6333a0 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:10:03 +0000 Subject: [PATCH 150/191] Travis fixes --- dnsapi/dns_pleskxml | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml index bed1b26..794127c 100644 --- a/dnsapi/dns_pleskxml +++ b/dnsapi/dns_pleskxml @@ -30,8 +30,7 @@ ## ## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed. - -#################### INTERNAL VARIABLES + NEWLINE ################################## +#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ################################## pleskxml_init_checks_done=0 @@ -40,9 +39,6 @@ pleskxml_init_checks_done=0 NEWLINE='\ ' - -#################### API Templates ################################## - pleskxml_tplt_get_domains="" # Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh # Also used to test credentials and URI. @@ -60,7 +56,6 @@ pleskxml_tplt_rmv_dns_record="%s # Add a TXT record to a domain. # ARG = the Plesk internal ID for the dns record to be deleted - #################### Public functions ################################## #Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" @@ -141,7 +136,7 @@ dns_pleskxml_rm() { reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ | grep "${root_domain_id}" \ | grep -E '[0-9]+' \ - | grep 'TXT' \ + | grep 'TXT' )" if [ -z "$reclist" ]; then @@ -154,8 +149,8 @@ dns_pleskxml_rm() { recid="$(_value "$reclist" \ | grep "$1." \ | grep "$txtvalue" \ - | sed -E 's/(^.*|<\/id>.*$)//g' \ - )" + | sed -E 's/(^.*|<\/id>.*$)//g' + )" _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" @@ -193,8 +188,6 @@ dns_pleskxml_rm() { return 0 } - - #################### Private functions below ################################## # Outputs value of a variable @@ -202,7 +195,6 @@ _value() { printf '%s' "$1" } - # Outputs value of a variable (FQDN) and cuts it at 2 delimiters # $1, $2 = where to cut # $3 = FQDN @@ -210,20 +202,18 @@ _valuecut() { printf '%s' "$3" | cut -d . -f "${1}-${2}" } - # Cleans up an API response, splits it "per item" and greps for a string to validate useful lines # $1 - result string from API # $2 - tag to resplit on (usually "result" or "domain") # $3 - regex to recognise useful return lines _api_response_split() { printf '%s' "$1" \ - | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ - | tr -d '\n\r' \ - | sed -E "s/<\/?$2>/${NEWLINE}/g" \ - | grep -E "$3" + | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ + | tr -d '\n\r' \ + | sed -E "s/<\/?$2>/${NEWLINE}/g" \ + | grep -E "$3" } - # Calls Plesk XML API, and checks results for obvious issues _call_api() { request="$1" @@ -249,11 +239,11 @@ _call_api() { if _value "$statuslines" | grep -qv 'ok'; then # We have some status lines that aren't "ok". Get the details - errtext="$( _value "$pleskxml_prettyprint_result" \ + errtext="$(_value "$pleskxml_prettyprint_result" \ | grep -iE "(||)" \ | sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' \ - | sed -E 's/^<([a-z]+)>/\1: /' \ - )" + | sed -E 's/^<([a-z]+)>/\1: /' + )" elif ! _value "$statuslines" | grep -q 'ok'; then @@ -278,10 +268,8 @@ _call_api() { return 0 } - +# Startup checks (credentials, URI) _credential_check() { - # Startup checks (credentials, URI) - _debug "Checking Plesk XML API login credentials and URI..." if [ "$pleskxml_init_checks_done" -eq 1 ]; then @@ -289,7 +277,6 @@ _credential_check() { return 0 fi - pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}" pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}" pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}" @@ -325,7 +312,6 @@ _credential_check() { return 0 } - # For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any. # IMPORTANT NOTE: a result with host = empty string is OK for this API, see @@ -369,7 +355,7 @@ _pleskxml_get_root_domain() { _debug "Checking if '$root_domain_name' is managed by the Plesk server..." - root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" + root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" if [ -n "$root_domain_id" ]; then # Found a match From 6df31eb7f58f32428de4a57971b882421d19b3f5 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:13:15 +0000 Subject: [PATCH 151/191] travis --- dnsapi/dns_pleskxml | 1 - 1 file changed, 1 deletion(-) diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml index 794127c..83017c3 100644 --- a/dnsapi/dns_pleskxml +++ b/dnsapi/dns_pleskxml @@ -77,7 +77,6 @@ dns_pleskxml_add() { _debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record' - # printf using template in a variable - not a style issue # shellcheck disable=SC2059 request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")" From 1253357a39206dd43047bdbbbd341ffef1d735ab Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 01:48:02 +0000 Subject: [PATCH 152/191] edits to comments --- dnsapi/dns_pleskxml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml index 83017c3..e2ec571 100644 --- a/dnsapi/dns_pleskxml +++ b/dnsapi/dns_pleskxml @@ -1,6 +1,6 @@ #!/usr/bin/env sh -## Name: dns_pleskxml.sh +## Name: dns_pleskxml ## Created by Stilez. ## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) @@ -13,14 +13,16 @@ ## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action. ## So this API module can handle such a request, if needed. +## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records. + ## The plesk plugin uses the xml api to add and remvoe the dns records. Therefore the url, username ## and password have to be configured by the user before this module is called. ## ## ``` -## export pleskxml_uri="https://YOUR_PLESK_URI_HERE:8443/enterprise/control/agent.php" +## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php" ## (or probably something similar) -## export pleskxml_user="plesk username" -## export pleskxml_pass="plesk password" +## export pleskxml_user="my plesk username" +## export pleskxml_pass="my plesk password" ## ``` ## Ok, let's issue a cert now: @@ -42,19 +44,19 @@ NEWLINE='\ pleskxml_tplt_get_domains="" # Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh # Also used to test credentials and URI. -# No args. +# No params. pleskxml_tplt_get_dns_records="%s" # Get all DNS records for a Plesk domain ID. -# ARG = Plesk domain id to query +# PARAM = Plesk domain id to query pleskxml_tplt_add_txt_record="%sTXT%s%s" # Add a TXT record to a domain. -# ARGS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value +# PARAMS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value pleskxml_tplt_rmv_dns_record="%s" -# Add a TXT record to a domain. -# ARG = the Plesk internal ID for the dns record to be deleted +# Delete a specific TXT record from a domain. +# PARAM = the Plesk internal ID for the DNS record to be deleted #################### Public functions ################################## @@ -63,7 +65,7 @@ dns_pleskxml_add() { fulldomain=$1 txtvalue=$2 - _info "Entering dns_pleskxml_add() to add TXT record '$2' to domain '$1'..." + _info "Entering dns_pleskxml_add() to add TXT record '$txtvalue' to domain '$fulldomain'..." # Get credentials if not already checked, and confirm we can log in to Plesk XML API if ! _credential_check; then @@ -110,7 +112,7 @@ dns_pleskxml_rm() { fulldomain=$1 txtvalue=$2 - _info "Entering dns_pleskxml_rm() to remove TXT record '$2' from domain '$1'..." + _info "Entering dns_pleskxml_rm() to remove TXT record '$txtvalue' from domain '$fulldomain'..." # Get credentials if not already checked, and confirm we can log in to Plesk XML API if ! _credential_check; then @@ -189,19 +191,19 @@ dns_pleskxml_rm() { #################### Private functions below ################################## -# Outputs value of a variable +# Outputs value of a variable without additional newlines etc _value() { printf '%s' "$1" } -# Outputs value of a variable (FQDN) and cuts it at 2 delimiters +# Outputs value of a variable (FQDN) and cuts it at 2 specified '.' delimiters, returning the text in between # $1, $2 = where to cut # $3 = FQDN _valuecut() { printf '%s' "$3" | cut -d . -f "${1}-${2}" } -# Cleans up an API response, splits it "per item" and greps for a string to validate useful lines +# Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines # $1 - result string from API # $2 - tag to resplit on (usually "result" or "domain") # $3 - regex to recognise useful return lines @@ -228,8 +230,6 @@ _call_api() { pleskxml_retcode="$?" _debug "acme _post() returned retcode=$pleskxml_retcode. Literal response:" '\n' "'${pleskxml_prettyprint_result}'" - # Error handling - # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. @@ -320,7 +320,7 @@ _credential_check() { _pleskxml_get_root_domain() { _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." - # test if the domain is valid for splitting. + # test if the domain as provided is valid for splitting. if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." @@ -334,10 +334,10 @@ _pleskxml_get_root_domain() { return 1 fi - # Generate a hacked list of domains known to this Plesk account. + # Generate a crude list of domains known to this Plesk account. # We convert tags to so it'll flag on a hit with either or fields, # for non-Western character sets. - # Output will be one line per known domain, containing 1 or 2 tages and an tag + # Output will be one line per known domain, containing 2 tages and a single tag # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" @@ -345,7 +345,7 @@ _pleskxml_get_root_domain() { _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" # loop and test if domain, or any parent domain, is managed by Plesk - # Loop until we don't have any '.' in the sring we're testing as a root domain + # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain root_domain_name="$1" doneloop=0 @@ -378,7 +378,7 @@ _pleskxml_get_root_domain() { done # if we get here, we failed to find a root domain match in the list of domains managed by Plesk. - # if we never ran the loop a first time, $1 wasn't at least a 2 level domain (domain.tld) and wasn't valid anyway + # if we never ran the loop even once, $1 wasn't a 2nd level (or deeper) domain (e.g. domain.tld) and wasn't valid anyway if [ -z $doneloop ]; then _err "'$1' isn't a valid domain for ACME DNS. Exiting." From 671edc33e173b0318972d7eb4fd50d03fbde85cd Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 27 Oct 2019 11:43:40 +0800 Subject: [PATCH 153/191] fix background color --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 37ce15a..07dc63d 100755 --- a/acme.sh +++ b/acme.sh @@ -153,7 +153,7 @@ fi __green() { if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[1;31;32m%b\033[0m' "$1" + printf '\33[1;32m%b\33[0m' "$1" return fi printf -- "%b" "$1" @@ -161,7 +161,7 @@ __green() { __red() { if [ "${__INTERACTIVE}${ACME_NO_COLOR:-0}" = "10" -o "${ACME_FORCE_COLOR}" = "1" ]; then - printf '\033[1;31;40m%b\033[0m' "$1" + printf '\33[1;31m%b\33[0m' "$1" return fi printf -- "%b" "$1" From 9eb5f65b8f272b71fdf2d69a33492e67b5b76836 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 07:38:22 +0000 Subject: [PATCH 154/191] edit comments --- dnsapi/dns_pleskxml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml index e2ec571..d613f8e 100644 --- a/dnsapi/dns_pleskxml +++ b/dnsapi/dns_pleskxml @@ -60,7 +60,7 @@ pleskxml_tplt_rmv_dns_record="%s #################### Public functions ################################## -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +#Usage: dns_pleskxml_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_pleskxml_add() { fulldomain=$1 txtvalue=$2 @@ -107,7 +107,7 @@ dns_pleskxml_add() { return 0 } -#Usage: rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +#Usage: dns_pleskxml_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_pleskxml_rm() { fulldomain=$1 txtvalue=$2 From 582c77805c837962129ea5372a664658739a13e1 Mon Sep 17 00:00:00 2001 From: peterkelm Date: Sun, 27 Oct 2019 13:13:22 +0100 Subject: [PATCH 155/191] variomedia dns api initial commit for the variomedia dns api implementation --- dnsapi/dns_variomedia.sh | 166 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 dnsapi/dns_variomedia.sh diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh new file mode 100644 index 0000000..56f1bf9 --- /dev/null +++ b/dnsapi/dns_variomedia.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env sh + +# +#VARIOMEDIA_API_TOKEN=000011112222333344445555666677778888 + +VARIOMEDIA_API="https://api.variomedia.de" + +######## Public functions ##################### + +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_variomedia_add() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}" + if test -z "$VARIOMEDIA_API_TOKEN"; then + VARIOMEDIA_API_TOKEN="" + _err 'VARIOMEDIA_API_TOKEN was not exported' + return 1 + fi + + _saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + +# _debug 'Getting txt records' +# _variomedia_rest GET "/dns-records?filter[domain]=$_domain" + +# if printf "%s\n" "$response" | grep "\"record_type\": \"A\", \"fqdn\": \"$fulldomain\"" >/dev/null; then +# _err 'Error' +# return 1 +# fi + + if ! _variomedia_rest POST "dns-records" "{\"data\": {\"type\": \"dns-record\", \"attributes\": {\"record_type\": \"TXT\", \"name\": \"$_sub_domain\", \"domain\": \"$_domain\", \"data\": \"$txtvalue\", \"ttl\":300}}}"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#fulldomain txtvalue +dns_variomedia_rm() { + fulldomain=$1 + txtvalue=$2 + _debug fulldomain "$fulldomain" + _debug txtvalue "$txtvalue" + + VARIOMEDIA_API_TOKEN="${VARIOMEDIA_API_TOKEN:-$(_readaccountconf_mutable VARIOMEDIA_API_TOKEN)}" + if test -z "$VARIOMEDIA_API_TOKEN"; then + VARIOMEDIA_API_TOKEN="" + _err 'VARIOMEDIA_API_TOKEN was not exported' + return 1 + fi + + _saveaccountconf_mutable VARIOMEDIA_API_TOKEN "$VARIOMEDIA_API_TOKEN" + + _debug 'First detect the root zone' + if ! _get_root "$fulldomain"; then + return 1 + fi + _debug _sub_domain "$_sub_domain" + _debug _domain "$_domain" + + _debug 'Getting txt records' + + if ! _variomedia_rest GET "dns-records?filter[domain]=$_domain"; then + _err 'Error' + return 1 + fi + + _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep $_sub_domain | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" + _debug _record_id "$_record_id" + if [ "$_record_id" ]; then + _info "Successfully retrieved the record id for ACME challenge." + else + _info "Empty record id, it seems no such record." + return 0 + fi + + if ! _variomedia_rest DELETE "/dns-records/$_record_id"; then + _err "$response" + return 1 + fi + + _debug2 _response "$response" + return 0 +} + +#################### Private functions below ################################## +#_acme-challenge.www.domain.com +#returns +# _sub_domain=_acme-challenge.www +# _domain=domain.com +_get_root() { + fulldomain=$1 + i=1 + while true; do + h=$(printf "%s" "$fulldomain" | cut -d . -f $i-100) + _debug h "$h" + if [ -z "$h" ]; then + return 1 + fi + + if ! _variomedia_rest GET "domains/$h"; then + return 1 + fi + + if _startswith "$response" "\{\"data\":"; then + if _contains "$response" "\"id\": \"$h\""; then + _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")" + _domain=$h + return 0 +# else +# _err 'Invalid domain' +# return 1 + fi +# else +# _err "$response" +# return 1 + fi + i=$(_math "$i" + 1) + done + + _debug "root domain not found" + + return 1 +} + +_variomedia_rest() { + m=$1 + ep="$2" + data="$3" + _debug "$ep" + +# api_key_trimmed=$(echo $VARIOMEDIA_API_TOKEN | tr -d '"') + +# export _H1="Api-Key: $api_key_trimmed" + + export _H1="Authorization: token $VARIOMEDIA_API_TOKEN" + export _H2="Content-Type: application/vnd.api+json" + export _H3="Accept: application/vnd.variomedia.v1+json" + + if [ "$m" != "GET" ]; then + _debug data "$data" + response="$(_post "$data" "$VARIOMEDIA_API/$ep" "" "$m")" + else + response="$(_get "$VARIOMEDIA_API/$ep")" + fi + + if [ "$?" != "0" ]; then + _err "Error $ep" + return 1 + fi + + _debug2 response "$response" + return 0 +} From 1271f97b669ee66727293beea3f2a39da2899e04 Mon Sep 17 00:00:00 2001 From: peterkelm Date: Sun, 27 Oct 2019 16:52:51 +0100 Subject: [PATCH 156/191] fixed dns_variomedia_rm for wildcard certs fixed dns_variomedia_rm to respect the txtvalue supplied as function parameter --- dnsapi/dns_variomedia.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index 56f1bf9..2e822a0 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -77,7 +77,7 @@ dns_variomedia_rm() { return 1 fi - _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep $_sub_domain | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" + _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep $_sub_domain | grep $txtvalue | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" _debug _record_id "$_record_id" if [ "$_record_id" ]; then _info "Successfully retrieved the record id for ACME challenge." From c1b089d1c3adb314daf29a084c3bf13042e72c7f Mon Sep 17 00:00:00 2001 From: peterkelm Date: Sun, 27 Oct 2019 16:58:36 +0100 Subject: [PATCH 157/191] unused code removed --- dnsapi/dns_variomedia.sh | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index 2e822a0..8588d7c 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -30,14 +30,6 @@ dns_variomedia_add() { _debug _sub_domain "$_sub_domain" _debug _domain "$_domain" -# _debug 'Getting txt records' -# _variomedia_rest GET "/dns-records?filter[domain]=$_domain" - -# if printf "%s\n" "$response" | grep "\"record_type\": \"A\", \"fqdn\": \"$fulldomain\"" >/dev/null; then -# _err 'Error' -# return 1 -# fi - if ! _variomedia_rest POST "dns-records" "{\"data\": {\"type\": \"dns-record\", \"attributes\": {\"record_type\": \"TXT\", \"name\": \"$_sub_domain\", \"domain\": \"$_domain\", \"data\": \"$txtvalue\", \"ttl\":300}}}"; then _err "$response" return 1 @@ -119,13 +111,7 @@ _get_root() { _sub_domain="$(echo "$fulldomain" | sed "s/\\.$h\$//")" _domain=$h return 0 -# else -# _err 'Invalid domain' -# return 1 fi -# else -# _err "$response" -# return 1 fi i=$(_math "$i" + 1) done @@ -141,10 +127,6 @@ _variomedia_rest() { data="$3" _debug "$ep" -# api_key_trimmed=$(echo $VARIOMEDIA_API_TOKEN | tr -d '"') - -# export _H1="Api-Key: $api_key_trimmed" - export _H1="Authorization: token $VARIOMEDIA_API_TOKEN" export _H2="Content-Type: application/vnd.api+json" export _H3="Accept: application/vnd.variomedia.v1+json" From bc291141b13a802ac0190ba06946ef2fa9add768 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 16:58:22 +0000 Subject: [PATCH 158/191] fix filename --- dnsapi/dns_pleskxml.sh | 391 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 dnsapi/dns_pleskxml.sh diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh new file mode 100644 index 0000000..25d1e6d --- /dev/null +++ b/dnsapi/dns_pleskxml.sh @@ -0,0 +1,391 @@ +#!/usr/bin/env sh + +## Name: dns_pleskxml.sh +## Created by Stilez. +## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) + +## This DNS01 method uses the Plesk XML API described at: +## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709 +## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784 + +## Note: a DNS ID with host = empty string is OK for this API, see +## https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 +## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action. +## So this API module can handle such a request, if needed. + +## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records. + +## The plesk plugin uses the xml api to add and remvoe the dns records. Therefore the url, username +## and password have to be configured by the user before this module is called. +## +## ``` +## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php" +## (or probably something similar) +## export pleskxml_user="my plesk username" +## export pleskxml_pass="my plesk password" +## ``` + +## Ok, let's issue a cert now: +## ``` +## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com +## ``` +## +## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed. + +#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ################################## + +pleskxml_init_checks_done=0 + +# Variable containing bare newline - not a style issue +# shellcheck disable=SC1004 +NEWLINE='\ +' + +pleskxml_tplt_get_domains="" +# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh +# Also used to test credentials and URI. +# No params. + +pleskxml_tplt_get_dns_records="%s" +# Get all DNS records for a Plesk domain ID. +# PARAM = Plesk domain id to query + +pleskxml_tplt_add_txt_record="%sTXT%s%s" +# Add a TXT record to a domain. +# PARAMS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value + +pleskxml_tplt_rmv_dns_record="%s" +# Delete a specific TXT record from a domain. +# PARAM = the Plesk internal ID for the DNS record to be deleted + +#################### Public functions ################################## + +#Usage: dns_pleskxml_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_pleskxml_add() { + fulldomain=$1 + txtvalue=$2 + + _info "Entering dns_pleskxml_add() to add TXT record '$txtvalue' to domain '$fulldomain'..." + + # Get credentials if not already checked, and confirm we can log in to Plesk XML API + if ! _credential_check; then + return 1 + fi + + # Get root and subdomain details, and Plesk domain ID + if ! _pleskxml_get_root_domain "$fulldomain"; then + return 1 + fi + + _debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record' + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")" + if ! _call_api "$request"; then + return 1 + fi + + # OK, we should have added a TXT record. Let's check and return success if so. + # All that should be left in the result, is one section, containing okNEW_DNS_RECORD_ID + + results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" + + if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + # Error - doesn't contain expected string. Something's wrong. + _err 'Error when calling Plesk XML API.' + _err 'The result did not contain the expected XXXXX section, or contained other values as well.' + _err 'This is unexpected: something has gone wrong.' + _err 'The full response was:\n' "$pleskxml_prettyprint_result" + return 1 + fi + + recid="$(_value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" + + _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." + + return 0 +} + +#Usage: dns_pleskxml_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +dns_pleskxml_rm() { + fulldomain=$1 + txtvalue=$2 + + _info "Entering dns_pleskxml_rm() to remove TXT record '$txtvalue' from domain '$fulldomain'..." + + # Get credentials if not already checked, and confirm we can log in to Plesk XML API + if ! _credential_check; then + return 1 + fi + + # Get root and subdomain details, and Plesk domain ID + if ! _pleskxml_get_root_domain "$fulldomain"; then + return 1 + fi + + _debug 'Credentials OK, and domain identified. Calling Plesk XML API to get list of TXT records and their IDs' + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$(printf "$pleskxml_tplt_get_dns_records" "$root_domain_id")" + if ! _call_api "$request"; then + return 1 + fi + + # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) + reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ + | grep "${root_domain_id}" \ + | grep -E '[0-9]+' \ + | grep 'TXT' + )" + + if [ -z "$reclist" ]; then + _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting." + return 1 + fi + + _debug "Got list of DNS TXT records for root domain '$root_domain_name'"':\n'"$reclist" + + recid="$(_value "$reclist" \ + | grep "$1." \ + | grep "$txtvalue" \ + | sed -E 's/(^.*|<\/id>.*$)//g' + )" + + _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" + + if ! _value "$recid" | grep -Eq '^[0-9]+$'; then + _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" + _err "Cannot delete TXT record. Exiting." + return 1 + fi + + _debug "Found Plesk record ID for target text string '${txtvalue}': ID=${recid}" + _debug 'Calling Plesk XML API to remove TXT record' + + # printf using template in a variable - not a style issue + # shellcheck disable=SC2059 + request="$(printf "$pleskxml_tplt_rmv_dns_record" "$recid")" + if ! _call_api "$request"; then + return 1 + fi + + # OK, we should have removed a TXT record. Let's check and return success if so. + # All that should be left in the result, is one section, containing okPLESK_DELETED_DNS_RECORD_ID + + results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" + + if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + # Error - doesn't contain expected string. Something's wrong. + _err 'Error when calling Plesk XML API.' + _err 'The result did not contain the expected XXXXX section, or contained other values as well.' + _err 'This is unexpected: something has gone wrong.' + _err 'The full response was:\n' "$pleskxml_prettyprint_result" + return 1 + fi + + _info "Success. TXT record appears to be correctly removed. Exiting dns_pleskxml_rm()." + return 0 +} + +#################### Private functions below ################################## + +# Outputs value of a variable without additional newlines etc +_value() { + printf '%s' "$1" +} + +# Outputs value of a variable (FQDN) and cuts it at 2 specified '.' delimiters, returning the text in between +# $1, $2 = where to cut +# $3 = FQDN +_valuecut() { + printf '%s' "$3" | cut -d . -f "${1}-${2}" +} + +# Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines +# $1 - result string from API +# $2 - tag to resplit on (usually "result" or "domain") +# $3 - regex to recognise useful return lines +_api_response_split() { + printf '%s' "$1" \ + | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ + | tr -d '\n\r' \ + | sed -E "s/<\/?$2>/${NEWLINE}/g" \ + | grep -E "$3" +} + +# Calls Plesk XML API, and checks results for obvious issues +_call_api() { + request="$1" + errtext='' + + _debug 'Entered _call_api(). Calling Plesk XML API with request:\n' "'${request}'" + + export _H1="HTTP_AUTH_LOGIN: $pleskxml_user" + export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass" + export _H3="content-Type: text/xml" + export _H4="HTTP_PRETTY_PRINT: true" + pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")" + pleskxml_retcode="$?" + _debug "acme _post() returned retcode=$pleskxml_retcode. Literal response:" '\n' "'${pleskxml_prettyprint_result}'" + + # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. + # Also detect if there simply aren't any status lines (null result?) and report that, as well. + + statuslines="$(echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$')" + + if _value "$statuslines" | grep -qv 'ok'; then + + # We have some status lines that aren't "ok". Get the details + errtext="$(_value "$pleskxml_prettyprint_result" \ + | grep -iE "(||)" \ + | sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' \ + | sed -E 's/^<([a-z]+)>/\1: /' + )" + + elif ! _value "$statuslines" | grep -q 'ok'; then + + # We have no status lines at all. Results are empty + errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.' + + fi + + if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then + _err "The Plesk XML API call failed." + _err "The return code for the POST request was $pleskxml_retcode (0=success)." + if [ "$errtext" != "" ]; then + _err 'Status and error messages received from the Plesk server:\n' "$errtext" + else + _err "No additional error messages were received back from the Plesk server" + fi + return 1 + fi + + _debug "Leaving _call_api(). Successful call." + + return 0 +} + +# Startup checks (credentials, URI) +_credential_check() { + _debug "Checking Plesk XML API login credentials and URI..." + + if [ "$pleskxml_init_checks_done" -eq 1 ]; then + _debug "Initial checks already done, no need to repeat. Skipped." + return 0 + fi + + pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}" + pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}" + pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}" + + _debug "Credentials - User: '${pleskxml_user}' Passwd: ****** URI: '${pleskxml_uri}'" + + if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then + pleskxml_user="" + pleskxml_pass="" + pleskxml_uri="" + _err "You didn't specify one or more of the Plesk XML API username, password, or URI." + _err "Please create these and try again." + _err "Instructions are in the module source code." + return 1 + fi + + # Test the API is usable, by trying to read the list of managed domains... + _call_api "$pleskxml_tplt_get_domains" + if [ "$pleskxml_retcode" -ne 0 ]; then + _err '\nFailed to access Plesk XML API.' + _err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again." + return 1 + fi + + _saveaccountconf_mutable pleskxml_uri "$pleskxml_uri" + _saveaccountconf_mutable pleskxml_user "$pleskxml_user" + _saveaccountconf_mutable pleskxml_pass "$pleskxml_pass" + + _debug "Test login to Plesk XML API successful. Login credentials and URI successfully saved to the acme.sh configuration file for future use." + + pleskxml_init_checks_done=1 + + return 0 +} + +# For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any. + +# IMPORTANT NOTE: a result with host = empty string is OK for this API, see +# https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 +# See notes at top of this file + +_pleskxml_get_root_domain() { + _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." + + # test if the domain as provided is valid for splitting. + + if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then + _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." + return 1 + fi + + _debug "Querying Plesk server for list of managed domains..." + + _call_api "$pleskxml_tplt_get_domains" + if [ "$pleskxml_retcode" -ne 0 ]; then + return 1 + fi + + # Generate a crude list of domains known to this Plesk account. + # We convert tags to so it'll flag on a hit with either or fields, + # for non-Western character sets. + # Output will be one line per known domain, containing 2 tages and a single tag + # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. + + output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" + + _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" + + # loop and test if domain, or any parent domain, is managed by Plesk + # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain + + root_domain_name="$1" + doneloop=0 + + while _contains "$root_domain_name" '\.'; do + + _debug "Checking if '$root_domain_name' is managed by the Plesk server..." + + root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" + + if [ -n "$root_domain_id" ]; then + # Found a match + # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. + # SO WE HANDLE IT AND DON'T PREVENT IT + sub_domain_name="$(_value "$1" | sed -E "s/\.?${root_domain_name}"'$//')" + _info "Matched host '$1' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." + return 0 + fi + + # No match, try next parent up (if any)... + + if _contains "$root_domain_name" '\.[^.]+\.'; then + _debug "No match, trying next parent up..." + else + _debug "No match,and next parent would be a TLD..." + fi + root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" + doneloop=1 + + done + + # if we get here, we failed to find a root domain match in the list of domains managed by Plesk. + # if we never ran the loop even once, $1 wasn't a 2nd level (or deeper) domain (e.g. domain.tld) and wasn't valid anyway + + if [ -z $doneloop ]; then + _err "'$1' isn't a valid domain for ACME DNS. Exiting." + else + _err "Cannot find '$1' or any parent domain of it, in Plesk." + _err "Are you sure that this domain is managed by this Plesk server?" + fi + + return 1 +} From 7c09bdc6e0dde113a241d1526ede4b9a01a7f864 Mon Sep 17 00:00:00 2001 From: stilez Date: Sun, 27 Oct 2019 16:58:58 +0000 Subject: [PATCH 159/191] renamed --- dnsapi/dns_pleskxml | 391 -------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 dnsapi/dns_pleskxml diff --git a/dnsapi/dns_pleskxml b/dnsapi/dns_pleskxml deleted file mode 100644 index d613f8e..0000000 --- a/dnsapi/dns_pleskxml +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/env sh - -## Name: dns_pleskxml -## Created by Stilez. -## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) - -## This DNS01 method uses the Plesk XML API described at: -## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709 -## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784 - -## Note: a DNS ID with host = empty string is OK for this API, see -## https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 -## For example, to add a TXT record to DNS alias domain "acme-alias.com" would be a valid Plesk action. -## So this API module can handle such a request, if needed. - -## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records. - -## The plesk plugin uses the xml api to add and remvoe the dns records. Therefore the url, username -## and password have to be configured by the user before this module is called. -## -## ``` -## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php" -## (or probably something similar) -## export pleskxml_user="my plesk username" -## export pleskxml_pass="my plesk password" -## ``` - -## Ok, let's issue a cert now: -## ``` -## acme.sh --issue --dns dns_pleskxml -d example.com -d www.example.com -## ``` -## -## The `pleskxml_uri`, `pleskxml_user` and `pleskxml_pass` will be saved in `~/.acme.sh/account.conf` and reused when needed. - -#################### INTERNAL VARIABLES + NEWLINE + API TEMPLATES ################################## - -pleskxml_init_checks_done=0 - -# Variable containing bare newline - not a style issue -# shellcheck disable=SC1004 -NEWLINE='\ -' - -pleskxml_tplt_get_domains="" -# Get a list of domains that PLESK can manage, so we can check root domain + host for acme.sh -# Also used to test credentials and URI. -# No params. - -pleskxml_tplt_get_dns_records="%s" -# Get all DNS records for a Plesk domain ID. -# PARAM = Plesk domain id to query - -pleskxml_tplt_add_txt_record="%sTXT%s%s" -# Add a TXT record to a domain. -# PARAMS = (1) Plesk internal domain ID, (2) "hostname" for the new record, eg '_acme_challenge', (3) TXT record value - -pleskxml_tplt_rmv_dns_record="%s" -# Delete a specific TXT record from a domain. -# PARAM = the Plesk internal ID for the DNS record to be deleted - -#################### Public functions ################################## - -#Usage: dns_pleskxml_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_pleskxml_add() { - fulldomain=$1 - txtvalue=$2 - - _info "Entering dns_pleskxml_add() to add TXT record '$txtvalue' to domain '$fulldomain'..." - - # Get credentials if not already checked, and confirm we can log in to Plesk XML API - if ! _credential_check; then - return 1 - fi - - # Get root and subdomain details, and Plesk domain ID - if ! _pleskxml_get_root_domain "$fulldomain"; then - return 1 - fi - - _debug 'Credentials OK, and domain identified. Calling Plesk XML API to add TXT record' - - # printf using template in a variable - not a style issue - # shellcheck disable=SC2059 - request="$(printf "$pleskxml_tplt_add_txt_record" "$root_domain_id" "$sub_domain_name" "$txtvalue")" - if ! _call_api "$request"; then - return 1 - fi - - # OK, we should have added a TXT record. Let's check and return success if so. - # All that should be left in the result, is one section, containing okNEW_DNS_RECORD_ID - - results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - - if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then - # Error - doesn't contain expected string. Something's wrong. - _err 'Error when calling Plesk XML API.' - _err 'The result did not contain the expected XXXXX section, or contained other values as well.' - _err 'This is unexpected: something has gone wrong.' - _err 'The full response was:\n' "$pleskxml_prettyprint_result" - return 1 - fi - - recid="$(_value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" - - _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." - - return 0 -} - -#Usage: dns_pleskxml_rm _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" -dns_pleskxml_rm() { - fulldomain=$1 - txtvalue=$2 - - _info "Entering dns_pleskxml_rm() to remove TXT record '$txtvalue' from domain '$fulldomain'..." - - # Get credentials if not already checked, and confirm we can log in to Plesk XML API - if ! _credential_check; then - return 1 - fi - - # Get root and subdomain details, and Plesk domain ID - if ! _pleskxml_get_root_domain "$fulldomain"; then - return 1 - fi - - _debug 'Credentials OK, and domain identified. Calling Plesk XML API to get list of TXT records and their IDs' - - # printf using template in a variable - not a style issue - # shellcheck disable=SC2059 - request="$(printf "$pleskxml_tplt_get_dns_records" "$root_domain_id")" - if ! _call_api "$request"; then - return 1 - fi - - # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) - reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ - | grep "${root_domain_id}" \ - | grep -E '[0-9]+' \ - | grep 'TXT' - )" - - if [ -z "$reclist" ]; then - _err "No TXT records found for root domain ${root_domain_name} (Plesk domain ID ${root_domain_id}). Exiting." - return 1 - fi - - _debug "Got list of DNS TXT records for root domain '$root_domain_name'"':\n'"$reclist" - - recid="$(_value "$reclist" \ - | grep "$1." \ - | grep "$txtvalue" \ - | sed -E 's/(^.*|<\/id>.*$)//g' - )" - - _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - - if ! _value "$recid" | grep -Eq '^[0-9]+$'; then - _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" - _err "Cannot delete TXT record. Exiting." - return 1 - fi - - _debug "Found Plesk record ID for target text string '${txtvalue}': ID=${recid}" - _debug 'Calling Plesk XML API to remove TXT record' - - # printf using template in a variable - not a style issue - # shellcheck disable=SC2059 - request="$(printf "$pleskxml_tplt_rmv_dns_record" "$recid")" - if ! _call_api "$request"; then - return 1 - fi - - # OK, we should have removed a TXT record. Let's check and return success if so. - # All that should be left in the result, is one section, containing okPLESK_DELETED_DNS_RECORD_ID - - results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - - if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then - # Error - doesn't contain expected string. Something's wrong. - _err 'Error when calling Plesk XML API.' - _err 'The result did not contain the expected XXXXX section, or contained other values as well.' - _err 'This is unexpected: something has gone wrong.' - _err 'The full response was:\n' "$pleskxml_prettyprint_result" - return 1 - fi - - _info "Success. TXT record appears to be correctly removed. Exiting dns_pleskxml_rm()." - return 0 -} - -#################### Private functions below ################################## - -# Outputs value of a variable without additional newlines etc -_value() { - printf '%s' "$1" -} - -# Outputs value of a variable (FQDN) and cuts it at 2 specified '.' delimiters, returning the text in between -# $1, $2 = where to cut -# $3 = FQDN -_valuecut() { - printf '%s' "$3" | cut -d . -f "${1}-${2}" -} - -# Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines -# $1 - result string from API -# $2 - tag to resplit on (usually "result" or "domain") -# $3 - regex to recognise useful return lines -_api_response_split() { - printf '%s' "$1" \ - | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ - | tr -d '\n\r' \ - | sed -E "s/<\/?$2>/${NEWLINE}/g" \ - | grep -E "$3" -} - -# Calls Plesk XML API, and checks results for obvious issues -_call_api() { - request="$1" - errtext='' - - _debug 'Entered _call_api(). Calling Plesk XML API with request:\n' "'${request}'" - - export _H1="HTTP_AUTH_LOGIN: $pleskxml_user" - export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass" - export _H3="content-Type: text/xml" - export _H4="HTTP_PRETTY_PRINT: true" - pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")" - pleskxml_retcode="$?" - _debug "acme _post() returned retcode=$pleskxml_retcode. Literal response:" '\n' "'${pleskxml_prettyprint_result}'" - - # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. - # Also detect if there simply aren't any status lines (null result?) and report that, as well. - - statuslines="$(echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$')" - - if _value "$statuslines" | grep -qv 'ok'; then - - # We have some status lines that aren't "ok". Get the details - errtext="$(_value "$pleskxml_prettyprint_result" \ - | grep -iE "(||)" \ - | sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' \ - | sed -E 's/^<([a-z]+)>/\1: /' - )" - - elif ! _value "$statuslines" | grep -q 'ok'; then - - # We have no status lines at all. Results are empty - errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.' - - fi - - if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then - _err "The Plesk XML API call failed." - _err "The return code for the POST request was $pleskxml_retcode (0=success)." - if [ "$errtext" != "" ]; then - _err 'Status and error messages received from the Plesk server:\n' "$errtext" - else - _err "No additional error messages were received back from the Plesk server" - fi - return 1 - fi - - _debug "Leaving _call_api(). Successful call." - - return 0 -} - -# Startup checks (credentials, URI) -_credential_check() { - _debug "Checking Plesk XML API login credentials and URI..." - - if [ "$pleskxml_init_checks_done" -eq 1 ]; then - _debug "Initial checks already done, no need to repeat. Skipped." - return 0 - fi - - pleskxml_user="${pleskxml_user:-$(_readaccountconf_mutable pleskxml_user)}" - pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}" - pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}" - - _debug "Credentials - User: '${pleskxml_user}' Passwd: ****** URI: '${pleskxml_uri}'" - - if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then - pleskxml_user="" - pleskxml_pass="" - pleskxml_uri="" - _err "You didn't specify one or more of the Plesk XML API username, password, or URI." - _err "Please create these and try again." - _err "Instructions are in the module source code." - return 1 - fi - - # Test the API is usable, by trying to read the list of managed domains... - _call_api "$pleskxml_tplt_get_domains" - if [ "$pleskxml_retcode" -ne 0 ]; then - _err '\nFailed to access Plesk XML API.' - _err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again." - return 1 - fi - - _saveaccountconf_mutable pleskxml_uri "$pleskxml_uri" - _saveaccountconf_mutable pleskxml_user "$pleskxml_user" - _saveaccountconf_mutable pleskxml_pass "$pleskxml_pass" - - _debug "Test login to Plesk XML API successful. Login credentials and URI successfully saved to the acme.sh configuration file for future use." - - pleskxml_init_checks_done=1 - - return 0 -} - -# For a FQDN, identify the root domain managed by Plesk, its domain ID in Plesk, and the host if any. - -# IMPORTANT NOTE: a result with host = empty string is OK for this API, see -# https://docs.plesk.com/en-US/obsidian/api-rpc/about-xml-api/reference/managing-dns/managing-dns-records/adding-dns-record.34798 -# See notes at top of this file - -_pleskxml_get_root_domain() { - _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." - - # test if the domain as provided is valid for splitting. - - if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then - _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." - return 1 - fi - - _debug "Querying Plesk server for list of managed domains..." - - _call_api "$pleskxml_tplt_get_domains" - if [ "$pleskxml_retcode" -ne 0 ]; then - return 1 - fi - - # Generate a crude list of domains known to this Plesk account. - # We convert tags to so it'll flag on a hit with either or fields, - # for non-Western character sets. - # Output will be one line per known domain, containing 2 tages and a single tag - # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. - - output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" - - _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" - - # loop and test if domain, or any parent domain, is managed by Plesk - # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain - - root_domain_name="$1" - doneloop=0 - - while _contains "$root_domain_name" '\.'; do - - _debug "Checking if '$root_domain_name' is managed by the Plesk server..." - - root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" - - if [ -n "$root_domain_id" ]; then - # Found a match - # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. - # SO WE HANDLE IT AND DON'T PREVENT IT - sub_domain_name="$(_value "$1" | sed -E "s/\.?${root_domain_name}"'$//')" - _info "Matched host '$1' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." - return 0 - fi - - # No match, try next parent up (if any)... - - if _contains "$root_domain_name" '\.[^.]+\.'; then - _debug "No match, trying next parent up..." - else - _debug "No match,and next parent would be a TLD..." - fi - root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" - doneloop=1 - - done - - # if we get here, we failed to find a root domain match in the list of domains managed by Plesk. - # if we never ran the loop even once, $1 wasn't a 2nd level (or deeper) domain (e.g. domain.tld) and wasn't valid anyway - - if [ -z $doneloop ]; then - _err "'$1' isn't a valid domain for ACME DNS. Exiting." - else - _err "Cannot find '$1' or any parent domain of it, in Plesk." - _err "Are you sure that this domain is managed by this Plesk server?" - fi - - return 1 -} From a22d3b239070ddc573a283bf2a709e68a12d2085 Mon Sep 17 00:00:00 2001 From: scottkof Date: Mon, 28 Oct 2019 06:32:08 -0700 Subject: [PATCH 160/191] Switch from `sleep` to `_sleep` --- dnsapi/dns_aws.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dnsapi/dns_aws.sh b/dnsapi/dns_aws.sh index 54b1bb3..6db8766 100755 --- a/dnsapi/dns_aws.sh +++ b/dnsapi/dns_aws.sh @@ -6,7 +6,7 @@ #AWS_SECRET_ACCESS_KEY="xxxxxxx" #This is the Amazon Route53 api wrapper for acme.sh -#All `sleep` commands are included to avoid Route53 throttling, see +#All `_sleep` commands are included to avoid Route53 throttling, see #https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html#limits-api-requests AWS_HOST="route53.amazonaws.com" @@ -45,7 +45,7 @@ dns_aws_add() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" - sleep 1 + _sleep 1 return 1 fi _debug _domain_id "$_domain_id" @@ -54,7 +54,7 @@ dns_aws_add() { _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then - sleep 1 + _sleep 1 return 1 fi @@ -67,7 +67,7 @@ dns_aws_add() { if [ "$_resource_record" ] && _contains "$response" "$txtvalue"; then _info "The TXT record already exists. Skipping." - sleep 1 + _sleep 1 return 0 fi @@ -77,10 +77,10 @@ dns_aws_add() { if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "TXT record updated successfully." - sleep 1 + _sleep 1 return 0 fi - sleep 1 + _sleep 1 return 1 } @@ -99,7 +99,7 @@ dns_aws_rm() { _debug "First detect the root zone" if ! _get_root "$fulldomain"; then _err "invalid domain" - sleep 1 + _sleep 1 return 1 fi _debug _domain_id "$_domain_id" @@ -108,7 +108,7 @@ dns_aws_rm() { _info "Getting existing records for $fulldomain" if ! aws_rest GET "2013-04-01$_domain_id/rrset" "name=$fulldomain&type=TXT"; then - sleep 1 + _sleep 1 return 1 fi @@ -117,7 +117,7 @@ dns_aws_rm() { _debug "_resource_record" "$_resource_record" else _debug "no records exist, skip" - sleep 1 + _sleep 1 return 0 fi @@ -125,10 +125,10 @@ dns_aws_rm() { if aws_rest POST "2013-04-01$_domain_id/rrset/" "" "$_aws_tmpl_xml" && _contains "$response" "ChangeResourceRecordSetsResponse"; then _info "TXT record deleted successfully." - sleep 1 + _sleep 1 return 0 fi - sleep 1 + _sleep 1 return 1 } From d7affad05981f3fdc59ecf1f30e4455f06cc9f5a Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 29 Oct 2019 10:30:00 +0000 Subject: [PATCH 161/191] various small improves --- dnsapi/dns_pleskxml.sh | 71 +++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 25d1e6d..bd6eaa8 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -189,7 +189,7 @@ dns_pleskxml_rm() { return 0 } -#################### Private functions below ################################## +#################### Private functions below (utility functions) ################################## # Outputs value of a variable without additional newlines etc _value() { @@ -203,6 +203,12 @@ _valuecut() { printf '%s' "$3" | cut -d . -f "${1}-${2}" } +# Counts '.' present in a domain name +# $1 = domain name +_countdots() { + _value "$1" | tr -dc '.' | wc -c +} + # Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines # $1 - result string from API # $2 - tag to resplit on (usually "result" or "domain") @@ -215,6 +221,8 @@ _api_response_split() { | grep -E "$3" } +#################### Private functions below (DNS functions) ################################## + # Calls Plesk XML API, and checks results for obvious issues _call_api() { request="$1" @@ -228,7 +236,7 @@ _call_api() { export _H4="HTTP_PRETTY_PRINT: true" pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")" pleskxml_retcode="$?" - _debug "acme _post() returned retcode=$pleskxml_retcode. Literal response:" '\n' "'${pleskxml_prettyprint_result}'" + _debug 'The responses from the Plesk XML server were:\n' "retcode=$pleskxml_retcode. Literal response:"'\n' "'$pleskxml_prettyprint_result'" # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. @@ -239,9 +247,9 @@ _call_api() { # We have some status lines that aren't "ok". Get the details errtext="$(_value "$pleskxml_prettyprint_result" \ - | grep -iE "(||)" \ - | sed -E 's/(^[[:space:]]+|<\/[a-z]+$)//g' \ - | sed -E 's/^<([a-z]+)>/\1: /' + | grep -E "(||)" \ + | sed -E 's/^<(status|errcode|errtext)>/\1: /' \ + | sed -E 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' \ )" elif ! _value "$statuslines" | grep -q 'ok'; then @@ -252,14 +260,23 @@ _call_api() { fi if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then - _err "The Plesk XML API call failed." - _err "The return code for the POST request was $pleskxml_retcode (0=success)." + # Call failed, for reasons either in the retcode or the response text... + + if [ "$pleskxml_retcode" -eq 0 ]; then + _err "The POST request was successfully sent to the Plesk server." + else + _err "The return code for the POST request was $pleskxml_retcode (non-zero = could not submit request to server)." + fi + if [ "$errtext" != "" ]; then - _err 'Status and error messages received from the Plesk server:\n' "$errtext" + _err 'The error responses received from the Plesk server were:\n' "$errtext" else _err "No additional error messages were received back from the Plesk server" fi + + _err "The Plesk XML API call failed." return 1 + fi _debug "Leaving _call_api(). Successful call." @@ -319,10 +336,12 @@ _credential_check() { _pleskxml_get_root_domain() { _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." + original_full_domain_name="$1" + root_domain_name="$1" # test if the domain as provided is valid for splitting. - if _value "$root_domain_name" | grep -qvE '^[^.]+\.[^.]+\.[^.]'; then + if ! _countdots "$root_domain_name"; then _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." return 1 fi @@ -347,10 +366,7 @@ _pleskxml_get_root_domain() { # loop and test if domain, or any parent domain, is managed by Plesk # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain - root_domain_name="$1" - doneloop=0 - - while _contains "$root_domain_name" '\.'; do + while true; do _debug "Checking if '$root_domain_name' is managed by the Plesk server..." @@ -360,32 +376,23 @@ _pleskxml_get_root_domain() { # Found a match # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. # SO WE HANDLE IT AND DON'T PREVENT IT - sub_domain_name="$(_value "$1" | sed -E "s/\.?${root_domain_name}"'$//')" - _info "Matched host '$1' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." + sub_domain_name="$(_value "$original_full_domain_name" | sed -E "s/\.?${root_domain_name}"'$//')" + _info "Success. Matched host '$original_full_domain_name' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." return 0 fi # No match, try next parent up (if any)... - if _contains "$root_domain_name" '\.[^.]+\.'; then - _debug "No match, trying next parent up..." - else - _debug "No match,and next parent would be a TLD..." - fi root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" - doneloop=1 - done + if ! _countdots "$root_domain_name"; then + _debug "No match, and next parent would be a TLD..." + _err "Cannot find '$original_full_domain_name' or any parent domain of it, in Plesk." + _err "Are you sure that this domain is managed by this Plesk server?" + return 1 + fi - # if we get here, we failed to find a root domain match in the list of domains managed by Plesk. - # if we never ran the loop even once, $1 wasn't a 2nd level (or deeper) domain (e.g. domain.tld) and wasn't valid anyway + _debug "No match, trying next parent up..." - if [ -z $doneloop ]; then - _err "'$1' isn't a valid domain for ACME DNS. Exiting." - else - _err "Cannot find '$1' or any parent domain of it, in Plesk." - _err "Are you sure that this domain is managed by this Plesk server?" - fi - - return 1 + done } From b7c3df455e275a6fa0556d178c48f944666410bf Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 09:38:03 +0000 Subject: [PATCH 162/191] travis fix --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index bd6eaa8..aa1da6f 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -249,7 +249,7 @@ _call_api() { errtext="$(_value "$pleskxml_prettyprint_result" \ | grep -E "(||)" \ | sed -E 's/^<(status|errcode|errtext)>/\1: /' \ - | sed -E 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' \ + | sed -E 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' )" elif ! _value "$statuslines" | grep -q 'ok'; then From 05ced9fbc4edf0c2d3bbe9913209e6c750702cad Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 09:53:40 +0000 Subject: [PATCH 163/191] edit a comment --- dnsapi/dns_pleskxml.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index aa1da6f..24650d1 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -4,7 +4,7 @@ ## Created by Stilez. ## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) -## This DNS01 method uses the Plesk XML API described at: +## This DNS-01 method uses the Plesk XML API described at: ## https://docs.plesk.com/en-US/12.5/api-rpc/about-xml-api.28709 ## and more specifically: https://docs.plesk.com/en-US/12.5/api-rpc/reference.28784 @@ -15,8 +15,8 @@ ## For ACME v2 purposes, new TXT records are appended when added, and removing one TXT record will not affect any other TXT records. -## The plesk plugin uses the xml api to add and remvoe the dns records. Therefore the url, username -## and password have to be configured by the user before this module is called. +## The user credentials (username+password) and URL/URI for the Plesk XML API must be set by the user +## before this module is called (case sensitive): ## ## ``` ## export pleskxml_uri="https://address-of-my-plesk-server.net:8443/enterprise/control/agent.php" From 3441bd0e7c476a5add8dbfec730b5fa25b7a9f7e Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 10:22:04 +0000 Subject: [PATCH 164/191] improve _err message and remove a dubious _debug message. --- dnsapi/dns_pleskxml.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 24650d1..8a957e4 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -297,15 +297,13 @@ _credential_check() { pleskxml_pass="${pleskxml_pass:-$(_readaccountconf_mutable pleskxml_pass)}" pleskxml_uri="${pleskxml_uri:-$(_readaccountconf_mutable pleskxml_uri)}" - _debug "Credentials - User: '${pleskxml_user}' Passwd: ****** URI: '${pleskxml_uri}'" - if [ -z "$pleskxml_user" ] || [ -z "$pleskxml_pass" ] || [ -z "$pleskxml_uri" ]; then pleskxml_user="" pleskxml_pass="" pleskxml_uri="" _err "You didn't specify one or more of the Plesk XML API username, password, or URI." _err "Please create these and try again." - _err "Instructions are in the module source code." + _err "Instructions are in the 'dns_pleskxml' plugin source code or in the acme.sh documentation." return 1 fi From 2422e0b481b74e008fc995afd4e1994d6f0817e4 Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 17:01:06 +0000 Subject: [PATCH 165/191] grep -E and sed -E --- dnsapi/dns_pleskxml.sh | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 8a957e4..03c5853 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -91,7 +91,7 @@ dns_pleskxml_add() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + if ! _value "$results" | grep 'ok' | egrep -q '[0-9]+'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -100,7 +100,7 @@ dns_pleskxml_add() { return 1 fi - recid="$(_value "$results" | grep -E '[0-9]+' | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" + recid="$(_value "$results" | egrep '[0-9]+' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -136,7 +136,7 @@ dns_pleskxml_rm() { # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ | grep "${root_domain_id}" \ - | grep -E '[0-9]+' \ + | egrep '[0-9]+' \ | grep 'TXT' )" @@ -150,12 +150,12 @@ dns_pleskxml_rm() { recid="$(_value "$reclist" \ | grep "$1." \ | grep "$txtvalue" \ - | sed -E 's/(^.*|<\/id>.*$)//g' + | sed -r 's/(^.*|<\/id>.*$)//g' )" _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - if ! _value "$recid" | grep -Eq '^[0-9]+$'; then + if ! _value "$recid" | egrep -q '^[0-9]+$'; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" _err "Cannot delete TXT record. Exiting." return 1 @@ -176,7 +176,7 @@ dns_pleskxml_rm() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -qE '[0-9]+'; then + if ! _value "$results" | grep 'ok' | egrep -q '[0-9]+'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -215,10 +215,10 @@ _countdots() { # $3 - regex to recognise useful return lines _api_response_split() { printf '%s' "$1" \ - | sed -E 's/(^[[:space:]]+|[[:space:]]+$)//g' \ + | sed -r 's/(^[[:space:]]+|[[:space:]]+$)//g' \ | tr -d '\n\r' \ - | sed -E "s/<\/?$2>/${NEWLINE}/g" \ - | grep -E "$3" + | sed -r "s/<\/?$2>/${NEWLINE}/g" \ + | egrep "$3" } #################### Private functions below (DNS functions) ################################## @@ -241,15 +241,15 @@ _call_api() { # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. - statuslines="$(echo "$pleskxml_prettyprint_result" | grep -E '^[[:space:]]*[^<]*[[:space:]]*$')" + statuslines="$(echo "$pleskxml_prettyprint_result" | egrep '^[[:space:]]*[^<]*[[:space:]]*$')" if _value "$statuslines" | grep -qv 'ok'; then # We have some status lines that aren't "ok". Get the details errtext="$(_value "$pleskxml_prettyprint_result" \ - | grep -E "(||)" \ - | sed -E 's/^<(status|errcode|errtext)>/\1: /' \ - | sed -E 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' + | egrep "(||)" \ + | sed -r 's/^<(status|errcode|errtext)>/\1: /' \ + | sed -r 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' )" elif ! _value "$statuslines" | grep -q 'ok'; then @@ -357,7 +357,7 @@ _pleskxml_get_root_domain() { # Output will be one line per known domain, containing 2 tages and a single tag # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. - output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -E 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" + output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -r 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" @@ -368,13 +368,13 @@ _pleskxml_get_root_domain() { _debug "Checking if '$root_domain_name' is managed by the Plesk server..." - root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -E 's/^.*([0-9]+)<\/id>.*$/\1/')" + root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" if [ -n "$root_domain_id" ]; then # Found a match # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. # SO WE HANDLE IT AND DON'T PREVENT IT - sub_domain_name="$(_value "$original_full_domain_name" | sed -E "s/\.?${root_domain_name}"'$//')" + sub_domain_name="$(_value "$original_full_domain_name" | sed -r "s/\.?${root_domain_name}"'$//')" _info "Success. Matched host '$original_full_domain_name' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." return 0 fi From a32b95544ba8aa385251f757940c93f3fef266c0 Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 17:06:03 +0000 Subject: [PATCH 166/191] [[:space:]] -> " " --- dnsapi/dns_pleskxml.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 03c5853..12f5631 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -215,7 +215,7 @@ _countdots() { # $3 - regex to recognise useful return lines _api_response_split() { printf '%s' "$1" \ - | sed -r 's/(^[[:space:]]+|[[:space:]]+$)//g' \ + | sed -r 's/(^ +| +$)//g' \ | tr -d '\n\r' \ | sed -r "s/<\/?$2>/${NEWLINE}/g" \ | egrep "$3" @@ -241,15 +241,15 @@ _call_api() { # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. - statuslines="$(echo "$pleskxml_prettyprint_result" | egrep '^[[:space:]]*[^<]*[[:space:]]*$')" + statuslines="$(echo "$pleskxml_prettyprint_result" | egrep '^ *[^<]* *$')" if _value "$statuslines" | grep -qv 'ok'; then # We have some status lines that aren't "ok". Get the details errtext="$(_value "$pleskxml_prettyprint_result" \ | egrep "(||)" \ - | sed -r 's/^<(status|errcode|errtext)>/\1: /' \ - | sed -r 's/(^[[:space:]]+|<\/(status|errcode|errtext)>$)//g' + | sed -r 's/^ *<(status|errcode|errtext)>/\1: /' \ + | sed -r 's/<\/(status|errcode|errtext)>$//g' )" elif ! _value "$statuslines" | grep -q 'ok'; then From dca6a4bbd57eed4b87f24cb7f1644fb5a51c2327 Mon Sep 17 00:00:00 2001 From: peterkelm Date: Wed, 30 Oct 2019 20:51:16 +0100 Subject: [PATCH 167/191] minor formatting changes --- dnsapi/dns_variomedia.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index 8588d7c..ecc9ea4 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -5,9 +5,9 @@ VARIOMEDIA_API="https://api.variomedia.de" -######## Public functions ##################### +######## Public functions ##################### -#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" +#Usage: add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs" dns_variomedia_add() { fulldomain=$1 txtvalue=$2 @@ -69,7 +69,7 @@ dns_variomedia_rm() { return 1 fi - _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep $_sub_domain | grep $txtvalue | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" + _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" _debug _record_id "$_record_id" if [ "$_record_id" ]; then _info "Successfully retrieved the record id for ACME challenge." @@ -87,7 +87,7 @@ dns_variomedia_rm() { return 0 } -#################### Private functions below ################################## +#################### Private functions below ################################## #_acme-challenge.www.domain.com #returns # _sub_domain=_acme-challenge.www @@ -117,7 +117,6 @@ _get_root() { done _debug "root domain not found" - return 1 } From 343d7df57c366fe594edf8f4a523611a18cb0ac2 Mon Sep 17 00:00:00 2001 From: stilez Date: Wed, 30 Oct 2019 22:11:16 +0000 Subject: [PATCH 168/191] shellcheck directive --- dnsapi/dns_pleskxml.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 12f5631..c12b2ee 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -1,5 +1,9 @@ #!/usr/bin/env sh +# Globally disable this shellcheck error. +# Shellcheck errors on egrep ("deprecated"), but acme.sh uses egrep for compatibility. +# shellcheck disable=SC2196 + ## Name: dns_pleskxml.sh ## Created by Stilez. ## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) From bec26ce754fb575a6969c917cd8fd5fa212a800e Mon Sep 17 00:00:00 2001 From: peterkelm Date: Thu, 31 Oct 2019 09:03:35 +0100 Subject: [PATCH 169/191] Shellcheck'd --- dnsapi/dns_variomedia.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_variomedia.sh b/dnsapi/dns_variomedia.sh index ecc9ea4..729cda5 100644 --- a/dnsapi/dns_variomedia.sh +++ b/dnsapi/dns_variomedia.sh @@ -69,7 +69,7 @@ dns_variomedia_rm() { return 1 fi - _record_id="$(echo $response | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" + _record_id="$(echo "$response" | cut -d '[' -f2 | cut -d']' -f1 | sed 's/},[ \t]*{/\},§\{/g' | tr § '\n' | grep "$_sub_domain" | grep "$txtvalue" | sed 's/^{//;s/}[,]?$//' | tr , '\n' | tr -d '\"' | grep ^id | cut -d : -f2 | tr -d ' ')" _debug _record_id "$_record_id" if [ "$_record_id" ]; then _info "Successfully retrieved the record id for ACME challenge." From fee9baca895cecf5cb7a6d94791b655f09c73684 Mon Sep 17 00:00:00 2001 From: Johann Richard <189003+johannrichard@users.noreply.github.com> Date: Fri, 1 Nov 2019 17:59:40 +0100 Subject: [PATCH 170/191] Add openssh package * `acme.sh`'s `ssh.sh` is probably one of the hooks that's most versatile * By default, it's not installed on `alpine` docker images and therefore is lacking in the `acme.sh` docker image * This change adds the `openssh` package and therefore the `ssh` and associated commands --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index a6e3799..02dd503 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM alpine:3.10 RUN apk update -f \ && apk --no-cache add -f \ openssl \ + openssh \ coreutils \ bind-tools \ curl \ From 5698bec6213d8c1ffa8c3c30b1587c8f982638c8 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Nov 2019 09:48:41 +0800 Subject: [PATCH 171/191] fix https://github.com/Neilpang/acme.sh/issues/2566 --- acme.sh | 2 +- notify/dingtalk.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 notify/dingtalk.sh diff --git a/acme.sh b/acme.sh index 07dc63d..7860433 100755 --- a/acme.sh +++ b/acme.sh @@ -6070,7 +6070,7 @@ _send_notify() { _set_notify_hook() { _nhooks="$1" - _test_subject="Hello, this is notification from $PROJECT_NAME" + _test_subject="Hello, this is a notification from $PROJECT_NAME" _test_content="If you receive this message, your notification works." _send_notify "$_test_subject" "$_test_content" "$_nhooks" 0 diff --git a/notify/dingtalk.sh b/notify/dingtalk.sh new file mode 100644 index 0000000..7d354da --- /dev/null +++ b/notify/dingtalk.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh + +#Support dingtalk webhooks api + +#DINGTALK_WEBHOOK="xxxx" + +#optional +#DINGTALK_KEYWORD="yyyy" + +#DINGTALK_SIGNING_KEY="SEC08ffdbd403cbc3fc8a65xxxxxxxxxxxxxxxxxxxx" + +# subject content statusCode +dingtalk_send() { + _subject="$1" + _content="$2" + _statusCode="$3" #0: success, 1: error 2($RENEW_SKIP): skipped + _debug "_subject" "$_subject" + _debug "_content" "$_content" + _debug "_statusCode" "$_statusCode" + + DINGTALK_WEBHOOK="${DINGTALK_WEBHOOK:-$(_readaccountconf_mutable DINGTALK_WEBHOOK)}" + if [ -z "$DINGTALK_WEBHOOK" ]; then + DINGTALK_WEBHOOK="" + _err "You didn't specify a dingtalk webhooks DINGTALK_WEBHOOK yet." + _err "You can get yours from https://dingtalk.com" + return 1 + fi + _saveaccountconf_mutable DINGTALK_WEBHOOK "$DINGTALK_WEBHOOK" + + DINGTALK_KEYWORD="${DINGTALK_KEYWORD:-$(_readaccountconf_mutable DINGTALK_KEYWORD)}" + if [ "$DINGTALK_KEYWORD" ]; then + _saveaccountconf_mutable DINGTALK_KEYWORD "$DINGTALK_KEYWORD" + fi + +# DINGTALK_SIGNING_KEY="${DINGTALK_SIGNING_KEY:-$(_readaccountconf_mutable DINGTALK_SIGNING_KEY)}" +# if [ -z "$DINGTALK_SIGNING_KEY" ]; then +# DINGTALK_SIGNING_KEY="value1" +# _info "The DINGTALK_SIGNING_KEY is not set, so use the default value1 as key." +# elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$DINGTALK_SIGNING_KEY"; then +# _err "The DINGTALK_SIGNING_KEY \"$DINGTALK_SIGNING_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS" +# DINGTALK_SIGNING_KEY="" +# return 1 +# else +# _saveaccountconf_mutable DINGTALK_SIGNING_KEY "$DINGTALK_SIGNING_KEY" +# fi + +# if [ "$DINGTALK_SIGNING_KEY" = "$IFTTT_CONTENT_KEY" ]; then +# DINGTALK_SIGNING_KEY="" +# IFTTT_CONTENT_KEY="" +# _err "The DINGTALK_SIGNING_KEY must not be same as IFTTT_CONTENT_KEY." +# return 1 +# fi + + _content=$(echo "$_content" | _json_encode) + _subject=$(echo "$_subject" | _json_encode) + _data="{\"msgtype\": \"text\", \"text\": {\"content\": \"[$DINGTALK_KEYWORD]\n$_subject\n$_content\"}}" + + response="$(_post "$_data" "$DINGTALK_WEBHOOK" "" "POST" "application/json")" + + if [ "$?" = "0" ] && _contains "$response" "errmsg\":\"ok"; then + _info "dingtalk webhooks event fired success." + return 0 + fi + + _err "dingtalk webhooks event fired error." + _err "$response" + return 1 +} + From 05acf28e0ddb47b5eab3a3c32ed69f7813237736 Mon Sep 17 00:00:00 2001 From: Johann Richard <189003+johannrichard@users.noreply.github.com> Date: Sat, 2 Nov 2019 07:10:50 +0100 Subject: [PATCH 172/191] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 02dd503..5112bf0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.10 RUN apk update -f \ && apk --no-cache add -f \ openssl \ - openssh \ + openssh-client \ coreutils \ bind-tools \ curl \ From 35b34c43ed26ec2ad08ff004823f5b408e6a3401 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 2 Nov 2019 19:44:43 +0800 Subject: [PATCH 173/191] fix format --- notify/dingtalk.sh | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/notify/dingtalk.sh b/notify/dingtalk.sh index 7d354da..c547da6 100644 --- a/notify/dingtalk.sh +++ b/notify/dingtalk.sh @@ -32,24 +32,24 @@ dingtalk_send() { _saveaccountconf_mutable DINGTALK_KEYWORD "$DINGTALK_KEYWORD" fi -# DINGTALK_SIGNING_KEY="${DINGTALK_SIGNING_KEY:-$(_readaccountconf_mutable DINGTALK_SIGNING_KEY)}" -# if [ -z "$DINGTALK_SIGNING_KEY" ]; then -# DINGTALK_SIGNING_KEY="value1" -# _info "The DINGTALK_SIGNING_KEY is not set, so use the default value1 as key." -# elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$DINGTALK_SIGNING_KEY"; then -# _err "The DINGTALK_SIGNING_KEY \"$DINGTALK_SIGNING_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS" -# DINGTALK_SIGNING_KEY="" -# return 1 -# else -# _saveaccountconf_mutable DINGTALK_SIGNING_KEY "$DINGTALK_SIGNING_KEY" -# fi + # DINGTALK_SIGNING_KEY="${DINGTALK_SIGNING_KEY:-$(_readaccountconf_mutable DINGTALK_SIGNING_KEY)}" + # if [ -z "$DINGTALK_SIGNING_KEY" ]; then + # DINGTALK_SIGNING_KEY="value1" + # _info "The DINGTALK_SIGNING_KEY is not set, so use the default value1 as key." + # elif ! _hasfield "$_IFTTT_AVAIL_MSG_KEYS" "$DINGTALK_SIGNING_KEY"; then + # _err "The DINGTALK_SIGNING_KEY \"$DINGTALK_SIGNING_KEY\" is not available, should be one of $_IFTTT_AVAIL_MSG_KEYS" + # DINGTALK_SIGNING_KEY="" + # return 1 + # else + # _saveaccountconf_mutable DINGTALK_SIGNING_KEY "$DINGTALK_SIGNING_KEY" + # fi -# if [ "$DINGTALK_SIGNING_KEY" = "$IFTTT_CONTENT_KEY" ]; then -# DINGTALK_SIGNING_KEY="" -# IFTTT_CONTENT_KEY="" -# _err "The DINGTALK_SIGNING_KEY must not be same as IFTTT_CONTENT_KEY." -# return 1 -# fi + # if [ "$DINGTALK_SIGNING_KEY" = "$IFTTT_CONTENT_KEY" ]; then + # DINGTALK_SIGNING_KEY="" + # IFTTT_CONTENT_KEY="" + # _err "The DINGTALK_SIGNING_KEY must not be same as IFTTT_CONTENT_KEY." + # return 1 + # fi _content=$(echo "$_content" | _json_encode) _subject=$(echo "$_subject" | _json_encode) @@ -66,4 +66,3 @@ dingtalk_send() { _err "$response" return 1 } - From 6eaf2d67b7588f23f1870c8813d3d6d391820b89 Mon Sep 17 00:00:00 2001 From: Kukushkin Alexander Date: Fri, 16 Nov 2018 08:30:44 +0300 Subject: [PATCH 174/191] Fix Vscale --- dnsapi/dns_vscale.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_vscale.sh b/dnsapi/dns_vscale.sh index e50b7d8..d717d6e 100755 --- a/dnsapi/dns_vscale.sh +++ b/dnsapi/dns_vscale.sh @@ -102,7 +102,7 @@ _get_root() { return 1 fi - hostedzone="$(echo "$response" | _egrep_o "{.*\"name\":\s*\"$h\".*}")" + hostedzone="$(echo "$response" | tr "{" "\n" | _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 From 2d1a776db792e475dfdedc3a3bfde7b649d8fa7e Mon Sep 17 00:00:00 2001 From: stilez Date: Mon, 4 Nov 2019 18:40:12 +0000 Subject: [PATCH 175/191] Replace egrep -> basic regex grep (( ... isn't it annoying that basic regex has * but not + ..... )) --- dnsapi/dns_pleskxml.sh | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index c12b2ee..f3d6a1d 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -1,9 +1,5 @@ #!/usr/bin/env sh -# Globally disable this shellcheck error. -# Shellcheck errors on egrep ("deprecated"), but acme.sh uses egrep for compatibility. -# shellcheck disable=SC2196 - ## Name: dns_pleskxml.sh ## Created by Stilez. ## Also uses some code from PR#1832 by @romanlum (https://github.com/Neilpang/acme.sh/pull/1832/files) @@ -95,7 +91,7 @@ dns_pleskxml_add() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | egrep -q '[0-9]+'; then + if ! _value "$results" | grep 'ok' | grep -q '[0-9][0-9]*'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -104,7 +100,7 @@ dns_pleskxml_add() { return 1 fi - recid="$(_value "$results" | egrep '[0-9]+' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" + recid="$(_value "$results" | grep '[0-9][0-9]*' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -140,7 +136,7 @@ dns_pleskxml_rm() { # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ | grep "${root_domain_id}" \ - | egrep '[0-9]+' \ + | grep '[0-9][0-9]*' \ | grep 'TXT' )" @@ -159,7 +155,7 @@ dns_pleskxml_rm() { _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - if ! _value "$recid" | egrep -q '^[0-9]+$'; then + if ! _value "$recid" | grep -q '^[0-9][0-9]*$'; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" _err "Cannot delete TXT record. Exiting." return 1 @@ -180,7 +176,7 @@ dns_pleskxml_rm() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | egrep -q '[0-9]+'; then + if ! _value "$results" | grep 'ok' | grep -q '[0-9][0-9]*'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -216,13 +212,16 @@ _countdots() { # Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines # $1 - result string from API # $2 - tag to resplit on (usually "result" or "domain") -# $3 - regex to recognise useful return lines +# $3 - basic regex to recognise useful return lines +# note: $3 matches via basic NOT extended regex (BRE), as extended regex capabilities not needed at the moment. +# Last line could change to instead, with suitablew ewscaping of ['"/$], +# if future Plesk XML API changes ever require extended regex _api_response_split() { printf '%s' "$1" \ | sed -r 's/(^ +| +$)//g' \ | tr -d '\n\r' \ | sed -r "s/<\/?$2>/${NEWLINE}/g" \ - | egrep "$3" + | grep "$3" } #################### Private functions below (DNS functions) ################################## @@ -245,15 +244,13 @@ _call_api() { # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. - statuslines="$(echo "$pleskxml_prettyprint_result" | egrep '^ *[^<]* *$')" + statuslines="$(echo "$pleskxml_prettyprint_result" | grep '^ *[^<]* *$')" if _value "$statuslines" | grep -qv 'ok'; then # We have some status lines that aren't "ok". Get the details errtext="$(_value "$pleskxml_prettyprint_result" \ - | egrep "(||)" \ - | sed -r 's/^ *<(status|errcode|errtext)>/\1: /' \ - | sed -r 's/<\/(status|errcode|errtext)>$//g' + | sed -rn 's/^ *<(status|errcode|errtext)>([^<]+)<\/(status|errcode|errtext)> *$/\1: \2/p' \ )" elif ! _value "$statuslines" | grep -q 'ok'; then From 63a779baa86ed8609c7030fa8aa37deb11341752 Mon Sep 17 00:00:00 2001 From: stilez Date: Mon, 4 Nov 2019 18:44:14 +0000 Subject: [PATCH 176/191] remove unnecessary \ --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index f3d6a1d..c673744 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -250,7 +250,7 @@ _call_api() { # We have some status lines that aren't "ok". Get the details errtext="$(_value "$pleskxml_prettyprint_result" \ - | sed -rn 's/^ *<(status|errcode|errtext)>([^<]+)<\/(status|errcode|errtext)> *$/\1: \2/p' \ + | sed -rn 's/^ *<(status|errcode|errtext)>([^<]+)<\/(status|errcode|errtext)> *$/\1: \2/p' )" elif ! _value "$statuslines" | grep -q 'ok'; then From 04b0c62bf959c800c36f9b19da599741caacd1c8 Mon Sep 17 00:00:00 2001 From: stilez Date: Mon, 4 Nov 2019 19:05:44 +0000 Subject: [PATCH 177/191] basic regex's to use \+ Maybe BRE aren't as basic as they sound. But I'm sure `man grep` didn't list the extra syntax of "preceded by backslash" :) So let's use it --- dnsapi/dns_pleskxml.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index c673744..dca0d24 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -91,7 +91,7 @@ dns_pleskxml_add() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -q '[0-9][0-9]*'; then + if ! _value "$results" | grep 'ok' | grep -q '[0-9]\+'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -100,7 +100,7 @@ dns_pleskxml_add() { return 1 fi - recid="$(_value "$results" | grep '[0-9][0-9]*' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" + recid="$(_value "$results" | grep '[0-9]\+' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -136,7 +136,7 @@ dns_pleskxml_rm() { # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ | grep "${root_domain_id}" \ - | grep '[0-9][0-9]*' \ + | grep '[0-9]\+' \ | grep 'TXT' )" @@ -155,7 +155,7 @@ dns_pleskxml_rm() { _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - if ! _value "$recid" | grep -q '^[0-9][0-9]*$'; then + if ! _value "$recid" | grep -q '^[0-9]\+$'; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" _err "Cannot delete TXT record. Exiting." return 1 @@ -176,7 +176,7 @@ dns_pleskxml_rm() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -q '[0-9][0-9]*'; then + if ! _value "$results" | grep 'ok' | grep -q '[0-9]\+'; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' From 896778cead810f88ffd8e5efa61d97cbd78dfe6d Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 17:56:20 +0000 Subject: [PATCH 178/191] Grep fixes and minor improvements --- dnsapi/dns_pleskxml.sh | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index dca0d24..551e42f 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -91,7 +91,7 @@ dns_pleskxml_add() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -q '[0-9]\+'; then + if ! _value "$results" | grep 'ok' | grep '[0-9]\{1,\}' >/dev/null; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -100,7 +100,7 @@ dns_pleskxml_add() { return 1 fi - recid="$(_value "$results" | grep '[0-9]\+' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" + recid="$(_value "$results" | grep '[0-9]\{1,\}' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -136,7 +136,7 @@ dns_pleskxml_rm() { # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ | grep "${root_domain_id}" \ - | grep '[0-9]\+' \ + | grep '[0-9]\{1,\}' \ | grep 'TXT' )" @@ -155,7 +155,7 @@ dns_pleskxml_rm() { _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - if ! _value "$recid" | grep -q '^[0-9]\+$'; then + if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" _err "Cannot delete TXT record. Exiting." return 1 @@ -176,7 +176,7 @@ dns_pleskxml_rm() { results="$(_api_response_split "$pleskxml_prettyprint_result" 'result' '')" - if ! _value "$results" | grep 'ok' | grep -q '[0-9]\+'; then + if ! _value "$results" | grep 'ok' | grep '[0-9]\{1,\}' >/dev/null; then # Error - doesn't contain expected string. Something's wrong. _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' @@ -244,20 +244,27 @@ _call_api() { # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. - statuslines="$(echo "$pleskxml_prettyprint_result" | grep '^ *[^<]* *$')" + statuslines_count_total="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *[^<]* *$')" + statuslines_count_okay="$(echo "$pleskxml_prettyprint_result" | grep -c '^ *ok *$')" - if _value "$statuslines" | grep -qv 'ok'; then - - # We have some status lines that aren't "ok". Get the details - errtext="$(_value "$pleskxml_prettyprint_result" \ - | sed -rn 's/^ *<(status|errcode|errtext)>([^<]+)<\/(status|errcode|errtext)> *$/\1: \2/p' - )" - - elif ! _value "$statuslines" | grep -q 'ok'; then + if [ -z "$statuslines_count_total" ]; then # We have no status lines at all. Results are empty errtext='The Plesk XML API unexpectedly returned an empty set of results for this call.' + elif [ "$statuslines_count_okay" -ne "$statuslines_count_total" ]; then + + # We have some status lines that aren't "ok". Any available details are in API response fields "status" "errcode" and "errtext" + # Workaround for basic regex: + # - filter output to keep only lines like this: "SPACEStextSPACES" (shouldn't be necessary with prettyprint but guarantees subsequent code is ok) + # - then edit the 3 "useful" error tokens individually and remove closing tags on all lines + # - then filter again to remove all lines not edited (which will be the lines not starting A-Z) + errtext="$(_value "$pleskxml_prettyprint_result" \ + | grep '^ *<[a-z]\{1,\}>[^<]*<\/[a-z]\{1,\}> *$' \ + | sed 's/^ */Status: /;s/^ */Error code: /;s/^ */Error text: /;s/<\/.*$//' \ + | grep '^[A-Z]' + )" + fi if [ "$pleskxml_retcode" -ne 0 ] || [ "$errtext" != "" ]; then @@ -266,7 +273,7 @@ _call_api() { if [ "$pleskxml_retcode" -eq 0 ]; then _err "The POST request was successfully sent to the Plesk server." else - _err "The return code for the POST request was $pleskxml_retcode (non-zero = could not submit request to server)." + _err "The return code for the POST request was $pleskxml_retcode (non-zero = failure in submitting request to server)." fi if [ "$errtext" != "" ]; then From cbacc779fcc3dc9bf3b5924d5691c12f9d74ebe3 Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 18:50:39 +0000 Subject: [PATCH 179/191] Fix some sed -r, and clean up some variable references ("$1" -> "$varname") --- dnsapi/dns_pleskxml.sh | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 551e42f..2c9a4d5 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -100,7 +100,7 @@ dns_pleskxml_add() { return 1 fi - recid="$(_value "$results" | grep '[0-9]\{1,\}' | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" + recid="$(_value "$results" | grep '[0-9]\{1,\}' | sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/')" _info "Success. TXT record appears to be correctly added (Plesk record ID=$recid). Exiting dns_pleskxml_add()." @@ -145,16 +145,16 @@ dns_pleskxml_rm() { return 1 fi - _debug "Got list of DNS TXT records for root domain '$root_domain_name'"':\n'"$reclist" + _debug "Got list of DNS TXT records for root domain '$root_domain_name'. Full list is:"'\n'"$reclist" + + _debug "DNS TXT records for host '$fulldomain':"'\n'"$(_value "$reclist" | grep "${fulldomain}.")" recid="$(_value "$reclist" \ - | grep "$1." \ - | grep "$txtvalue" \ - | sed -r 's/(^.*|<\/id>.*$)//g' + | grep "${fulldomain}." \ + | grep "${txtvalue}" \ + | sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/' )" - _debug "List of DNS TXT records for host:"'\n'"$(_value "$reclist" | grep "$1.")" - if ! _value "$recid" | grep '^[0-9]\{1,\}$' >/dev/null; then _err "DNS records for root domain '${root_domain_name}' (Plesk ID ${root_domain_id}) + host '${sub_domain_name}' do not contain the TXT record '${txtvalue}'" _err "Cannot delete TXT record. Exiting." @@ -341,13 +341,13 @@ _credential_check() { # See notes at top of this file _pleskxml_get_root_domain() { - _debug "Identifying DNS root domain for '$1' that is managed by the Plesk account." original_full_domain_name="$1" - root_domain_name="$1" + + _debug "Identifying DNS root domain for '$original_full_domain_name' that is managed by the Plesk account." # test if the domain as provided is valid for splitting. - if ! _countdots "$root_domain_name"; then + if ! _countdots "$original_full_domain_name"; then _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." return 1 fi @@ -372,6 +372,8 @@ _pleskxml_get_root_domain() { # loop and test if domain, or any parent domain, is managed by Plesk # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain + root_domain_name="$original_full_domain_name" + while true; do _debug "Checking if '$root_domain_name' is managed by the Plesk server..." From a8d670fc0d7abb2efa3cbcc411c79f37ad8c737d Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 19:01:32 +0000 Subject: [PATCH 180/191] Rest of sed -r --- dnsapi/dns_pleskxml.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 2c9a4d5..c2de218 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -211,16 +211,16 @@ _countdots() { # Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines # $1 - result string from API -# $2 - tag to resplit on (usually "result" or "domain") +# $2 - plain text tag to resplit on (usually "result" or "domain"). NOT REGEX # $3 - basic regex to recognise useful return lines # note: $3 matches via basic NOT extended regex (BRE), as extended regex capabilities not needed at the moment. -# Last line could change to instead, with suitablew ewscaping of ['"/$], +# Last line could change to instead, with suitable escaping of ['"/$], # if future Plesk XML API changes ever require extended regex _api_response_split() { printf '%s' "$1" \ - | sed -r 's/(^ +| +$)//g' \ + | sed 's/^ +//;s/ +$//' \ | tr -d '\n\r' \ - | sed -r "s/<\/?$2>/${NEWLINE}/g" \ + | sed "s/<\/\{0,1\}$2>/${NEWLINE}/g" \ | grep "$3" } @@ -365,7 +365,7 @@ _pleskxml_get_root_domain() { # Output will be one line per known domain, containing 2 tages and a single tag # We don't actually need to check for type, name, *and* id, but it guarantees only usable lines are returned. - output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed -r 's/<(\/?)ascii-name>/<\1name>/g' | grep '' | grep '')" + output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')" _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" @@ -378,13 +378,13 @@ _pleskxml_get_root_domain() { _debug "Checking if '$root_domain_name' is managed by the Plesk server..." - root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed -r 's/^.*([0-9]+)<\/id>.*$/\1/')" + root_domain_id="$(_value "$output" | grep "$root_domain_name" | _head_n 1 | sed 's/^.*\([0-9]\{1,\}\)<\/id>.*$/\1/')" if [ -n "$root_domain_id" ]; then # Found a match # SEE IMPORTANT NOTE ABOVE - THIS FUNCTION CAN RETURN HOST='', AND THAT'S OK FOR PLESK XML API WHICH ALLOWS IT. # SO WE HANDLE IT AND DON'T PREVENT IT - sub_domain_name="$(_value "$original_full_domain_name" | sed -r "s/\.?${root_domain_name}"'$//')" + sub_domain_name="$(_value "$original_full_domain_name" | sed "s/\.\{0,1\}${root_domain_name}"'$//')" _info "Success. Matched host '$original_full_domain_name' to: DOMAIN '${root_domain_name}' (Plesk ID '${root_domain_id}'), HOST '${sub_domain_name}'. Returning." return 0 fi From 4216c9e8f745d76588c8dc8592ade0e020f82d62 Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 19:19:08 +0000 Subject: [PATCH 181/191] rmv spaces --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index c2de218..d2a2684 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -146,7 +146,7 @@ dns_pleskxml_rm() { fi _debug "Got list of DNS TXT records for root domain '$root_domain_name'. Full list is:"'\n'"$reclist" - + _debug "DNS TXT records for host '$fulldomain':"'\n'"$(_value "$reclist" | grep "${fulldomain}.")" recid="$(_value "$reclist" \ From a9726bd52f764f519332783f8002ef8e2c742b5b Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 20:58:51 +0000 Subject: [PATCH 182/191] bugfix --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index d2a2684..ad8d5cf 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -393,7 +393,7 @@ _pleskxml_get_root_domain() { root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" - if ! _countdots "$root_domain_name"; then + if [ _countdots "$root_domain_name" -eq 0 ]; then _debug "No match, and next parent would be a TLD..." _err "Cannot find '$original_full_domain_name' or any parent domain of it, in Plesk." _err "Are you sure that this domain is managed by this Plesk server?" From 38854bd876d11c4bf0f696e23c694a61d2b640ec Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 21:00:34 +0000 Subject: [PATCH 183/191] bugfix --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index ad8d5cf..ca64325 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -393,7 +393,7 @@ _pleskxml_get_root_domain() { root_domain_name="$(_valuecut 2 1000 "$root_domain_name")" - if [ _countdots "$root_domain_name" -eq 0 ]; then + if [ "$(_countdots "$root_domain_name")" -eq 0 ]; then _debug "No match, and next parent would be a TLD..." _err "Cannot find '$original_full_domain_name' or any parent domain of it, in Plesk." _err "Are you sure that this domain is managed by this Plesk server?" From 43011f3bfa2e08807b3e7450d123aabcbd6905a8 Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 21:04:10 +0000 Subject: [PATCH 184/191] enhance --- dnsapi/dns_pleskxml.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index ca64325..5abe9dd 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -203,10 +203,10 @@ _valuecut() { printf '%s' "$3" | cut -d . -f "${1}-${2}" } -# Counts '.' present in a domain name +# Counts '.' present in a domain name or other string # $1 = domain name _countdots() { - _value "$1" | tr -dc '.' | wc -c + _value "$1" | tr -dc '.' | wc -c | sed 's/ //g' } # Cleans up an API response, splits it "one line per item in the response" and greps for a string that in the context, identifies "useful" lines From 05247dc4a4dca564a8d38e223da89556e2e68493 Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 21:09:14 +0000 Subject: [PATCH 185/191] fix --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index 5abe9dd..b6781c2 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -347,7 +347,7 @@ _pleskxml_get_root_domain() { # test if the domain as provided is valid for splitting. - if ! _countdots "$original_full_domain_name"; then + if [ "$(_countdots "$original_full_domain_name")" -eq 0 ]; then _err "Invalid domain. The ACME domain must contain at least two parts (aa.bb) to identify a domain and tld for the TXT record." return 1 fi From 6d0e4bed4b9d9bcb31e445ecadc7c4f741abc6fc Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 23:26:05 +0000 Subject: [PATCH 186/191] remove \n in output messages --- dnsapi/dns_pleskxml.sh | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index b6781c2..ca07bc4 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -96,7 +96,8 @@ dns_pleskxml_add() { _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' _err 'This is unexpected: something has gone wrong.' - _err 'The full response was:\n' "$pleskxml_prettyprint_result" + _err 'The full response was:' + _err "$pleskxml_prettyprint_result" return 1 fi @@ -134,7 +135,9 @@ dns_pleskxml_rm() { fi # Reduce output to one line per DNS record, filtered for TXT records with a record ID only (which they should all have) + # Also strip out spaces between tags, redundant and group tags and any tags reclist="$(_api_response_split "$pleskxml_prettyprint_result" 'result' 'ok' \ + | sed 's# \{1,\}<\([a-zA-Z]\)#<\1#g;s###g;s#<[a-z][^/<>]*/>##g' \ | grep "${root_domain_id}" \ | grep '[0-9]\{1,\}' \ | grep 'TXT' @@ -145,9 +148,8 @@ dns_pleskxml_rm() { return 1 fi - _debug "Got list of DNS TXT records for root domain '$root_domain_name'. Full list is:"'\n'"$reclist" - - _debug "DNS TXT records for host '$fulldomain':"'\n'"$(_value "$reclist" | grep "${fulldomain}.")" + _debug "Got list of DNS TXT records for root domain '$root_domain_name':" + _debug "$reclist" recid="$(_value "$reclist" \ | grep "${fulldomain}." \ @@ -181,7 +183,8 @@ dns_pleskxml_rm() { _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' _err 'This is unexpected: something has gone wrong.' - _err 'The full response was:\n' "$pleskxml_prettyprint_result" + _err 'The full response was:' + _err "$pleskxml_prettyprint_result" return 1 fi @@ -231,7 +234,8 @@ _call_api() { request="$1" errtext='' - _debug 'Entered _call_api(). Calling Plesk XML API with request:\n' "'${request}'" + _debug 'Entered _call_api(). Calling Plesk XML API with request:' + _debug "'$request'" export _H1="HTTP_AUTH_LOGIN: $pleskxml_user" export _H2="HTTP_AUTH_PASSWD: $pleskxml_pass" @@ -239,7 +243,9 @@ _call_api() { export _H4="HTTP_PRETTY_PRINT: true" pleskxml_prettyprint_result="$(_post "${request}" "$pleskxml_uri" "" "POST")" pleskxml_retcode="$?" - _debug 'The responses from the Plesk XML server were:\n' "retcode=$pleskxml_retcode. Literal response:"'\n' "'$pleskxml_prettyprint_result'" + _debug 'The responses from the Plesk XML server were:' + _debug "retcode=$pleskxml_retcode. Literal response:" + _debug "'$pleskxml_prettyprint_result'" # Detect any that isn't "ok". None of the used calls should fail if the API is working correctly. # Also detect if there simply aren't any status lines (null result?) and report that, as well. @@ -277,7 +283,8 @@ _call_api() { fi if [ "$errtext" != "" ]; then - _err 'The error responses received from the Plesk server were:\n' "$errtext" + _err 'The error responses received from the Plesk server were:' + _err "$errtext" else _err "No additional error messages were received back from the Plesk server" fi @@ -318,7 +325,7 @@ _credential_check() { # Test the API is usable, by trying to read the list of managed domains... _call_api "$pleskxml_tplt_get_domains" if [ "$pleskxml_retcode" -ne 0 ]; then - _err '\nFailed to access Plesk XML API.' + _err 'Failed to access Plesk XML API.' _err "Please check your login credentials and Plesk URI, and that the URI is reachable, and try again." return 1 fi @@ -367,7 +374,8 @@ _pleskxml_get_root_domain() { output="$(_api_response_split "$pleskxml_prettyprint_result" 'domain' 'domain' | sed 's///g;s/<\/ascii-name>/<\/name>/g' | grep '' | grep '')" - _debug 'Domains managed by Plesk server are (ignore the hacked output):\n' "$output" + _debug 'Domains managed by Plesk server are (ignore the hacked output):' + _debug "$output" # loop and test if domain, or any parent domain, is managed by Plesk # Loop until we don't have any '.' in the string we're testing as a candidate Plesk-managed domain From 51cfd996eb8aaa142c9cbe61270bf1c5751f7d1e Mon Sep 17 00:00:00 2001 From: stilez Date: Tue, 5 Nov 2019 23:29:51 +0000 Subject: [PATCH 187/191] rmv space --- dnsapi/dns_pleskxml.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_pleskxml.sh b/dnsapi/dns_pleskxml.sh index ca07bc4..c5d9e54 100644 --- a/dnsapi/dns_pleskxml.sh +++ b/dnsapi/dns_pleskxml.sh @@ -96,7 +96,7 @@ dns_pleskxml_add() { _err 'Error when calling Plesk XML API.' _err 'The result did not contain the expected XXXXX section, or contained other values as well.' _err 'This is unexpected: something has gone wrong.' - _err 'The full response was:' + _err 'The full response was:' _err "$pleskxml_prettyprint_result" return 1 fi From f1f14040b85cb05330552d644c94890f6b82f2c3 Mon Sep 17 00:00:00 2001 From: Vitalii Tverdokhlib Date: Sat, 9 Nov 2019 12:12:30 +0200 Subject: [PATCH 188/191] DOCS: typo --- notify/mailgun.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify/mailgun.sh b/notify/mailgun.sh index 4b6ee3b..93cdf79 100644 --- a/notify/mailgun.sh +++ b/notify/mailgun.sh @@ -7,7 +7,7 @@ #MAILGUN_REGION="us|eu" #optional, use "us" as default #MAILGUN_API_DOMAIN="xxxxxx.com" #optional, use the default sandbox domain -#MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sendbox account +#MAILGUN_FROM="xxx@xxxxx.com" #optional, use the default sandbox account _MAILGUN_BASE_US="https://api.mailgun.net/v3" _MAILGUN_BASE_EU="https://api.eu.mailgun.net/v3" From 867ec010abe4aa4af349895a1d1ac93c3d0fba66 Mon Sep 17 00:00:00 2001 From: arlecchino Date: Tue, 12 Nov 2019 19:58:36 +0100 Subject: [PATCH 189/191] Fix callhook error in manual mode Fixes #1586 Check force manual switch before causing error about it. --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 81a20b0..0164d54 100755 --- a/acme.sh +++ b/acme.sh @@ -3365,7 +3365,7 @@ _on_issue_success() { fi fi - if _hasfield "$Le_Webroot" "$W_DNS"; then + if _hasfield "$Le_Webroot" "$W_DNS" && [ -z "$FORCE_DNS_MANUAL" ]; then _err "$_DNS_MANUAL_WARN" fi From adce8f52e8b71da504541bec2a10a607b9b3fac4 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Tue, 12 Nov 2019 08:48:41 -0800 Subject: [PATCH 190/191] debug_bash_helper: Use eval as busybox systems have problems In _debug_bash_helper use eval as we are seeing issues with busybox systems having issues with array access. Even though they aren't actually running the code they appear to be parsing it and failing. Also older versions of busybox have a bug with eval and double quotes, so make sure to use single quotes when using eval. Resolves: #2579 --- acme.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/acme.sh b/acme.sh index 81a20b0..0c8ab90 100755 --- a/acme.sh +++ b/acme.sh @@ -268,31 +268,31 @@ _usage() { __debug_bash_helper() { # At this point only do for --debug 3 if [ "${DEBUG:-$DEBUG_LEVEL_NONE}" -lt "$DEBUG_LEVEL_3" ]; then - echo "" return fi # Return extra debug info when running with bash, otherwise return empty # string. if [ -z "${BASH_VERSION}" ]; then - echo "" return fi # We are a bash shell at this point, return the filename, function name, and # line number as a string _dbh_saveIFS=$IFS IFS=" " - # Must use eval or syntax error happens under dash + # Must use eval or syntax error happens under dash. The eval should use + # single quotes as older versions of busybox had a bug with double quotes and + # eval. # Use 'caller 1' as we want one level up the stack as we should be called # by one of the _debug* functions - eval "_dbh_called=($(caller 1))" + eval '_dbh_called=($(caller 1))' IFS=$_dbh_saveIFS - _dbh_file=${_dbh_called[2]} + eval '_dbh_file=${_dbh_called[2]}' if [ -n "${_script_home}" ]; then # Trim off the _script_home directory name - _dbh_file=${_dbh_file#$_script_home/} + eval '_dbh_file=${_dbh_file#$_script_home/}' fi - _dbh_function=${_dbh_called[1]} - _dbh_lineno=${_dbh_called[0]} + eval '_dbh_function=${_dbh_called[1]}' + eval '_dbh_lineno=${_dbh_called[0]}' printf "%-40s " "$_dbh_file:${_dbh_function}:${_dbh_lineno}" } From c282dd086fe0fc2152f7d47fb4ee1885f26aec09 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sat, 16 Nov 2019 08:06:21 +0800 Subject: [PATCH 191/191] minor --- dnsapi/dns_nic.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dnsapi/dns_nic.sh b/dnsapi/dns_nic.sh index 493b05b..e3b39b0 100644 --- a/dnsapi/dns_nic.sh +++ b/dnsapi/dns_nic.sh @@ -89,7 +89,7 @@ dns_nic_rm() { return 1 fi - _domain_id=$(printf "%s" "$response" | grep "$_sub_domain" | grep "$txtvalue" | sed -r "s/.*