Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Acme.Sh
Commits
22bab90a
Unverified
Commit
22bab90a
authored
Mar 18, 2019
by
Sylvia van Os
Committed by
GitHub
Mar 18, 2019
Browse files
Merge branch 'dev' into feature/dns-openprovider
parents
ea86ddc6
c74d597c
Changes
8
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
22bab90a
...
@@ -360,6 +360,8 @@ You don't have to do anything manually!
...
@@ -360,6 +360,8 @@ You don't have to do anything manually!
1.
MyDevil.net (https://www.mydevil.net/)
1.
MyDevil.net (https://www.mydevil.net/)
1.
Core-Networks.de (https://core-networks.de)
1.
Core-Networks.de (https://core-networks.de)
1.
NederHost API (https://www.nederhost.nl/)
1.
NederHost API (https://www.nederhost.nl/)
1.
Zone.ee (zone.eu) API (https://api.zone.eu/v2)
1.
UltraDNS API (https://portal.ultradns.com)
1.
OpenProvider API (https://www.openprovider.com/)
1.
OpenProvider API (https://www.openprovider.com/)
And:
And:
...
...
acme.sh
View file @
22bab90a
...
@@ -9,6 +9,9 @@ PROJECT_ENTRY="acme.sh"
...
@@ -9,6 +9,9 @@ PROJECT_ENTRY="acme.sh"
PROJECT
=
"https://github.com/Neilpang/
$PROJECT_NAME
"
PROJECT
=
"https://github.com/Neilpang/
$PROJECT_NAME
"
DEFAULT_INSTALL_HOME
=
"
$HOME
/.
$PROJECT_NAME
"
DEFAULT_INSTALL_HOME
=
"
$HOME
/.
$PROJECT_NAME
"
_WINDOWS_SCHEDULER_NAME
=
"
$PROJECT_NAME
.cron"
_SCRIPT_
=
"
$0
"
_SCRIPT_
=
"
$0
"
_SUB_FOLDERS
=
"dnsapi deploy"
_SUB_FOLDERS
=
"dnsapi deploy"
...
@@ -2108,7 +2111,7 @@ _startserver() {
...
@@ -2108,7 +2111,7 @@ _startserver() {
echo 'HTTP/1.0 200 OK';
\
echo 'HTTP/1.0 200 OK';
\
echo 'Content-Length
\:
$_content_len
';
\
echo 'Content-Length
\:
$_content_len
';
\
echo '';
\
echo '';
\
printf '
$content
';"
&
printf
--
'
$content
';"
&
serverproc
=
"
$!
"
serverproc
=
"
$!
"
}
}
...
@@ -3748,7 +3751,7 @@ issue() {
...
@@ -3748,7 +3751,7 @@ issue() {
return
1
return
1
fi
fi
Le_OrderFinalize
=
"
$(
echo
"
$response
"
|
tr
-d
'\r\n'
|
_egrep_o
'"finalize" *: *"[^"]*"'
|
cut
-d
'"'
-f
4
)
"
Le_OrderFinalize
=
"
$(
echo
"
$response
"
| _egrep_o
'"finalize" *: *"[^"]*"'
|
cut
-d
'"'
-f
4
)
"
_debug Le_OrderFinalize
"
$Le_OrderFinalize
"
_debug Le_OrderFinalize
"
$Le_OrderFinalize
"
if
[
-z
"
$Le_OrderFinalize
"
]
;
then
if
[
-z
"
$Le_OrderFinalize
"
]
;
then
_err
"Create new order error. Le_OrderFinalize not found.
$response
"
_err
"Create new order error. Le_OrderFinalize not found.
$response
"
...
@@ -3760,7 +3763,7 @@ issue() {
...
@@ -3760,7 +3763,7 @@ issue() {
#for dns manual mode
#for dns manual mode
_savedomainconf
"Le_OrderFinalize"
"
$Le_OrderFinalize
"
_savedomainconf
"Le_OrderFinalize"
"
$Le_OrderFinalize
"
_authorizations_seg
=
"
$(
echo
"
$response
"
|
tr
-d
'\r\n'
|
_egrep_o
'"authorizations" *: *\[[^\]*\]'
|
cut
-d
'['
-f
2 |
tr
-d
']'
|
tr
-d
'"'
)
"
_authorizations_seg
=
"
$(
echo
"
$response
"
| _egrep_o
'"authorizations" *: *\[[^\]*\]'
|
cut
-d
'['
-f
2 |
tr
-d
']'
|
tr
-d
'"'
)
"
_debug2 _authorizations_seg
"
$_authorizations_seg
"
_debug2 _authorizations_seg
"
$_authorizations_seg
"
if
[
-z
"
$_authorizations_seg
"
]
;
then
if
[
-z
"
$_authorizations_seg
"
]
;
then
_err
"_authorizations_seg not found."
_err
"_authorizations_seg not found."
...
@@ -3846,7 +3849,7 @@ $_authorizations_map"
...
@@ -3846,7 +3849,7 @@ $_authorizations_map"
thumbprint
=
"
$(
__calc_account_thumbprint
)
"
thumbprint
=
"
$(
__calc_account_thumbprint
)
"
fi
fi
entry
=
"
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
'[^\{]*"type":"'
$vtype
'"[^\}]*'
)
"
entry
=
"
$(
echo
"
$response
"
| _egrep_o
'[^\{]*"type":"'
$vtype
'"[^\}]*'
)
"
_debug entry
"
$entry
"
_debug entry
"
$entry
"
if
[
-z
"
$entry
"
]
;
then
if
[
-z
"
$entry
"
]
;
then
_err
"Error, can not get domain token entry
$d
"
_err
"Error, can not get domain token entry
$d
"
...
@@ -3858,7 +3861,7 @@ $_authorizations_map"
...
@@ -3858,7 +3861,7 @@ $_authorizations_map"
_on_issue_err
"
$_post_hook
"
_on_issue_err
"
$_post_hook
"
return
1
return
1
fi
fi
token
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
'"token":"[^"]*'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
token
=
"
$(
echo
"
$entry
"
| _egrep_o
'"token":"[^"]*'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
_debug token
"
$token
"
_debug token
"
$token
"
if
[
-z
"
$token
"
]
;
then
if
[
-z
"
$token
"
]
;
then
...
@@ -3868,9 +3871,9 @@ $_authorizations_map"
...
@@ -3868,9 +3871,9 @@ $_authorizations_map"
return
1
return
1
fi
fi
if
[
"
$ACME_VERSION
"
=
"2"
]
;
then
if
[
"
$ACME_VERSION
"
=
"2"
]
;
then
uri
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
'"url":"[^"]*'
|
cut
-d
'"'
-f
4 | _head_n 1
)
"
uri
=
"
$(
echo
"
$entry
"
| _egrep_o
'"url":"[^"]*'
|
cut
-d
'"'
-f
4 | _head_n 1
)
"
else
else
uri
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
'"uri":"[^"]*'
|
cut
-d
'"'
-f
4
)
"
uri
=
"
$(
echo
"
$entry
"
| _egrep_o
'"uri":"[^"]*'
|
cut
-d
'"'
-f
4
)
"
fi
fi
_debug uri
"
$uri
"
_debug uri
"
$uri
"
...
@@ -3928,21 +3931,21 @@ $_authorizations_map"
...
@@ -3928,21 +3931,21 @@ $_authorizations_map"
else
else
txtdomain
=
"_acme-challenge.
$_d_alias
"
txtdomain
=
"_acme-challenge.
$_d_alias
"
fi
fi
dns_entr
ies
=
"
${
dns_entries
}
${
_dns_root_d
}${
dvsep
}
_acme-challenge.
$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot
"
dns_entr
y
=
"
${
_dns_root_d
}${
dvsep
}
_acme-challenge.
$_dns_root_d$dvsep$txtdomain$dvsep$_currentRoot
"
else
else
txtdomain
=
"_acme-challenge.
$_dns_root_d
"
txtdomain
=
"_acme-challenge.
$_dns_root_d
"
dns_entr
ies
=
"
${
dns_entries
}
${
_dns_root_d
}${
dvsep
}
_acme-challenge.
$_dns_root_d$dvsep$dvsep$_currentRoot
"
dns_entr
y
=
"
${
_dns_root_d
}${
dvsep
}
_acme-challenge.
$_dns_root_d$dvsep$dvsep$_currentRoot
"
fi
fi
_debug txtdomain
"
$txtdomain
"
_debug txtdomain
"
$txtdomain
"
txt
=
"
$(
printf
"%s"
"
$keyauthorization
"
| _digest
"sha256"
| _url_replace
)
"
txt
=
"
$(
printf
"%s"
"
$keyauthorization
"
| _digest
"sha256"
| _url_replace
)
"
_debug txt
"
$txt
"
_debug txt
"
$txt
"
d_api
=
"
$(
_findHook
"
$_dns_root_d
"
dnsapi
"
$_currentRoot
"
)
"
d_api
=
"
$(
_findHook
"
$_dns_root_d
"
dnsapi
"
$_currentRoot
"
)
"
_debug d_api
"
$d_api
"
_debug d_api
"
$d_api
"
dns_entries
=
"
$dns_entries$dvsep$txt
${
dvsep
}
$d_api
"
dns_entry
=
"
$dns_entry$dvsep$txt
${
dvsep
}
$d_api
"
_debug2
"
$dns_entr
ies
"
_debug2
dns_entry
"
$dns_entr
y
"
if
[
"
$d_api
"
]
;
then
if
[
"
$d_api
"
]
;
then
_info
"Found domain api file:
$d_api
"
_info
"Found domain api file:
$d_api
"
else
else
...
@@ -3981,6 +3984,9 @@ $_authorizations_map"
...
@@ -3981,6 +3984,9 @@ $_authorizations_map"
_clearup
_clearup
return
1
return
1
fi
fi
dns_entries
=
"
$dns_entries$dns_entry
"
_debug2
"
$dns_entries
"
dnsadded
=
'1'
dnsadded
=
'1'
fi
fi
done
done
...
@@ -4191,7 +4197,7 @@ $_authorizations_map"
...
@@ -4191,7 +4197,7 @@ $_authorizations_map"
fi
fi
if
[
"
$status
"
=
"invalid"
]
;
then
if
[
"
$status
"
=
"invalid"
]
;
then
error
=
"
$(
echo
"
$response
"
|
tr
-d
"
\r\n
"
|
_egrep_o
'"error":\{[^\}]*'
)
"
error
=
"
$(
echo
"
$response
"
| _egrep_o
'"error":\{[^\}]*'
)
"
_debug2 error
"
$error
"
_debug2 error
"
$error
"
errordetail
=
"
$(
echo
"
$error
"
| _egrep_o
'"detail": *"[^"]*'
|
cut
-d
'"'
-f
4
)
"
errordetail
=
"
$(
echo
"
$error
"
| _egrep_o
'"detail": *"[^"]*'
|
cut
-d
'"'
-f
4
)
"
_debug2 errordetail
"
$errordetail
"
_debug2 errordetail
"
$errordetail
"
...
@@ -4257,7 +4263,7 @@ $_authorizations_map"
...
@@ -4257,7 +4263,7 @@ $_authorizations_map"
while
[
"
$_link_cert_retry
"
-lt
"
$_MAX_CERT_RETRY
"
]
;
do
while
[
"
$_link_cert_retry
"
-lt
"
$_MAX_CERT_RETRY
"
]
;
do
if
_contains
"
$response
"
"
\"
status
\"
:
\"
valid
\"
"
;
then
if
_contains
"
$response
"
"
\"
status
\"
:
\"
valid
\"
"
;
then
_debug
"Order status is valid."
_debug
"Order status is valid."
Le_LinkCert
=
"
$(
echo
"
$response
"
|
tr
-d
'\r\n'
|
_egrep_o
'"certificate" *: *"[^"]*"'
|
cut
-d
'"'
-f
4
)
"
Le_LinkCert
=
"
$(
echo
"
$response
"
| _egrep_o
'"certificate" *: *"[^"]*"'
|
cut
-d
'"'
-f
4
)
"
_debug Le_LinkCert
"
$Le_LinkCert
"
_debug Le_LinkCert
"
$Le_LinkCert
"
if
[
-z
"
$Le_LinkCert
"
]
;
then
if
[
-z
"
$Le_LinkCert
"
]
;
then
_err
"Sign error, can not find Le_LinkCert"
_err
"Sign error, can not find Le_LinkCert"
...
@@ -4923,35 +4929,107 @@ _installcert() {
...
@@ -4923,35 +4929,107 @@ _installcert() {
}
}
__read_password
()
{
unset
_pp
prompt
=
"Enter Password:"
while
IFS
=
read
-p
"
$prompt
"
-r
-s
-n
1 char
;
do
if
[
"
$char
"
=
$'
\0
'
]
;
then
break
fi
prompt
=
'*'
_pp
=
"
$_pp$char
"
done
echo
"
$_pp
"
}
_install_win_taskscheduler
()
{
_lesh
=
"
$1
"
_centry
=
"
$2
"
_randomminute
=
"
$3
"
if
!
_exists cygpath
;
then
_err
"cygpath not found"
return
1
fi
if
!
_exists schtasks
;
then
_err
"schtasks.exe is not found, are you on Windows?"
return
1
fi
_winbash
=
"
$(
cygpath
-w
$(
which bash
))
"
_debug _winbash
"
$_winbash
"
if
[
-z
"
$_winbash
"
]
;
then
_err
"can not find bash path"
return
1
fi
_myname
=
"
$(
whoami
)
"
_debug
"_myname"
"
$_myname
"
if
[
-z
"
$_myname
"
]
;
then
_err
"can not find my user name"
return
1
fi
_debug
"_lesh"
"
$_lesh
"
_info
"To install scheduler task in your Windows account, you must input your windows password."
_info
"
$PROJECT_NAME
doesn't save your password."
_info
"Please input your Windows password for:
$(
__green
"
$_myname
"
)
"
_password
=
"
$(
__read_password
)
"
#SCHTASKS.exe '/create' '/SC' 'DAILY' '/TN' "$_WINDOWS_SCHEDULER_NAME" '/F' '/ST' "00:$_randomminute" '/RU' "$_myname" '/RP' "$_password" '/TR' "$_winbash -l -c '$_lesh --cron --home \"$LE_WORKING_DIR\" $_centry'" >/dev/null
echo
SCHTASKS.exe
'/create'
'/SC'
'DAILY'
'/TN'
"
$_WINDOWS_SCHEDULER_NAME
"
'/F'
'/ST'
"00:
$_randomminute
"
'/RU'
"
$_myname
"
'/RP'
"
$_password
"
'/TR'
"
\"
$_winbash
-l -c '
$_lesh
--cron --home
\"
$LE_WORKING_DIR
\"
$_centry
'
\"
"
| cmd.exe
>
/dev/null
echo
}
_uninstall_win_taskscheduler
()
{
if
!
_exists schtasks
;
then
_err
"schtasks.exe is not found, are you on Windows?"
return
1
fi
if
!
echo
SCHTASKS /query /tn
"
$_WINDOWS_SCHEDULER_NAME
"
| cmd.exe
>
/dev/null
;
then
_debug
"scheduler
$_WINDOWS_SCHEDULER_NAME
is not found."
else
_info
"Removing
$_WINDOWS_SCHEDULER_NAME
"
echo
SCHTASKS /delete /f /tn
"
$_WINDOWS_SCHEDULER_NAME
"
| cmd.exe
>
/dev/null
fi
}
#confighome
#confighome
installcronjob
()
{
installcronjob
()
{
_c_home
=
"
$1
"
_c_home
=
"
$1
"
_initpath
_initpath
_CRONTAB
=
"crontab"
_CRONTAB
=
"crontab"
if
[
-f
"
$LE_WORKING_DIR
/
$PROJECT_ENTRY
"
]
;
then
lesh
=
"
\"
$LE_WORKING_DIR
\"
/
$PROJECT_ENTRY
"
else
_err
"Can not install cronjob,
$PROJECT_ENTRY
not found."
return
1
fi
if
[
"
$_c_home
"
]
;
then
_c_entry
=
"--config-home
\"
$_c_home
\"
"
fi
_t
=
$(
_time
)
random_minute
=
$(
_math
$_t
% 60
)
if
!
_exists
"
$_CRONTAB
"
&&
_exists
"fcrontab"
;
then
if
!
_exists
"
$_CRONTAB
"
&&
_exists
"fcrontab"
;
then
_CRONTAB
=
"fcrontab"
_CRONTAB
=
"fcrontab"
fi
fi
if
!
_exists
"
$_CRONTAB
"
;
then
if
!
_exists
"
$_CRONTAB
"
;
then
if
_exists cygpath
&&
_exists schtasks.exe
;
then
_info
"It seems you are on Windows, let's install Windows scheduler task."
if
_install_win_taskscheduler
"
$lesh
"
"
$_c_entry
"
"
$random_minute
"
;
then
_info
"Install Windows scheduler task success."
return
0
else
_err
"Install Windows scheduler task failed."
return
1
fi
fi
_err
"crontab/fcrontab doesn't exist, so, we can not install cron jobs."
_err
"crontab/fcrontab doesn't exist, so, we can not install cron jobs."
_err
"All your certs will not be renewed automatically."
_err
"All your certs will not be renewed automatically."
_err
"You must add your own cron job to call '
$PROJECT_ENTRY
--cron' everyday."
_err
"You must add your own cron job to call '
$PROJECT_ENTRY
--cron' everyday."
return
1
return
1
fi
fi
_info
"Installing cron job"
_info
"Installing cron job"
if
!
$_CRONTAB
-l
|
grep
"
$PROJECT_ENTRY
--cron"
;
then
if
!
$_CRONTAB
-l
|
grep
"
$PROJECT_ENTRY
--cron"
;
then
if
[
-f
"
$LE_WORKING_DIR
/
$PROJECT_ENTRY
"
]
;
then
lesh
=
"
\"
$LE_WORKING_DIR
\"
/
$PROJECT_ENTRY
"
else
_err
"Can not install cronjob,
$PROJECT_ENTRY
not found."
return
1
fi
if
[
"
$_c_home
"
]
;
then
_c_entry
=
"--config-home
\"
$_c_home
\"
"
fi
_t
=
$(
_time
)
random_minute
=
$(
_math
$_t
% 60
)
if
_exists
uname
&&
uname
-a
|
grep
SunOS
>
/dev/null
;
then
if
_exists
uname
&&
uname
-a
|
grep
SunOS
>
/dev/null
;
then
$_CRONTAB
-l
|
{
$_CRONTAB
-l
|
{
cat
cat
...
@@ -4979,6 +5057,16 @@ uninstallcronjob() {
...
@@ -4979,6 +5057,16 @@ uninstallcronjob() {
fi
fi
if
!
_exists
"
$_CRONTAB
"
;
then
if
!
_exists
"
$_CRONTAB
"
;
then
if
_exists cygpath
&&
_exists schtasks.exe
;
then
_info
"It seems you are on Windows, let's uninstall Windows scheduler task."
if
_uninstall_win_taskscheduler
;
then
_info
"Uninstall Windows scheduler task success."
return
0
else
_err
"Uninstall Windows scheduler task failed."
return
1
fi
fi
return
return
fi
fi
_info
"Removing cron job"
_info
"Removing cron job"
...
@@ -5110,7 +5198,7 @@ _deactivate() {
...
@@ -5110,7 +5198,7 @@ _deactivate() {
_err
"Can not get domain new order."
_err
"Can not get domain new order."
return
1
return
1
fi
fi
_authorizations_seg
=
"
$(
echo
"
$response
"
|
tr
-d
'\r\n'
|
_egrep_o
'"authorizations" *: *\[[^\]*\]'
|
cut
-d
'['
-f
2 |
tr
-d
']'
|
tr
-d
'"'
)
"
_authorizations_seg
=
"
$(
echo
"
$response
"
| _egrep_o
'"authorizations" *: *\[[^\]*\]'
|
cut
-d
'['
-f
2 |
tr
-d
']'
|
tr
-d
'"'
)
"
_debug2 _authorizations_seg
"
$_authorizations_seg
"
_debug2 _authorizations_seg
"
$_authorizations_seg
"
if
[
-z
"
$_authorizations_seg
"
]
;
then
if
[
-z
"
$_authorizations_seg
"
]
;
then
_err
"_authorizations_seg not found."
_err
"_authorizations_seg not found."
...
@@ -5156,16 +5244,16 @@ _deactivate() {
...
@@ -5156,16 +5244,16 @@ _deactivate() {
fi
fi
_debug
"Trigger validation."
_debug
"Trigger validation."
vtype
=
"
$VTYPE_DNS
"
vtype
=
"
$VTYPE_DNS
"
entry
=
"
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
'[^\{]*"type":"'
$vtype
'"[^\}]*'
)
"
entry
=
"
$(
echo
"
$response
"
| _egrep_o
'[^\{]*"type":"'
$vtype
'"[^\}]*'
)
"
_debug entry
"
$entry
"
_debug entry
"
$entry
"
if
[
-z
"
$entry
"
]
;
then
if
[
-z
"
$entry
"
]
;
then
_err
"Error, can not get domain token
$d
"
_err
"Error, can not get domain token
$d
"
return
1
return
1
fi
fi
token
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
'"token":"[^"]*'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
token
=
"
$(
echo
"
$entry
"
| _egrep_o
'"token":"[^"]*'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
_debug token
"
$token
"
_debug token
"
$token
"
uri
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
"
\"
$_URL_NAME
\"
:
\"
[^
\"
]*"
|
cut
-d
:
-f
2,3 |
tr
-d
'"'
)
"
uri
=
"
$(
echo
"
$entry
"
| _egrep_o
"
\"
$_URL_NAME
\"
:
\"
[^
\"
]*"
|
cut
-d
:
-f
2,3 |
tr
-d
'"'
)
"
_debug uri
"
$uri
"
_debug uri
"
$uri
"
keyauthorization
=
"
$token
.
$thumbprint
"
keyauthorization
=
"
$token
.
$thumbprint
"
...
@@ -5187,11 +5275,11 @@ _deactivate() {
...
@@ -5187,11 +5275,11 @@ _deactivate() {
break
break
fi
fi
_vtype
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
'"type": *"[^"]*"'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
_vtype
=
"
$(
echo
"
$entry
"
| _egrep_o
'"type": *"[^"]*"'
|
cut
-d
:
-f
2 |
tr
-d
'"'
)
"
_debug _vtype
"
$_vtype
"
_debug _vtype
"
$_vtype
"
_info
"Found
$_vtype
"
_info
"Found
$_vtype
"
uri
=
"
$(
printf
"%s
\n
"
"
$entry
"
| _egrep_o
"
\"
$_URL_NAME
\"
:
\"
[^
\"
]*"
|
cut
-d
:
-f
2,3 |
tr
-d
'"'
)
"
uri
=
"
$(
echo
"
$entry
"
| _egrep_o
"
\"
$_URL_NAME
\"
:
\"
[^
\"
]*"
|
cut
-d
:
-f
2,3 |
tr
-d
'"'
)
"
_debug uri
"
$uri
"
_debug uri
"
$uri
"
if
[
"
$_d_type
"
]
&&
[
"
$_d_type
"
!=
"
$_vtype
"
]
;
then
if
[
"
$_d_type
"
]
&&
[
"
$_d_type
"
!=
"
$_vtype
"
]
;
then
...
@@ -5306,13 +5394,17 @@ _precheck() {
...
@@ -5306,13 +5394,17 @@ _precheck() {
if
[
-z
"
$_nocron
"
]
;
then
if
[
-z
"
$_nocron
"
]
;
then
if
!
_exists
"crontab"
&&
!
_exists
"fcrontab"
;
then
if
!
_exists
"crontab"
&&
!
_exists
"fcrontab"
;
then
_err
"It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
if
_exists cygpath
&&
_exists schtasks.exe
;
then
_err
"We need to set cron job to renew the certs automatically."
_info
"It seems you are on Windows, we will install Windows scheduler task."
_err
"Otherwise, your certs will not be able to be renewed automatically."
else
if
[
-z
"
$FORCE
"
]
;
then
_err
"It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
_err
"Please add '--force' and try install again to go without crontab."
_err
"We need to set cron job to renew the certs automatically."
_err
"./
$PROJECT_ENTRY
--install --force"
_err
"Otherwise, your certs will not be able to be renewed automatically."
return
1
if
[
-z
"
$FORCE
"
]
;
then
_err
"Please add '--force' and try install again to go without crontab."
_err
"./
$PROJECT_ENTRY
--install --force"
return
1
fi
fi
fi
fi
fi
fi
fi
...
...
dnsapi/README.md
View file @
22bab90a
...
@@ -1310,18 +1310,54 @@ To issue a certificate run:
...
@@ -1310,18 +1310,54 @@ To issue a certificate run:
acme.sh --issue --dns dns_nederhost -d example.com -d
*
.example.com
acme.sh --issue --dns dns_nederhost -d example.com -d
*
.example.com
```
```
## 69. Use
OpenProvider
API
## 69. Use
Zone.ee DNS
API
First, you need to
enable API access and
retri
e
ve your
password hash
on https://
rcp.openprovider.eu/account/dashboard.php
First, you
'll
need to retrive your
API key. Estonian insructi
on
s
https://
help.zone.eu/kb/zoneid-api-v2/
```
```
export OPENPROVIDER_USER='username'
export ZONE_Username=yourusername
export OPENPROVIDER_PASSWORDHASH='xxx'
export ZONE_Key=keygoeshere
```
To issue a cert run:
```
acme.sh --issue -d example.com -d www.example.com --dns dns_zone
```
`ZONE_Username` and `ZONE_Key` will be saved in `~/.acme.sh/account.conf` and will be reused when needed.
## 70. Use UltraDNS API
UltraDNS is a paid for service that provides DNS, as well as Web and Mail forwarding (as well as reporting, auditing, and advanced tools).
More information can be found here: https://www.security.neustar/lp/ultra20/index.html
The REST API documentation for this service is found here: https://portal.ultradns.com/static/docs/REST-API_User_Guide.pdf
Set your UltraDNS User name, and password; these would be the same you would use here:
https://portal.ultradns.com/ - or if you create an API only user, that username and password would be better utilized.
```
```
export ULTRA_USR="abcd"
export ULTRA_PWD="efgh"
To issue a cert run:
To issue a cert run:
acme.sh --issue --dns dns_ultra -d example.com -d www.example.com
```
```
`ULTRA_USR` and `ULTRA_PWD` will be saved in `~/.acme.sh/account.conf` and will be resued when needed.
## 71. Use OpenProvider API
First, you need to enable API access and retrieve your password hash on https://rcp.openprovider.eu/account/dashboard.php
```
export OPENPROVIDER_USER='username'
export OPENPROVIDER_PASSWORDHASH='xxx'
acme.sh --issue --dns dns_openprovider -d example.com -d www.example.com
acme.sh --issue --dns dns_openprovider -d example.com -d www.example.com
```
```
...
...
dnsapi/dns_hostingde.sh
View file @
22bab90a
...
@@ -28,6 +28,7 @@ dns_hostingde_rm() {
...
@@ -28,6 +28,7 @@ dns_hostingde_rm() {
_hostingde_apiKey
()
{
_hostingde_apiKey
()
{
HOSTINGDE_APIKEY
=
"
${
HOSTINGDE_APIKEY
:-
$(
_readaccountconf_mutable HOSTINGDE_APIKEY
)
}
"
HOSTINGDE_APIKEY
=
"
${
HOSTINGDE_APIKEY
:-
$(
_readaccountconf_mutable HOSTINGDE_APIKEY
)
}
"
HOSTINGDE_ENDPOINT
=
"
${
HOSTINGDE_ENDPOINT
:-
$(
_readaccountconf_mutable HOSTINGDE_ENDPOINT
)
}
"
if
[
-z
"
$HOSTINGDE_APIKEY
"
]
||
[
-z
"
$HOSTINGDE_ENDPOINT
"
]
;
then
if
[
-z
"
$HOSTINGDE_APIKEY
"
]
||
[
-z
"
$HOSTINGDE_ENDPOINT
"
]
;
then
HOSTINGDE_APIKEY
=
""
HOSTINGDE_APIKEY
=
""
HOSTINGDE_ENDPOINT
=
""
HOSTINGDE_ENDPOINT
=
""
...
...
dnsapi/dns_namecom.sh
View file @
22bab90a
...
@@ -13,6 +13,8 @@ dns_namecom_add() {
...
@@ -13,6 +13,8 @@ dns_namecom_add() {
fulldomain
=
$1
fulldomain
=
$1
txtvalue
=
$2
txtvalue
=
$2
Namecom_Username
=
"
${
Namecom_Username
:-
$(
_readaccountconf_mutable Namecom_Username
)
}
"
Namecom_Token
=
"
${
Namecom_Token
:-
$(
_readaccountconf_mutable Namecom_Token
)
}
"
# First we need name.com credentials.
# First we need name.com credentials.
if
[
-z
"
$Namecom_Username
"
]
;
then
if
[
-z
"
$Namecom_Username
"
]
;
then
Namecom_Username
=
""
Namecom_Username
=
""
...
@@ -27,10 +29,11 @@ dns_namecom_add() {
...
@@ -27,10 +29,11 @@ dns_namecom_add() {
_err
"Please specify that in your environment variable."
_err
"Please specify that in your environment variable."
return
1
return
1
fi
fi
_debug Namecom_Username
"
$Namecom_Username
"
_secure_debug Namecom_Token
"
$Namecom_Token
"
# Save them in configuration.
# Save them in configuration.
_saveaccountconf Namecom_Username
"
$Namecom_Username
"
_saveaccountconf
_mutable
Namecom_Username
"
$Namecom_Username
"
_saveaccountconf Namecom_Token
"
$Namecom_Token
"
_saveaccountconf
_mutable
Namecom_Token
"
$Namecom_Token
"
# Login in using API
# Login in using API
if
!
_namecom_login
;
then
if
!
_namecom_login
;
then
...
@@ -46,7 +49,7 @@ dns_namecom_add() {
...
@@ -46,7 +49,7 @@ dns_namecom_add() {
# Add TXT record.
# Add TXT record.
_namecom_addtxt_json
=
"{
\"
host
\"
:
\"
$_sub_domain
\"
,
\"
type
\"
:
\"
TXT
\"
,
\"
answer
\"
:
\"
$txtvalue
\"
,
\"
ttl
\"
:
\"
300
\"
}"
_namecom_addtxt_json
=
"{
\"
host
\"
:
\"
$_sub_domain
\"
,
\"
type
\"
:
\"
TXT
\"
,
\"
answer
\"
:
\"
$txtvalue
\"
,
\"
ttl
\"
:
\"
300
\"
}"
if
_namecom_rest POST
"domains/
$_domain
/records"
"
$_namecom_addtxt_json
"
;
then
if
_namecom_rest POST
"domains/
$_domain
/records"
"
$_namecom_addtxt_json
"
;
then
_retvalue
=
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
"
\"
$_sub_domain
\"
"
)
_retvalue
=
$(
echo
"
$response
"
| _egrep_o
"
\"
$_sub_domain
\"
"
)
if
[
"
$_retvalue
"
]
;
then
if
[
"
$_retvalue
"
]
;
then
_info
"Successfully added TXT record, ready for validation."
_info
"Successfully added TXT record, ready for validation."
return
0
return
0
...
@@ -63,6 +66,8 @@ dns_namecom_rm() {
...
@@ -63,6 +66,8 @@ dns_namecom_rm() {
fulldomain
=
$1
fulldomain
=
$1
txtvalue
=
$2
txtvalue
=
$2
Namecom_Username
=
"
${
Namecom_Username
:-
$(
_readaccountconf_mutable Namecom_Username
)
}
"
Namecom_Token
=
"
${
Namecom_Token
:-
$(
_readaccountconf_mutable Namecom_Token
)
}
"
if
!
_namecom_login
;
then
if
!
_namecom_login
;
then
return
1
return
1
fi
fi
...
@@ -75,7 +80,7 @@ dns_namecom_rm() {
...
@@ -75,7 +80,7 @@ dns_namecom_rm() {
# Get the record id.
# Get the record id.
if
_namecom_rest GET
"domains/
$_domain
/records"
;
then
if
_namecom_rest GET
"domains/
$_domain
/records"
;
then
_record_id
=
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
"
\"
id
\"
:[0-9]+,
\"
domainName
\"
:
\"
$_domain
\"
,
\"
host
\"
:
\"
$_sub_domain
\"
,
\"
fqdn
\"
:
\"
$fulldomain
.
\"
,
\"
type
\"
:
\"
TXT
\"
,
\"
answer
\"
:
\"
$txtvalue
\"
"
|
cut
-d
\"
-f
3 | _egrep_o
[
0-9]+
)
_record_id
=
$(
echo
"
$response
"
| _egrep_o
"
\"
id
\"
:[0-9]+,
\"
domainName
\"
:
\"
$_domain
\"
,
\"
host
\"
:
\"
$_sub_domain
\"
,
\"
fqdn
\"
:
\"
$fulldomain
.
\"
,
\"
type
\"
:
\"
TXT
\"
,
\"
answer
\"
:
\"
$txtvalue
\"
"
|
cut
-d
\"
-f
3 | _egrep_o
[
0-9]+
)
_debug record_id
"
$_record_id
"
_debug record_id
"
$_record_id
"
if
[
"
$_record_id
"
]
;
then
if
[
"
$_record_id
"
]
;
then
_info
"Successfully retrieved the record id for ACME challenge."
_info
"Successfully retrieved the record id for ACME challenge."
...
@@ -126,10 +131,12 @@ _namecom_login() {
...
@@ -126,10 +131,12 @@ _namecom_login() {
_namecom_auth
=
$(
printf
"%s:%s"
"
$Namecom_Username
"
"
$Namecom_Token
"
| _base64
)
_namecom_auth
=
$(
printf
"%s:%s"
"
$Namecom_Username
"
"
$Namecom_Token
"
| _base64
)
if
_namecom_rest GET
"hello"
;
then
if
_namecom_rest GET
"hello"
;
then
retcode
=
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
"
\"
username
\"\:\"
$Namecom_Username
\"
"
)
retcode
=
$(
echo
"
$response
"
| _egrep_o
"
\"
username
\"\:\"
$Namecom_Username
\"
"
)
if
[
"
$retcode
"
]
;
then
if
[
"
$retcode
"
]
;
then
_info
"Successfully logged in."
_info
"Successfully logged in."
else
else
_err
"
$response
"
_err
"Please add your ip to api whitelist"
_err
"Logging in failed."
_err
"Logging in failed."
return
1
return
1
fi
fi
...
...
dnsapi/dns_netcup.sh
View file @
22bab90a
...
@@ -8,6 +8,7 @@ end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
...
@@ -8,6 +8,7 @@ end="https://ccp.netcup.net/run/webservice/servers/endpoint.php?JSON"
client
=
""
client
=
""
dns_netcup_add
()
{
dns_netcup_add
()
{
_debug NC_Apikey
"
$NC_Apikey
"
login
login
if
[
"
$NC_Apikey
"
=
""
]
||
[
"
$NC_Apipw
"
=
""
]
||
[
"
$NC_CID
"
=
""
]
;
then
if
[
"
$NC_Apikey
"
=
""
]
||
[
"
$NC_Apipw
"
=
""
]
||
[
"
$NC_CID
"
=
""
]
;
then
_err
"No Credentials given"
_err
"No Credentials given"
...
...
dnsapi/dns_ultra.sh
0 → 100644
View file @
22bab90a
#!/usr/bin/env sh
#
# ULTRA_USR="your_user_goes_here"
#
# ULTRA_PWD="some_password_goes_here"
ULTRA_API
=
"https://restapi.ultradns.com/v2/"
#Usage: add _acme-challenge.www.domain.com "some_long_string_of_characters_go_here_from_lets_encrypt"
dns_ultra_add
()
{
fulldomain
=
$1
txtvalue
=
$2
export
txtvalue
ULTRA_USR
=
"
${
ULTRA_USR
:-
$(
_readaccountconf_mutable ULTRA_USR
)
}
"
ULTRA_PWD
=
"
${
ULTRA_PWD
:-
$(
_readaccountconf_mutable ULTRA_PWD
)
}
"
if
[
-z
"
$ULTRA_USR
"
]
||
[
-z
"
$ULTRA_PWD
"
]
;
then
ULTRA_USR
=
""
ULTRA_PWD
=
""
_err
"You didn't specify an UltraDNS username and password yet"
return
1
fi
# save the username and password to the account conf file.
_saveaccountconf_mutable ULTRA_USR
"
$ULTRA_USR
"
_saveaccountconf_mutable ULTRA_PWD
"
$ULTRA_PWD
"
_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"
_ultra_rest GET
"zones/
${
_domain_id
}
/rrsets/TXT?q=value:
${
fulldomain
}
"
if
printf
"%s"
"
$response
"
|
grep
\"
totalCount
\"
>
/dev/null
;
then
_err
"Error, it would appear that this record already exists. Please review existing TXT records for this domain."
return
1
fi
_info
"Adding record"
if
_ultra_rest POST
"zones/
$_domain_id
/rrsets/TXT/
${
_sub_domain
}
"
'{"ttl":300,"rdata":["'
"
${
txtvalue
}
"
'"]}'
;
then
if
_contains
"
$response
"
"Successful"
;
then
_info
"Added, OK"
return
0
elif
_contains
"
$response
"
"Resource Record of type 16 with these attributes already exists"
;
then
_info
"Already exists, OK"
return
0
else
_err
"Add txt record error."
return
1
fi
fi
_err
"Add txt record error."
}
dns_ultra_rm
()
{
fulldomain
=
$1
txtvalue
=
$2
export
txtvalue
ULTRA_USR
=
"
${
ULTRA_USR
:-
$(
_readaccountconf_mutable ULTRA_USR
)
}
"
ULTRA_PWD
=
"
${
ULTRA_PWD
:-
$(
_readaccountconf_mutable ULTRA_PWD
)
}
"
if
[
-z
"
$ULTRA_USR
"
]
||
[
-z
"
$ULTRA_PWD
"
]
;
then
ULTRA_USR
=
""
ULTRA_PWD
=
""
_err
"You didn't specify an UltraDNS username and password yet"
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"
_ultra_rest GET
"zones/
${
_domain_id
}
/rrsets?q=kind:RECORDS+owner:
${
_sub_domain
}
"
if
!
printf
"%s"
"
$response
"
|
grep
\"
resultInfo
\"
>
/dev/null
;
then
_err
"There was an error in obtaining the resource records for
${
_domain_id
}
"
return
1
fi
count
=
$(
echo
"
$response
"
| _egrep_o
"
\"
returnedCount
\"
:[^,]*"
|
cut
-d
:
-f2
|
cut
-d
'}'
-f1
)
_debug count
"
${
count
}
"
if
[
"
${
count
}
"
=
""
]
;
then
_info
"Text record is not present, will not delete anything."
else
if
!
_ultra_rest DELETE
"zones/
$_domain_id
/rrsets/TXT/
${
_sub_domain
}
"
'{"ttl":300,"rdata":["'
"
${
txtvalue
}
"
'"]}'
;
then
_err
"Deleting the record did not succeed, please verify/check."
return
1
fi
_contains
"
$response
"
""
fi
}
#################### Private functions below ##################################
#_acme-challenge.www.domain.com
#returns
# _sub_domain=_acme-challenge.www
# _domain=domain.com
# _domain_id=sdjkglgdfewsdfg
_get_root
()
{
domain
=
$1
i
=
2
p
=
1
while
true
;
do
h
=
$(
printf
"%s"
"
$domain
"
|
cut
-d
.
-f
$i
-100
)
_debug h
"
$h
"
_debug response
"
$response
"
if
[
-z
"
$h
"
]
;
then
#not valid
return
1
fi
if
!
_ultra_rest GET
"zones"
;
then
return
1
fi
if
_contains
"
${
response
}
"
"
${
h
}
."
>
/dev/null
;
then
_domain_id
=
$(
echo
"
$response
"
| _egrep_o
"
${
h
}
"
)
if
[
"
$_domain_id
"
]
;
then
_sub_domain
=
$(
printf
"%s"
"
$domain
"
|
cut
-d
.
-f
1-
$p
)
_domain
=
"
${
h
}
"
_debug sub_domain
"
${
_sub_domain
}
"
_debug domain
"
${
_domain
}
"
return
0
fi
return
1
fi
p
=
$i
i
=
$(
_math
"
$i
"
+ 1
)
done
return
1
}
_ultra_rest
()
{
m
=
$1
ep
=
"
$2
"
data
=
"
$3
"
_debug
"
$ep
"
_debug TOKEN
"
${
AUTH_TOKEN
}
"
_ultra_login
export
_H1
=
"Content-Type: application/json"
export
_H2
=
"Authorization: Bearer
${
AUTH_TOKEN
}
"
if
[
"
$m
"
!=
"GET"
]
;
then
_debug data
"
${
data
}
"
response
=
"
$(
_post
"
${
data
}
"
"
${
ULTRA_API
}
"
/
"
${
ep
}
"
""
"
${
m
}
"
)
"
else
response
=
"
$(
_get
"
$ULTRA_API
/
$ep
"
)
"
fi
}
_ultra_login
()
{
export
_H1
=
""
export
_H2
=
""
AUTH_TOKEN
=
$(
_post
"grant_type=password&username=
${
ULTRA_USR
}
&password=
${
ULTRA_PWD
}
"
"
${
ULTRA_API
}
authorization/token"
|
cut
-d
,
-f3
|
cut
-d
\"
-f4
)
export
AUTH_TOKEN
}
dnsapi/dns_zone.sh
0 → 100755
View file @
22bab90a
#!/usr/bin/env sh
# Zone.ee dns API
# https://help.zone.eu/kb/zoneid-api-v2/
# required ZONE_Username and ZONE_Key
ZONE_Api
=
"https://api.zone.eu/v2"
######## Public functions #####################
#Usage: dns_zone_add _acme-challenge.www.domain.com "XKrxpRBosdIKFzxW_CT3KLZNf6q0HG9i01zxXp5CPBs"
dns_zone_add
()
{
fulldomain
=
$1
txtvalue
=
$2
_info
"Using zone.ee dns api"
_debug fulldomain
"
$fulldomain
"
_debug txtvalue
"
$txtvalue
"
ZONE_Username
=
"
${
ZONE_Username
:-
$(
_readaccountconf_mutable ZONE_Username
)
}
"
ZONE_Key
=
"
${
ZONE_Key
:-
$(
_readaccountconf_mutable ZONE_Key
)
}
"
if
[
-z
"
$ZONE_Username
"
]
||
[
-z
"
$ZONE_Key
"
]
;
then
ZONE_Username
=
""
ZONE_Key
=
""
_err
"Zone api key and username must be present."
return
1
fi
_saveaccountconf_mutable ZONE_Username
"
$ZONE_Username
"
_saveaccountconf_mutable ZONE_Key
"
$ZONE_Key
"
_debug
"First detect the root zone"
if
!
_get_root
"
$fulldomain
"
;
then
_err
"invalid domain"
return
1
fi
_debug
"Adding txt record"
if
_zone_rest POST
"dns/
${
_domain
}
/txt"
"{
\"
name
\"
:
\"
$fulldomain
\"
,
\"
destination
\"
:
\"
$txtvalue
\"
}"
;
then
if
printf
--
"%s"
"
$response
"
|
grep
"
$fulldomain
"
>
/dev/null
;
then
_info
"Added, OK"
return
0
else
_err
"Adding txt record error."
return
1
fi
else
_err
"Adding txt record error."
fi
}
#Usage: fulldomain txtvalue
#Remove the txt record after validation.
dns_zone_rm
()
{
fulldomain
=
$1
txtvalue
=
$2
_info
"Using zone.ee dns api"
_debug fulldomain
"
$fulldomain
"
_debug txtvalue
"
$txtvalue
"
ZONE_Username
=
"
${
ZONE_Username
:-
$(
_readaccountconf_mutable ZONE_Username
)
}
"
ZONE_Key
=
"
${
ZONE_Key
:-
$(
_readaccountconf_mutable ZONE_Key
)
}
"
if
[
-z
"
$ZONE_Username
"
]
||
[
-z
"
$ZONE_Key
"
]
;
then
ZONE_Username
=
""
ZONE_Key
=
""
_err
"Zone api key and username must be present."
return
1
fi
_saveaccountconf_mutable ZONE_Username
"
$ZONE_Username
"
_saveaccountconf_mutable ZONE_Key
"
$ZONE_Key
"
_debug
"First detect the root zone"
if
!
_get_root
"
$fulldomain
"
;
then
_err
"invalid domain"
return
1
fi
_debug
"Getting txt records"
_debug _domain
"
$_domain
"
_zone_rest GET
"dns/
${
_domain
}
/txt"
if
printf
"%s"
"
$response
"
|
grep
\"
error
\"
>
/dev/null
;
then
_err
"Error"
return
1
fi
count
=
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
"
\"
name
\"
:
\"
$fulldomain
\"
"
|
wc
-l
)
_debug count
"
$count
"
if
[
"
$count
"
=
"0"
]
;
then
_info
"Nothing to remove."
else
record_id
=
$(
printf
"%s
\n
"
"
$response
"
| _egrep_o
"
\"
id
\"
:
\"
[^
\"
]*
\"
,
\"
resource_url
\"
:
\"
[^
\"
]*
\"
,
\"
name
\"
:
\"
$fulldomain
\"
,"
|
cut
-d
:
-f2
|
cut
-d
,
-f1
|
tr
-d
\"
| _head_n 1
)
if
[
-z
"
$record_id
"
]
;
then
_err
"No id found to remove."
return
1
fi
if
!
_zone_rest DELETE
"dns/
${
_domain
}
/txt/
$record_id
"
;
then
_err
"Record deleting error."
return
1
fi
_info
"Record deleted"
return
0
fi
}
#################### Private functions below ##################################
_zone_rest
()
{
m
=
$1
ep
=
"
$2
"
data
=
"
$3
"
_debug
"
$ep
"
realm
=
"
$(
printf
"%s"
"
$ZONE_Username
:
$ZONE_Key
"
| _base64
)
"
export
_H1
=
"Authorization: Basic
$realm
"
export
_H2
=
"Content-Type: application/json"
if
[
"
$m
"
!=
"GET"
]
;
then
_debug data
"
$data
"
response
=
"
$(
_post
"
$data
"
"
$ZONE_Api
/
$ep
"
""
"
$m
"
)
"
else
response
=
"
$(
_get
"
$ZONE_Api
/
$ep
"
)
"
fi
if
[
"
$?
"
!=
"0"
]
;
then
_err
"error
$ep
"
return
1
fi
_debug2 response
"
$response
"
return
0
}
_get_root
()
{
domain
=
$1
i
=
2
while
true
;
do
h
=
$(
printf
"%s"
"
$domain
"
|
cut
-d
.
-f
$i
-100
)
_debug h
"
$h
"
if
[
-z
"
$h
"
]
;
then
return
1
fi
if
!
_zone_rest GET
"dns/
$h
/a"
;
then
return
1
fi
if
_contains
"
$response
"
"
\"
name
\"
:
\"
$h
\"
"
>
/dev/null
;
then
_domain
=
$h
return
0
fi
i
=
$(
_math
"
$i
"
+ 1
)
done
return
0
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment