]> git.proxmox.com Git - mirror_acme.sh.git/blame - acme.sh
2.2.9 support --upgrade
[mirror_acme.sh.git] / acme.sh
CommitLineData
0a7c9364 1#!/usr/bin/env sh
bfdf1f48 2
52677b0a 3VER=2.2.9
a7b7355d 4
6cc11ffb 5PROJECT_NAME="acme.sh"
a7b7355d 6
6cc11ffb 7PROJECT_ENTRY="acme.sh"
8
9PROJECT="https://github.com/Neilpang/$PROJECT_NAME"
4c3b3608 10
11DEFAULT_CA="https://acme-v01.api.letsencrypt.org"
12DEFAULT_AGREEMENT="https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf"
13
a7b7355d 14DEFAULT_USER_AGENT="$PROJECT_ENTRY client: $PROJECT"
bbbdcb09 15
4c3b3608 16STAGE_CA="https://acme-staging.api.letsencrypt.org"
17
18VTYPE_HTTP="http-01"
19VTYPE_DNS="dns-01"
e22bcf7c 20VTYPE_TLS="tls-sni-01"
21VTYPE_TLS2="tls-sni-02"
22
523c7682 23MAX_RENEW=80
24
e22bcf7c 25W_TLS="tls"
4c3b3608 26
88fab7d6 27BEGIN_CSR="-----BEGIN CERTIFICATE REQUEST-----"
28END_CSR="-----END CERTIFICATE REQUEST-----"
29
30BEGIN_CERT="-----BEGIN CERTIFICATE-----"
31END_CERT="-----END CERTIFICATE-----"
32
cc179731 33RENEW_SKIP=2
34
8663fb7e 35if [ -z "$AGREEMENT" ] ; then
4c3b3608 36 AGREEMENT="$DEFAULT_AGREEMENT"
37fi
38
4c3b3608 39
00a50605 40
41_URGLY_PRINTF=""
f4312b44 42if [ "$(printf '\x41')" != 'A' ] ; then
00a50605 43 _URGLY_PRINTF=1
44fi
45
46
4c3b3608 47_info() {
8663fb7e 48 if [ -z "$2" ] ; then
a63b05a9 49 echo "[$(date)] $1"
4c3b3608 50 else
19539575 51 echo "[$(date)] $1='$2'"
4c3b3608 52 fi
53}
54
55_err() {
a63b05a9 56 _info "$@" >&2
4c3b3608 57 return 1
58}
59
c60883ef 60_debug() {
8663fb7e 61 if [ -z "$DEBUG" ] ; then
c60883ef 62 return
63 fi
a63b05a9 64 _err "$@"
c60883ef 65 return 0
66}
67
a63b05a9 68_debug2() {
8663fb7e 69 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
a63b05a9 70 _debug "$@"
71 fi
72 return
73}
74
dceb3aca 75_startswith(){
76 _str="$1"
77 _sub="$2"
19539575 78 echo "$_str" | grep "^$_sub" >/dev/null 2>&1
dceb3aca 79}
80
81_contains(){
82 _str="$1"
83 _sub="$2"
19539575 84 echo "$_str" | grep "$_sub" >/dev/null 2>&1
dceb3aca 85}
86
c53da1ef 87_hasfield() {
88 _str="$1"
89 _field="$2"
90 _sep="$3"
91 if [ -z "$_field" ] ; then
92 _err "Usage: str field [sep]"
93 return 1
94 fi
95
96 if [ -z "$_sep" ] ; then
97 _sep=","
98 fi
99
100 for f in $(echo "$_str" | tr ',' ' ') ; do
101 if [ "$f" = "$_field" ] ; then
102 _debug "'$_str' contains '$_field'"
103 return 0 #contains ok
104 fi
105 done
106 _debug "'$_str' does not contain '$_field'"
107 return 1 #not contains
108}
109
dceb3aca 110_exists(){
c60883ef 111 cmd="$1"
8663fb7e 112 if [ -z "$cmd" ] ; then
c60883ef 113 _err "Usage: _exists cmd"
114 return 1
115 fi
eac18b1c 116 if type command >/dev/null 2>&1 ; then
19539575 117 command -v "$cmd" >/dev/null 2>&1
eac18b1c 118 else
19539575 119 type "$cmd" >/dev/null 2>&1
eac18b1c 120 fi
c60883ef 121 ret="$?"
a63b05a9 122 _debug2 "$cmd exists=$ret"
c60883ef 123 return $ret
124}
125
00a50605 126#a + b
127_math(){
128 expr "$@"
129}
130
131_h_char_2_dec() {
132 _ch=$1
133 case "${_ch}" in
134 a|A)
19539575 135 printf "10"
00a50605 136 ;;
137 b|B)
19539575 138 printf "11"
00a50605 139 ;;
140 c|C)
19539575 141 printf "12"
00a50605 142 ;;
143 d|D)
19539575 144 printf "13"
00a50605 145 ;;
146 e|E)
19539575 147 printf "14"
00a50605 148 ;;
149 f|F)
19539575 150 printf "15"
00a50605 151 ;;
152 *)
19539575 153 printf "%s" "$_ch"
00a50605 154 ;;
19539575 155 esac
00a50605 156
157}
158
4c3b3608 159_h2b() {
160 hex=$(cat)
161 i=1
162 j=2
00a50605 163 if _exists let ; then
164 uselet="1"
165 fi
166 _debug uselet "$uselet"
f4312b44 167 _debug _URGLY_PRINTF "$_URGLY_PRINTF"
19539575 168 while true ; do
00a50605 169 if [ -z "$_URGLY_PRINTF" ] ; then
19539575 170 h="$(printf $hex | cut -c $i-$j)"
00a50605 171 if [ -z "$h" ] ; then
172 break;
173 fi
174 printf "\x$h"
175 else
19539575 176 ic="$(printf $hex | cut -c $i)"
177 jc="$(printf $hex | cut -c $j)"
00a50605 178 if [ -z "$ic$jc" ] ; then
179 break;
180 fi
19539575 181 ic="$(_h_char_2_dec "$ic")"
182 jc="$(_h_char_2_dec "$jc")"
00a50605 183 printf '\'"$(printf %o "$(_math $ic \* 16 + $jc)")"
4c3b3608 184 fi
00a50605 185 if [ "$uselet" ] ; then
f4312b44 186 let "i+=2" >/dev/null
187 let "j+=2" >/dev/null
00a50605 188 else
189 i="$(_math $i + 2)"
190 j="$(_math $j + 2)"
191 fi
4c3b3608 192 done
193}
194
c60883ef 195#options file
196_sed_i() {
197 options="$1"
198 filename="$2"
8663fb7e 199 if [ -z "$filename" ] ; then
c60883ef 200 _err "Usage:_sed_i options filename"
201 return 1
202 fi
14f3dbb7 203 _debug2 options "$options"
204 if sed -h 2>&1 | grep "\-i\[SUFFIX]" >/dev/null 2>&1; then
c60883ef 205 _debug "Using sed -i"
14f3dbb7 206 sed -i "$options" "$filename"
c60883ef 207 else
208 _debug "No -i support in sed"
19539575 209 text="$(cat "$filename")"
c60883ef 210 echo "$text" | sed "$options" > "$filename"
211 fi
212}
213
88fab7d6 214#Usage: file startline endline
215_getfile() {
216 filename="$1"
217 startline="$2"
218 endline="$3"
8663fb7e 219 if [ -z "$endline" ] ; then
88fab7d6 220 _err "Usage: file startline endline"
221 return 1
222 fi
223
19539575 224 i="$(grep -n -- "$startline" "$filename" | cut -d : -f 1)"
8663fb7e 225 if [ -z "$i" ] ; then
88fab7d6 226 _err "Can not find start line: $startline"
227 return 1
228 fi
19539575 229 i="$(_math "$i" + 1)"
230 _debug i "$i"
88fab7d6 231
19539575 232 j="$(grep -n -- "$endline" "$filename" | cut -d : -f 1)"
8663fb7e 233 if [ -z "$j" ] ; then
88fab7d6 234 _err "Can not find end line: $endline"
235 return 1
236 fi
19539575 237 j="$(_math "$j" - 1)"
238 _debug j "$j"
88fab7d6 239
19539575 240 sed -n "$i,${j}p" "$filename"
88fab7d6 241
242}
243
244#Usage: multiline
4c3b3608 245_base64() {
8663fb7e 246 if [ "$1" ] ; then
88fab7d6 247 openssl base64 -e
248 else
249 openssl base64 -e | tr -d '\r\n'
250 fi
251}
252
253#Usage: multiline
254_dbase64() {
8663fb7e 255 if [ "$1" ] ; then
88fab7d6 256 openssl base64 -d -A
257 else
258 openssl base64 -d
259 fi
260}
261
e22bcf7c 262#Usage: hashalg [outputhex]
88fab7d6 263#Output Base64-encoded digest
264_digest() {
265 alg="$1"
8663fb7e 266 if [ -z "$alg" ] ; then
88fab7d6 267 _err "Usage: _digest hashalg"
268 return 1
269 fi
270
e22bcf7c 271 outputhex="$2"
272
8663fb7e 273 if [ "$alg" = "sha256" ] ; then
e22bcf7c 274 if [ "$outputhex" ] ; then
275 echo $(openssl dgst -sha256 -hex | cut -d = -f 2)
276 else
277 openssl dgst -sha256 -binary | _base64
278 fi
88fab7d6 279 else
280 _err "$alg is not supported yet"
281 return 1
282 fi
283
284}
285
286#Usage: keyfile hashalg
287#Output: Base64-encoded signature value
288_sign() {
289 keyfile="$1"
290 alg="$2"
8663fb7e 291 if [ -z "$alg" ] ; then
88fab7d6 292 _err "Usage: _sign keyfile hashalg"
293 return 1
294 fi
295
8663fb7e 296 if [ "$alg" = "sha256" ] ; then
88fab7d6 297 openssl dgst -sha256 -sign "$keyfile" | _base64
298 else
299 _err "$alg is not supported yet"
300 return 1
301 fi
302
4c3b3608 303}
304
e22bcf7c 305# _createkey 2048|ec-256 file
306_createkey() {
307 length="$1"
308 f="$2"
309 isec=""
310 if _startswith "$length" "ec-" ; then
311 isec="1"
312 length=$(printf $length | cut -d '-' -f 2-100)
313 eccname="$length"
314 fi
315
316 if [ -z "$length" ] ; then
317 if [ "$isec" ] ; then
318 length=256
319 else
320 length=2048
321 fi
322 fi
323 _info "Use length $length"
324
325 if [ "$isec" ] ; then
326 if [ "$length" = "256" ] ; then
327 eccname="prime256v1"
328 fi
329 if [ "$length" = "384" ] ; then
330 eccname="secp384r1"
331 fi
332 if [ "$length" = "521" ] ; then
333 eccname="secp521r1"
334 fi
335 _info "Using ec name: $eccname"
336 fi
337
338 #generate account key
339 if [ "$isec" ] ; then
340 openssl ecparam -name $eccname -genkey 2>/dev/null > "$f"
341 else
342 openssl genrsa $length 2>/dev/null > "$f"
343 fi
344}
345
346#_createcsr cn san_list keyfile csrfile conf
347_createcsr() {
348 _debug _createcsr
349 domain="$1"
350 domainlist="$2"
351 key="$3"
352 csr="$4"
353 csrconf="$5"
354 _debug2 domain "$domain"
355 _debug2 domainlist "$domainlist"
356 if [ -z "$domainlist" ] || [ "$domainlist" = "no" ]; then
357 #single domain
358 _info "Single domain" "$domain"
359 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\n" > "$csrconf"
360 openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
361 else
362 if _contains "$domainlist" "," ; then
363 alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")"
364 else
365 alt="DNS:$domainlist"
366 fi
367 #multi
368 _info "Multi domain" "$alt"
369 printf "[ req_distinguished_name ]\n[ req ]\ndistinguished_name = req_distinguished_name\nreq_extensions = v3_req\n[ v3_req ]\nkeyUsage = nonRepudiation, digitalSignature, keyEncipherment\nsubjectAltName=$alt" > "$csrconf"
370 openssl req -new -sha256 -key "$key" -subj "/CN=$domain" -config "$csrconf" -out "$csr"
371 fi
372}
373
374#_signcsr key csr conf cert
375_signcsr() {
376 key="$1"
377 csr="$2"
378 conf="$3"
379 cert="$4"
5aa146a5 380 _debug "_signcsr"
e22bcf7c 381
5aa146a5 382 _msg="$(openssl x509 -req -days 365 -in "$csr" -signkey "$key" -extensions v3_req -extfile "$conf" -out "$cert" 2>&1)"
383 _ret="$?"
384 _debug "$_msg"
385 return $_ret
e22bcf7c 386}
387
34c27e09 388_ss() {
389 _port="$1"
edf08da6 390
391 if _exists "ss" ; then
392 _debug "Using: ss"
19539575 393 ss -ntpl | grep ":$_port "
edf08da6 394 return 0
395 fi
396
397 if _exists "netstat" ; then
251fc37c 398 _debug "Using: netstat"
ccb96535 399 if netstat -h 2>&1 | grep "\-p proto" >/dev/null ; then
400 #for windows version netstat tool
19539575 401 netstat -anb -p tcp | grep "LISTENING" | grep ":$_port "
ccb96535 402 else
14165e5a 403 if netstat -help 2>&1 | grep "\-p protocol" >/dev/null ; then
19539575 404 netstat -an -p tcp | grep LISTEN | grep ":$_port "
edf08da6 405 else
19539575 406 netstat -ntpl | grep ":$_port "
edf08da6 407 fi
ccb96535 408 fi
34c27e09 409 return 0
410 fi
edf08da6 411
34c27e09 412 return 1
413}
414
ac2d5123 415toPkcs() {
416 domain="$1"
417 pfxPassword="$2"
8663fb7e 418 if [ -z "$domain" ] ; then
a7b7355d 419 echo "Usage: $PROJECT_ENTRY --toPkcs -d domain [--password pfx-password]"
ac2d5123 420 return 1
421 fi
422
423 _initpath "$domain"
424
8663fb7e 425 if [ "$pfxPassword" ] ; then
ac2d5123 426 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH" -password "pass:$pfxPassword"
427 else
428 openssl pkcs12 -export -out "$CERT_PFX_PATH" -inkey "$CERT_KEY_PATH" -in "$CERT_PATH" -certfile "$CA_CERT_PATH"
429 fi
430
8663fb7e 431 if [ "$?" = "0" ] ; then
ac2d5123 432 _info "Success, Pfx is exported to: $CERT_PFX_PATH"
433 fi
434
435}
436
4c3b3608 437#domain [2048]
438createAccountKey() {
439 _info "Creating account key"
8663fb7e 440 if [ -z "$1" ] ; then
a7b7355d 441 echo Usage: $PROJECT_ENTRY --createAccountKey -d domain.com [--accountkeylength 2048]
4c3b3608 442 return
443 fi
444
445 account=$1
446 length=$2
b5eb4b90 447 _debug account "$account"
448 _debug length "$length"
dceb3aca 449 if _startswith "$length" "ec-" ; then
4c3b3608 450 length=2048
451 fi
452
8663fb7e 453 if [ -z "$2" ] || [ "$2" = "no" ] ; then
4c3b3608 454 _info "Use default length 2048"
455 length=2048
456 fi
457 _initpath
458
8663fb7e 459 if [ -f "$ACCOUNT_KEY_PATH" ] ; then
4c3b3608 460 _info "Account key exists, skip"
461 return
462 else
463 #generate account key
e22bcf7c 464 _createkey $length "$ACCOUNT_KEY_PATH"
4c3b3608 465 fi
466
467}
468
469#domain length
470createDomainKey() {
471 _info "Creating domain key"
8663fb7e 472 if [ -z "$1" ] ; then
a7b7355d 473 echo Usage: $PROJECT_ENTRY --createDomainKey -d domain.com [ --keylength 2048 ]
4c3b3608 474 return
475 fi
476
477 domain=$1
4c3b3608 478 _initpath $domain
479
e22bcf7c 480 length=$2
481
8663fb7e 482 if [ ! -f "$CERT_KEY_PATH" ] || ( [ "$FORCE" ] && ! [ "$IS_RENEW" ] ); then
e22bcf7c 483 _createkey "$length" "$CERT_KEY_PATH"
4c3b3608 484 else
8663fb7e 485 if [ "$IS_RENEW" ] ; then
4c3b3608 486 _info "Domain key exists, skip"
487 return 0
488 else
489 _err "Domain key exists, do you want to overwrite the key?"
41e3eafa 490 _err "Add '--force', and try again."
4c3b3608 491 return 1
492 fi
493 fi
494
495}
496
497# domain domainlist
498createCSR() {
499 _info "Creating csr"
8663fb7e 500 if [ -z "$1" ] ; then
19539575 501 echo "Usage: $PROJECT_ENTRY --createCSR -d domain1.com [-d domain2.com -d domain3.com ... ]"
4c3b3608 502 return
503 fi
504 domain=$1
19539575 505 _initpath "$domain"
4c3b3608 506
507 domainlist=$2
508
8663fb7e 509 if [ -f "$CSR_PATH" ] && [ "$IS_RENEW" ] && [ -z "$FORCE" ]; then
4c3b3608 510 _info "CSR exists, skip"
511 return
512 fi
513
e22bcf7c 514 _createcsr "$domain" "$domainlist" "$CERT_KEY_PATH" "$CSR_PATH" "$DOMAIN_SSL_CONF"
515
4c3b3608 516}
517
166096dc 518_urlencode() {
4c3b3608 519 __n=$(cat)
520 echo $__n | tr '/+' '_-' | tr -d '= '
521}
522
523_time2str() {
524 #BSD
525 if date -u -d@$1 2>/dev/null ; then
526 return
527 fi
528
529 #Linux
530 if date -u -r $1 2>/dev/null ; then
531 return
532 fi
533
534}
535
eae29099 536_normalizeJson() {
537 sed "s/\" *: *\([\"{\[]\)/\":\1/g" | sed "s/^ *\([^ ]\)/\1/" | tr -d "\r\n"
538}
539
44df2967 540_stat() {
541 #Linux
542 if stat -c '%U:%G' "$1" 2>/dev/null ; then
543 return
544 fi
545
546 #BSD
547 if stat -f '%Su:%Sg' "$1" 2>/dev/null ; then
548 return
549 fi
550}
551
166096dc 552#keyfile
553_calcjwk() {
554 keyfile="$1"
8663fb7e 555 if [ -z "$keyfile" ] ; then
166096dc 556 _err "Usage: _calcjwk keyfile"
557 return 1
558 fi
559 EC_SIGN=""
560 if grep "BEGIN RSA PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
561 _debug "RSA key"
562 pub_exp=$(openssl rsa -in $keyfile -noout -text | grep "^publicExponent:"| cut -d '(' -f 2 | cut -d 'x' -f 2 | cut -d ')' -f 1)
8663fb7e 563 if [ "${#pub_exp}" = "5" ] ; then
166096dc 564 pub_exp=0$pub_exp
565 fi
a63b05a9 566 _debug2 pub_exp "$pub_exp"
166096dc 567
568 e=$(echo $pub_exp | _h2b | _base64)
a63b05a9 569 _debug2 e "$e"
166096dc 570
571 modulus=$(openssl rsa -in $keyfile -modulus -noout | cut -d '=' -f 2 )
00a50605 572 _debug2 modulus "$modulus"
19539575 573 n="$(printf "%s" "$modulus"| _h2b | _base64 | _urlencode )"
166096dc 574 jwk='{"e": "'$e'", "kty": "RSA", "n": "'$n'"}'
a63b05a9 575 _debug2 jwk "$jwk"
166096dc 576
577 HEADER='{"alg": "RS256", "jwk": '$jwk'}'
578 HEADERPLACE='{"nonce": "NONCE", "alg": "RS256", "jwk": '$jwk'}'
579 elif grep "BEGIN EC PRIVATE KEY" "$keyfile" > /dev/null 2>&1 ; then
580 _debug "EC key"
581 EC_SIGN="1"
582 crv="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep "^NIST CURVE:" | cut -d ":" -f 2 | tr -d " \r\n")"
19539575 583 _debug2 crv "$crv"
166096dc 584
585 pubi="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n pub: | cut -d : -f 1)"
00a50605 586 pubi=$(_math $pubi + 1)
19539575 587 _debug2 pubi "$pubi"
166096dc 588
589 pubj="$(openssl ec -in $keyfile -noout -text 2>/dev/null | grep -n "ASN1 OID:" | cut -d : -f 1)"
00a50605 590 pubj=$(_math $pubj + 1)
19539575 591 _debug2 pubj "$pubj"
166096dc 592
593 pubtext="$(openssl ec -in $keyfile -noout -text 2>/dev/null | sed -n "$pubi,${pubj}p" | tr -d " \n\r")"
a63b05a9 594 _debug2 pubtext "$pubtext"
166096dc 595
596 xlen="$(printf "$pubtext" | tr -d ':' | wc -c)"
00a50605 597 xlen=$(_math $xlen / 4)
19539575 598 _debug2 xlen "$xlen"
00a50605 599
19539575 600 xend=$(_math "$xend" + 1)
166096dc 601 x="$(printf $pubtext | cut -d : -f 2-$xend)"
19539575 602 _debug2 x "$x"
166096dc 603
604 x64="$(printf $x | tr -d : | _h2b | _base64 | _urlencode)"
19539575 605 _debug2 x64 "$x64"
00a50605 606
19539575 607 xend=$(_math "$xend" + 1)
166096dc 608 y="$(printf $pubtext | cut -d : -f $xend-10000)"
19539575 609 _debug2 y "$y"
166096dc 610
611 y64="$(printf $y | tr -d : | _h2b | _base64 | _urlencode)"
19539575 612 _debug2 y64 "$y64"
166096dc 613
614 jwk='{"kty": "EC", "crv": "'$crv'", "x": "'$x64'", "y": "'$y64'"}'
a63b05a9 615 _debug2 jwk "$jwk"
166096dc 616
617 HEADER='{"alg": "ES256", "jwk": '$jwk'}'
618 HEADERPLACE='{"nonce": "NONCE", "alg": "ES256", "jwk": '$jwk'}'
619
620 else
621 _err "Only RSA or EC key is supported."
622 return 1
623 fi
624
a63b05a9 625 _debug2 HEADER "$HEADER"
166096dc 626}
c839b2b0 627# body url [needbase64] [POST|PUT]
c60883ef 628_post() {
629 body="$1"
630 url="$2"
631 needbase64="$3"
a4270efa 632 httpmethod="$4"
c60883ef 633
a4270efa 634 if [ -z "$httpmethod" ] ; then
635 httpmethod="POST"
636 fi
637 _debug $httpmethod
484d9d2a 638 _debug "url" "$url"
c60883ef 639 if _exists "curl" ; then
8f48168c 640 _CURL="$CURL --dump-header $HTTP_HEADER "
ec9fc8cb 641 _debug "_CURL" "$_CURL"
8663fb7e 642 if [ "$needbase64" ] ; then
63a195e5 643 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" --data "$body" "$url" | _base64)"
c60883ef 644 else
63a195e5 645 response="$($_CURL --user-agent "$USER_AGENT" -X $httpmethod -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" --data "$body" "$url" )"
c60883ef 646 fi
16679b57 647 _ret="$?"
687cfcc2 648 if [ "$_ret" != "0" ] ; then
87ab2d90 649 _err "Please refer to https://curl.haxx.se/libcurl/c/libcurl-errors.html for error code: $_ret"
650 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
651 _err "Here is the curl dump log:"
652 _err "$(cat "$_CURL_DUMP")"
653 fi
687cfcc2 654 fi
c60883ef 655 else
ec9fc8cb 656 _debug "WGET" "$WGET"
8663fb7e 657 if [ "$needbase64" ] ; then
8fb9a709 658 if [ "$httpmethod"="POST" ] ; then
659 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
660 else
661 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)"
662 fi
c60883ef 663 else
8fb9a709 664 if [ "$httpmethod"="POST" ] ; then
665 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")"
666 else
667 response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")"
668 fi
c60883ef 669 fi
16679b57 670 _ret="$?"
687cfcc2 671 if [ "$_ret" != "0" ] ; then
672 _err "Please refer to https://www.gnu.org/software/wget/manual/html_node/Exit-Status.html for error code: $_ret"
673 fi
c60883ef 674 _sed_i "s/^ *//g" "$HTTP_HEADER"
675 fi
16679b57 676 _debug "_ret" "$_ret"
19539575 677 printf "%s" "$response"
16679b57 678 return $_ret
c60883ef 679}
680
681# url getheader
682_get() {
a4270efa 683 _debug GET
c60883ef 684 url="$1"
685 onlyheader="$2"
686 _debug url $url
687 if _exists "curl" ; then
ec9fc8cb 688 _debug "CURL" "$CURL"
8663fb7e 689 if [ "$onlyheader" ] ; then
63a195e5 690 $CURL -I --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" $url
c60883ef 691 else
63a195e5 692 $CURL --user-agent "$USER_AGENT" -H "$_H1" -H "$_H2" -H "$_H3" -H "$_H4" $url
c60883ef 693 fi
9aaf36cd 694 ret=$?
c60883ef 695 else
bbbdcb09 696 _debug "WGET" "$WGET"
8663fb7e 697 if [ "$onlyheader" ] ; then
a4270efa 698 $WGET --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -S -O /dev/null $url 2>&1 | sed 's/^[ ]*//g'
c60883ef 699 else
cc7fdbd6 700 $WGET --user-agent="$USER_AGENT" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" -O - $url
c60883ef 701 fi
9aaf36cd 702 ret=$?
703 fi
ec9fc8cb 704 _debug "ret" "$ret"
c60883ef 705 return $ret
706}
166096dc 707
708# url payload needbase64 keyfile
4c3b3608 709_send_signed_request() {
710 url=$1
711 payload=$2
712 needbase64=$3
166096dc 713 keyfile=$4
8663fb7e 714 if [ -z "$keyfile" ] ; then
166096dc 715 keyfile="$ACCOUNT_KEY_PATH"
716 fi
4c3b3608 717 _debug url $url
718 _debug payload "$payload"
719
166096dc 720 if ! _calcjwk "$keyfile" ; then
721 return 1
722 fi
c60883ef 723
166096dc 724 payload64=$(echo -n $payload | _base64 | _urlencode)
a63b05a9 725 _debug2 payload64 $payload64
4c3b3608 726
727 nonceurl="$API/directory"
a272ee4f 728 _headers="$(_get $nonceurl "onlyheader")"
729
7012b91f 730 if [ "$?" != "0" ] ; then
731 _err "Can not connect to $nonceurl to get nonce."
732 return 1
733 fi
a272ee4f 734
735 _debug2 _headers "$_headers"
736
737 nonce="$( echo "$_headers" | grep "Replay-Nonce:" | head -1 | tr -d "\r\n " | cut -d ':' -f 2)"
738
4c3b3608 739 _debug nonce "$nonce"
740
741 protected="$(printf "$HEADERPLACE" | sed "s/NONCE/$nonce/" )"
a63b05a9 742 _debug2 protected "$protected"
4c3b3608 743
166096dc 744 protected64="$(printf "$protected" | _base64 | _urlencode)"
a63b05a9 745 _debug2 protected64 "$protected64"
166096dc 746
88fab7d6 747 sig=$(echo -n "$protected64.$payload64" | _sign "$keyfile" "sha256" | _urlencode)
a63b05a9 748 _debug2 sig "$sig"
4c3b3608 749
750 body="{\"header\": $HEADER, \"protected\": \"$protected64\", \"payload\": \"$payload64\", \"signature\": \"$sig\"}"
a63b05a9 751 _debug2 body "$body"
4c3b3608 752
bbbdcb09 753
eae29099 754 response="$(_post "$body" $url "$needbase64")"
7012b91f 755 if [ "$?" != "0" ] ; then
756 _err "Can not post to $url."
757 return 1
758 fi
eae29099 759 _debug2 original "$response"
760
761 response="$( echo "$response" | _normalizeJson )"
4c3b3608 762
c60883ef 763 responseHeaders="$(cat $HTTP_HEADER)"
4c3b3608 764
a63b05a9 765 _debug2 responseHeaders "$responseHeaders"
766 _debug2 response "$response"
c60883ef 767 code="$(grep "^HTTP" $HTTP_HEADER | tail -1 | cut -d " " -f 2 | tr -d "\r\n" )"
4c3b3608 768 _debug code $code
769
770}
771
4c3b3608 772
773#setopt "file" "opt" "=" "value" [";"]
774_setopt() {
775 __conf="$1"
776 __opt="$2"
777 __sep="$3"
778 __val="$4"
779 __end="$5"
8663fb7e 780 if [ -z "$__opt" ] ; then
4c3b3608 781 echo usage: _setopt '"file" "opt" "=" "value" [";"]'
782 return
783 fi
8663fb7e 784 if [ ! -f "$__conf" ] ; then
4c3b3608 785 touch "$__conf"
786 fi
787
788 if grep -H -n "^$__opt$__sep" "$__conf" > /dev/null ; then
a63b05a9 789 _debug2 OK
dceb3aca 790 if _contains "$__val" "&" ; then
4c3b3608 791 __val="$(echo $__val | sed 's/&/\\&/g')"
792 fi
793 text="$(cat $__conf)"
6dfaaa70 794 echo "$text" | sed "s|^$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
4c3b3608 795
796 elif grep -H -n "^#$__opt$__sep" "$__conf" > /dev/null ; then
dceb3aca 797 if _contains "$__val" "&" ; then
4c3b3608 798 __val="$(echo $__val | sed 's/&/\\&/g')"
799 fi
800 text="$(cat $__conf)"
6dfaaa70 801 echo "$text" | sed "s|^#$__opt$__sep.*$|$__opt$__sep$__val$__end|" > "$__conf"
4c3b3608 802
803 else
a63b05a9 804 _debug2 APP
4c3b3608 805 echo "$__opt$__sep$__val$__end" >> "$__conf"
806 fi
807 _debug "$(grep -H -n "^$__opt$__sep" $__conf)"
808}
809
810#_savedomainconf key value
811#save to domain.conf
812_savedomainconf() {
813 key="$1"
814 value="$2"
8663fb7e 815 if [ "$DOMAIN_CONF" ] ; then
4d2f38b0 816 _setopt "$DOMAIN_CONF" "$key" "=" "\"$value\""
817 else
818 _err "DOMAIN_CONF is empty, can not save $key=$value"
819 fi
820}
821
822#_cleardomainconf key
823_cleardomainconf() {
824 key="$1"
825 if [ "$DOMAIN_CONF" ] ; then
826 _sed_i "s/^$key.*$//" "$DOMAIN_CONF"
4c3b3608 827 else
828 _err "DOMAIN_CONF is empty, can not save $key=$value"
829 fi
830}
831
832#_saveaccountconf key value
833_saveaccountconf() {
834 key="$1"
835 value="$2"
8663fb7e 836 if [ "$ACCOUNT_CONF_PATH" ] ; then
4d2f38b0 837 _setopt "$ACCOUNT_CONF_PATH" "$key" "=" "\"$value\""
4c3b3608 838 else
839 _err "ACCOUNT_CONF_PATH is empty, can not save $key=$value"
840 fi
841}
842
843_startserver() {
844 content="$1"
6fc1447f 845 _debug "startserver: $$"
1b2e940d 846 nchelp="$(nc -h 2>&1)"
850c1128 847
399306a1 848 if echo "$nchelp" | grep "\-q[ ,]" >/dev/null ; then
850c1128 849 _NC="nc -q 1 -l"
850 else
f76eb452 851 if echo "$nchelp" | grep "GNU netcat" >/dev/null && echo "$nchelp" | grep "\-c, \-\-close" >/dev/null ; then
852 _NC="nc -c -l"
6d60f288 853 elif echo "$nchelp" | grep "\-N" |grep "Shutdown the network socket after EOF on stdin" >/dev/null ; then
854 _NC="nc -N -l"
f76eb452 855 else
856 _NC="nc -l"
857 fi
4c3b3608 858 fi
1b2e940d 859
f76eb452 860 _debug "_NC" "$_NC"
39c8f79f 861 _debug Le_HTTPPort "$Le_HTTPPort"
4c3b3608 862# while true ; do
8663fb7e 863 if [ "$DEBUG" ] ; then
2c554a4b 864 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort ; then
865 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ;
3aff11f6 866 fi
4c3b3608 867 else
2c554a4b 868 if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then
869 printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1
3aff11f6 870 fi
4c3b3608 871 fi
8663fb7e 872 if [ "$?" != "0" ] ; then
051c706d 873 _err "nc listen error."
6fc1447f 874 exit 1
051c706d 875 fi
4c3b3608 876# done
877}
878
6fc1447f 879_stopserver(){
4c3b3608 880 pid="$1"
6fc1447f 881 _debug "pid" "$pid"
8663fb7e 882 if [ -z "$pid" ] ; then
6fc1447f 883 return
884 fi
e22bcf7c 885
3d434e43 886 _get "http://localhost:$Le_HTTPPort" >/dev/null 2>&1
5aa146a5 887 _get "https://localhost:$Le_TLSPort" >/dev/null 2>&1
4c3b3608 888
889}
890
e22bcf7c 891
892# _starttlsserver san_a san_b port content
893_starttlsserver() {
894 _info "Starting tls server."
895 san_a="$1"
896 san_b="$2"
897 port="$3"
898 content="$4"
899
900 _debug san_a "$san_a"
901 _debug san_b "$san_b"
902 _debug port "$port"
903
904 #create key TLS_KEY
905 if ! _createkey "2048" "$TLS_KEY" ; then
906 _err "Create tls validation key error."
907 return 1
908 fi
909
910 #create csr
911 alt="$san_a"
912 if [ "$san_b" ] ; then
913 alt="$alt,$san_b"
914 fi
915 if ! _createcsr "tls.acme.sh" "$alt" "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" ; then
916 _err "Create tls validation csr error."
917 return 1
918 fi
919
920 #self signed
921 if ! _signcsr "$TLS_KEY" "$TLS_CSR" "$TLS_CONF" "$TLS_CERT" ; then
922 _err "Create tls validation cert error."
923 return 1
924 fi
925
926 #start openssl
fbad6a39 927 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
5aa146a5 928 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | openssl s_server -cert "$TLS_CERT" -key "$TLS_KEY" -accept $port -naccept 1 -tlsextdebug ) &
331c4bb6 929 else
5aa146a5 930 (printf "HTTP/1.1 200 OK\r\n\r\n$content" | openssl s_server -cert "$TLS_CERT" -key "$TLS_KEY" -accept $port -naccept 1 >/dev/null 2>&1) &
331c4bb6 931 fi
932
e22bcf7c 933 serverproc="$!"
934 sleep 2
935 _debug serverproc $serverproc
936}
937
4c3b3608 938_initpath() {
939
8663fb7e 940 if [ -z "$LE_WORKING_DIR" ] ; then
6cc11ffb 941 LE_WORKING_DIR=$HOME/.$PROJECT_NAME
4c3b3608 942 fi
943
d53289d7 944 _DEFAULT_ACCOUNT_CONF_PATH="$LE_WORKING_DIR/account.conf"
945
8663fb7e 946 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
947 if [ -f "$_DEFAULT_ACCOUNT_CONF_PATH" ] ; then
948 . "$_DEFAULT_ACCOUNT_CONF_PATH"
635695ec 949 fi
d53289d7 950 fi
951
8663fb7e 952 if [ -z "$ACCOUNT_CONF_PATH" ] ; then
d53289d7 953 ACCOUNT_CONF_PATH="$_DEFAULT_ACCOUNT_CONF_PATH"
4c3b3608 954 fi
955
8663fb7e 956 if [ -f "$ACCOUNT_CONF_PATH" ] ; then
957 . "$ACCOUNT_CONF_PATH"
4c3b3608 958 fi
959
8663fb7e 960 if [ "$IN_CRON" ] ; then
961 if [ ! "$_USER_PATH_EXPORTED" ] ; then
281aa349 962 _USER_PATH_EXPORTED=1
963 export PATH="$USER_PATH:$PATH"
964 fi
965 fi
966
8663fb7e 967 if [ -z "$API" ] ; then
968 if [ -z "$STAGE" ] ; then
4c3b3608 969 API="$DEFAULT_CA"
970 else
971 API="$STAGE_CA"
972 _info "Using stage api:$API"
973 fi
974 fi
975
8663fb7e 976 if [ -z "$ACME_DIR" ] ; then
4c3b3608 977 ACME_DIR="/home/.acme"
978 fi
979
8663fb7e 980 if [ -z "$APACHE_CONF_BACKUP_DIR" ] ; then
8a144f4d 981 APACHE_CONF_BACKUP_DIR="$LE_WORKING_DIR"
4c3b3608 982 fi
983
8663fb7e 984 if [ -z "$USER_AGENT" ] ; then
bbbdcb09 985 USER_AGENT="$DEFAULT_USER_AGENT"
986 fi
987
988 HTTP_HEADER="$LE_WORKING_DIR/http.header"
989
990 WGET="wget -q"
8663fb7e 991 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
bbbdcb09 992 WGET="$WGET -d "
993 fi
994
87ab2d90 995 _CURL_DUMP="$LE_WORKING_DIR/curl.dump"
4a0f23e2 996 CURL="curl -L --silent"
8663fb7e 997 if [ "$DEBUG" ] && [ "$DEBUG" -ge "2" ] ; then
87ab2d90 998 CURL="$CURL --trace-ascii $_CURL_DUMP "
bbbdcb09 999 fi
13d7cae9 1000
1001 if [ "$Le_Insecure" ] ; then
1002 WGET="$WGET --no-check-certificate "
1003 CURL="$CURL --insecure "
1004 fi
b2817897 1005
1006 _DEFAULT_ACCOUNT_KEY_PATH="$LE_WORKING_DIR/account.key"
8663fb7e 1007 if [ -z "$ACCOUNT_KEY_PATH" ] ; then
b2817897 1008 ACCOUNT_KEY_PATH="$_DEFAULT_ACCOUNT_KEY_PATH"
4c3b3608 1009 fi
b2817897 1010
a79b26af
RD
1011 _DEFAULT_CERT_HOME="$LE_WORKING_DIR"
1012 if [ -z "$CERT_HOME" ] ; then
1013 CERT_HOME="$_DEFAULT_CERT_HOME"
1014 fi
1015
b2817897 1016 domain="$1"
4c3b3608 1017
8663fb7e 1018 if [ -z "$domain" ] ; then
4c3b3608 1019 return 0
1020 fi
1021
b2817897 1022 domainhome="$CERT_HOME/$domain"
4c3b3608 1023 mkdir -p "$domainhome"
1024
8663fb7e 1025 if [ -z "$DOMAIN_PATH" ] ; then
4c3b3608 1026 DOMAIN_PATH="$domainhome"
1027 fi
8663fb7e 1028 if [ -z "$DOMAIN_CONF" ] ; then
1ad65f7d 1029 DOMAIN_CONF="$domainhome/$domain.conf"
4c3b3608 1030 fi
1031
8663fb7e 1032 if [ -z "$DOMAIN_SSL_CONF" ] ; then
1ad65f7d 1033 DOMAIN_SSL_CONF="$domainhome/$domain.ssl.conf"
4c3b3608 1034 fi
1035
8663fb7e 1036 if [ -z "$CSR_PATH" ] ; then
4c3b3608 1037 CSR_PATH="$domainhome/$domain.csr"
1038 fi
8663fb7e 1039 if [ -z "$CERT_KEY_PATH" ] ; then
4c3b3608 1040 CERT_KEY_PATH="$domainhome/$domain.key"
1041 fi
8663fb7e 1042 if [ -z "$CERT_PATH" ] ; then
4c3b3608 1043 CERT_PATH="$domainhome/$domain.cer"
1044 fi
8663fb7e 1045 if [ -z "$CA_CERT_PATH" ] ; then
4c3b3608 1046 CA_CERT_PATH="$domainhome/ca.cer"
1047 fi
8663fb7e 1048 if [ -z "$CERT_FULLCHAIN_PATH" ] ; then
850db6d4 1049 CERT_FULLCHAIN_PATH="$domainhome/fullchain.cer"
caf1fc10 1050 fi
8663fb7e 1051 if [ -z "$CERT_PFX_PATH" ] ; then
ac2d5123 1052 CERT_PFX_PATH="$domainhome/$domain.pfx"
1053 fi
e22bcf7c 1054
1055 if [ -z "$TLS_CONF" ] ; then
1056 TLS_CONF="$domainhome/tls.valdation.conf"
1057 fi
1058 if [ -z "$TLS_CERT" ] ; then
1059 TLS_CERT="$domainhome/tls.valdation.cert"
1060 fi
1061 if [ -z "$TLS_KEY" ] ; then
1062 TLS_KEY="$domainhome/tls.valdation.key"
1063 fi
1064 if [ -z "$TLS_CSR" ] ; then
1065 TLS_CSR="$domainhome/tls.valdation.csr"
1066 fi
1067
4c3b3608 1068}
1069
1070
1071_apachePath() {
e4a19585 1072 _APAHECTL="apachectl"
80a0a7b5 1073 if ! _exists apachectl ; then
e4a19585 1074 if _exists apache2ctl ; then
1075 _APAHECTL="apache2ctl"
1076 else
1077 _err "'apachecrl not found. It seems that apache is not installed, or you are not root user.'"
1078 _err "Please use webroot mode to try again."
1079 return 1
1080 fi
80a0a7b5 1081 fi
e4a19585 1082 httpdconfname="$($_APAHECTL -V | grep SERVER_CONFIG_FILE= | cut -d = -f 2 | tr -d '"' )"
78768e98 1083 _debug httpdconfname "$httpdconfname"
dceb3aca 1084 if _startswith "$httpdconfname" '/' ; then
d62ee940 1085 httpdconf="$httpdconfname"
c456d954 1086 httpdconfname="$(basename $httpdconfname)"
d62ee940 1087 else
e4a19585 1088 httpdroot="$($_APAHECTL -V | grep HTTPD_ROOT= | cut -d = -f 2 | tr -d '"' )"
78768e98 1089 _debug httpdroot "$httpdroot"
d62ee940 1090 httpdconf="$httpdroot/$httpdconfname"
8f63baf7 1091 httpdconfname="$(basename $httpdconfname)"
d62ee940 1092 fi
78768e98 1093 _debug httpdconf "$httpdconf"
8f63baf7 1094 _debug httpdconfname "$httpdconfname"
78768e98 1095 if [ ! -f "$httpdconf" ] ; then
1096 _err "Apache Config file not found" "$httpdconf"
4c3b3608 1097 return 1
1098 fi
1099 return 0
1100}
1101
1102_restoreApache() {
8663fb7e 1103 if [ -z "$usingApache" ] ; then
4c3b3608 1104 return 0
1105 fi
1106 _initpath
1107 if ! _apachePath ; then
1108 return 1
1109 fi
1110
8663fb7e 1111 if [ ! -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname" ] ; then
4c3b3608 1112 _debug "No config file to restore."
1113 return 0
1114 fi
1115
ff3bce32 1116 cat "$APACHE_CONF_BACKUP_DIR/$httpdconfname" > "$httpdconf"
5ef501c5 1117 _debug "Restored: $httpdconf."
e4a19585 1118 if ! $_APAHECTL -t >/dev/null 2>&1 ; then
4c3b3608 1119 _err "Sorry, restore apache config error, please contact me."
1120 return 1;
1121 fi
5ef501c5 1122 _debug "Restored successfully."
4c3b3608 1123 rm -f "$APACHE_CONF_BACKUP_DIR/$httpdconfname"
1124 return 0
1125}
1126
1127_setApache() {
1128 _initpath
1129 if ! _apachePath ; then
1130 return 1
1131 fi
1132
5fc5016d 1133 #test the conf first
869578ce 1134 _info "Checking if there is an error in the apache config file before starting."
e4a19585 1135 _msg="$($_APAHECTL -t 2>&1 )"
5fc5016d 1136 if [ "$?" != "0" ] ; then
869578ce 1137 _err "Sorry, apache config file has error, please fix it first, then try again."
1138 _err "Don't worry, there is nothing changed to your system."
5fc5016d 1139 _err "$_msg"
1140 return 1;
1141 else
1142 _info "OK"
1143 fi
1144
4c3b3608 1145 #backup the conf
5778811a 1146 _debug "Backup apache config file" "$httpdconf"
8f63baf7 1147 if ! cp "$httpdconf" "$APACHE_CONF_BACKUP_DIR/" ; then
869578ce 1148 _err "Can not backup apache config file, so abort. Don't worry, the apache config is not changed."
8f63baf7 1149 _err "This might be a bug of $PROJECT_NAME , pleae report issue: $PROJECT"
1150 return 1
1151 fi
4c3b3608 1152 _info "JFYI, Config file $httpdconf is backuped to $APACHE_CONF_BACKUP_DIR/$httpdconfname"
1153 _info "In case there is an error that can not be restored automatically, you may try restore it yourself."
1154 _info "The backup file will be deleted on sucess, just forget it."
1155
1156 #add alias
b09d597c 1157
e4a19585 1158 apacheVer="$($_APAHECTL -V | grep "Server version:" | cut -d : -f 2 | cut -d " " -f 2 | cut -d '/' -f 2 )"
b09d597c 1159 _debug "apacheVer" "$apacheVer"
1160 apacheMajer="$(echo "$apacheVer" | cut -d . -f 1)"
1161 apacheMinor="$(echo "$apacheVer" | cut -d . -f 2)"
1162
5778811a 1163 if [ "$apacheVer" ] && [ "$apacheMajer$apacheMinor" -ge "24" ] ; then
b09d597c 1164 echo "
4c3b3608 1165Alias /.well-known/acme-challenge $ACME_DIR
1166
1167<Directory $ACME_DIR >
1168Require all granted
b09d597c 1169</Directory>
5778811a 1170 " >> "$httpdconf"
b09d597c 1171 else
1172 echo "
1173Alias /.well-known/acme-challenge $ACME_DIR
1174
1175<Directory $ACME_DIR >
1176Order allow,deny
1177Allow from all
4c3b3608 1178</Directory>
5778811a 1179 " >> "$httpdconf"
b09d597c 1180 fi
1181
e4a19585 1182 _msg="$($_APAHECTL -t 2>&1 )"
5fc5016d 1183 if [ "$?" != "0" ] ; then
1184 _err "Sorry, apache config error"
1185 if _restoreApache ; then
869578ce 1186 _err "The apache config file is restored."
5fc5016d 1187 else
869578ce 1188 _err "Sorry, The apache config file can not be restored, please report bug."
5fc5016d 1189 fi
4c3b3608 1190 return 1;
1191 fi
1192
8663fb7e 1193 if [ ! -d "$ACME_DIR" ] ; then
4c3b3608 1194 mkdir -p "$ACME_DIR"
1195 chmod 755 "$ACME_DIR"
1196 fi
1197
e4a19585 1198 if ! $_APAHECTL graceful ; then
1199 _err "Sorry, $_APAHECTL graceful error, please contact me."
4c3b3608 1200 _restoreApache
1201 return 1;
1202 fi
1203 usingApache="1"
1204 return 0
1205}
1206
5ef501c5 1207_clearup() {
4c3b3608 1208 _stopserver $serverproc
1209 serverproc=""
1210 _restoreApache
e22bcf7c 1211 if [ -z "$DEBUG" ] ; then
1212 rm -f "$TLS_CONF"
1213 rm -f "$TLS_CERT"
1214 rm -f "$TLS_KEY"
1215 rm -f "$TLS_CSR"
1216 fi
4c3b3608 1217}
1218
1219# webroot removelevel tokenfile
1220_clearupwebbroot() {
1221 __webroot="$1"
8663fb7e 1222 if [ -z "$__webroot" ] ; then
4c3b3608 1223 _debug "no webroot specified, skip"
1224 return 0
1225 fi
1226
8663fb7e 1227 if [ "$2" = '1' ] ; then
4c3b3608 1228 _debug "remove $__webroot/.well-known"
1229 rm -rf "$__webroot/.well-known"
8663fb7e 1230 elif [ "$2" = '2' ] ; then
4c3b3608 1231 _debug "remove $__webroot/.well-known/acme-challenge"
1232 rm -rf "$__webroot/.well-known/acme-challenge"
8663fb7e 1233 elif [ "$2" = '3' ] ; then
4c3b3608 1234 _debug "remove $__webroot/.well-known/acme-challenge/$3"
1235 rm -rf "$__webroot/.well-known/acme-challenge/$3"
1236 else
cc179731 1237 _debug "Skip for removelevel:$2"
4c3b3608 1238 fi
1239
1240 return 0
1241
1242}
1243
1244issue() {
8663fb7e 1245 if [ -z "$2" ] ; then
a7b7355d 1246 echo "Usage: $PROJECT_ENTRY --issue -d a.com -w /path/to/webroot/a.com/ "
4c3b3608 1247 return 1
1248 fi
1249 Le_Webroot="$1"
1250 Le_Domain="$2"
1251 Le_Alt="$3"
1252 Le_Keylength="$4"
1253 Le_RealCertPath="$5"
1254 Le_RealKeyPath="$6"
1255 Le_RealCACertPath="$7"
1256 Le_ReloadCmd="$8"
a63b05a9 1257 Le_RealFullChainPath="$9"
4c3b3608 1258
eccec5f6 1259 #remove these later.
8663fb7e 1260 if [ "$Le_Webroot" = "dns-cf" ] ; then
eccec5f6 1261 Le_Webroot="dns_cf"
1262 fi
8663fb7e 1263 if [ "$Le_Webroot" = "dns-dp" ] ; then
eccec5f6 1264 Le_Webroot="dns_dp"
1265 fi
8663fb7e 1266 if [ "$Le_Webroot" = "dns-cx" ] ; then
eccec5f6 1267 Le_Webroot="dns_cx"
1268 fi
4c3b3608 1269
eccec5f6 1270 _initpath $Le_Domain
1271
8663fb7e 1272 if [ -f "$DOMAIN_CONF" ] ; then
a4270efa 1273 Le_NextRenewTime=$(grep "^Le_NextRenewTime=" "$DOMAIN_CONF" | cut -d '=' -f 2 | tr -d "'\"")
1274 _debug Le_NextRenewTime "$Le_NextRenewTime"
1275 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ $(date -u "+%s" ) -lt $Le_NextRenewTime ] ; then
4c3b3608 1276 _info "Skip, Next renewal time is: $(grep "^Le_NextRenewTimeStr" "$DOMAIN_CONF" | cut -d '=' -f 2)"
cc179731 1277 return $RENEW_SKIP
4c3b3608 1278 fi
1279 fi
96a46cfc 1280
4d2f38b0 1281 _savedomainconf "Le_Domain" "$Le_Domain"
1282 _savedomainconf "Le_Alt" "$Le_Alt"
1283 _savedomainconf "Le_Webroot" "$Le_Webroot"
1284 _savedomainconf "Le_Keylength" "$Le_Keylength"
4c3b3608 1285
8663fb7e 1286 if [ "$Le_Alt" = "no" ] ; then
4c3b3608 1287 Le_Alt=""
1288 fi
8663fb7e 1289 if [ "$Le_Keylength" = "no" ] ; then
4c3b3608 1290 Le_Keylength=""
1291 fi
4c3b3608 1292
c53da1ef 1293 if _hasfield "$Le_Webroot" "no" ; then
4c3b3608 1294 _info "Standalone mode."
5ef501c5 1295 if ! _exists "nc" ; then
4c3b3608 1296 _err "Please install netcat(nc) tools first."
1297 return 1
1298 fi
1299
8663fb7e 1300 if [ -z "$Le_HTTPPort" ] ; then
4c3b3608 1301 Le_HTTPPort=80
054cb72e 1302 else
1303 _savedomainconf "Le_HTTPPort" "$Le_HTTPPort"
1304 fi
4c3b3608 1305
251fc37c 1306 netprc="$(_ss "$Le_HTTPPort" | grep "$Le_HTTPPort")"
8663fb7e 1307 if [ "$netprc" ] ; then
4c3b3608 1308 _err "$netprc"
1309 _err "tcp port $Le_HTTPPort is already used by $(echo "$netprc" | cut -d : -f 4)"
1310 _err "Please stop it first"
1311 return 1
1312 fi
1313 fi
1314
e22bcf7c 1315 if _hasfield "$Le_Webroot" "$W_TLS" ; then
1316 _info "Standalone tls mode."
1317
1318 if [ -z "$Le_TLSPort" ] ; then
1319 Le_TLSPort=443
1320 else
1321 _savedomainconf "Le_TLSPort" "$Le_TLSPort"
1322 fi
1323
1324 netprc="$(_ss "$Le_TLSPort" | grep "$Le_TLSPort")"
1325 if [ "$netprc" ] ; then
1326 _err "$netprc"
1327 _err "tcp port $Le_TLSPort is already used by $(echo "$netprc" | cut -d : -f 4)"
1328 _err "Please stop it first"
1329 return 1
1330 fi
1331 fi
1332
c53da1ef 1333 if _hasfield "$Le_Webroot" "apache" ; then
4c3b3608 1334 if ! _setApache ; then
1335 _err "set up apache error. Report error to me."
1336 return 1
1337 fi
4c3b3608 1338 else
1339 usingApache=""
1340 fi
1341
8663fb7e 1342 if [ ! -f "$ACCOUNT_KEY_PATH" ] ; then
41e3eafa 1343 if ! createAccountKey $Le_Domain $Le_Keylength ; then
1344 _err "Create account key error."
8663fb7e 1345 if [ "$usingApache" ] ; then
5ef501c5 1346 _restoreApache
1347 fi
41e3eafa 1348 return 1
1349 fi
1350 fi
1351
166096dc 1352 if ! _calcjwk "$ACCOUNT_KEY_PATH" ; then
8663fb7e 1353 if [ "$usingApache" ] ; then
5ef501c5 1354 _restoreApache
1355 fi
166096dc 1356 return 1
1357 fi
1358
1359 accountkey_json=$(echo -n "$jwk" | tr -d ' ' )
88fab7d6 1360 thumbprint=$(echo -n "$accountkey_json" | _digest "sha256" | _urlencode)
4c3b3608 1361
88fab7d6 1362 accountkeyhash="$(cat "$ACCOUNT_KEY_PATH" | _digest "sha256" )"
a63b05a9 1363 accountkeyhash="$(echo $accountkeyhash$API | _digest "sha256" )"
8663fb7e 1364 if [ "$accountkeyhash" != "$ACCOUNT_KEY_HASH" ] ; then
166096dc 1365 _info "Registering account"
1366 regjson='{"resource": "new-reg", "agreement": "'$AGREEMENT'"}'
8663fb7e 1367 if [ "$ACCOUNT_EMAIL" ] ; then
166096dc 1368 regjson='{"resource": "new-reg", "contact": ["mailto: '$ACCOUNT_EMAIL'"], "agreement": "'$AGREEMENT'"}'
1369 fi
1370 _send_signed_request "$API/acme/new-reg" "$regjson"
1371
8663fb7e 1372 if [ "$code" = "" ] || [ "$code" = '201' ] ; then
166096dc 1373 _info "Registered"
1374 echo $response > $LE_WORKING_DIR/account.json
8663fb7e 1375 elif [ "$code" = '409' ] ; then
166096dc 1376 _info "Already registered"
1377 else
1378 _err "Register account Error: $response"
1379 _clearup
1380 return 1
1381 fi
1382 ACCOUNT_KEY_HASH="$accountkeyhash"
1383 _saveaccountconf "ACCOUNT_KEY_HASH" "$ACCOUNT_KEY_HASH"
1384 else
1385 _info "Skip register account key"
1386 fi
1387
8663fb7e 1388 if [ ! -f "$CERT_KEY_PATH" ] ; then
41e3eafa 1389 if ! createDomainKey $Le_Domain $Le_Keylength ; then
1390 _err "Create domain key error."
5ef501c5 1391 _clearup
41e3eafa 1392 return 1
1393 fi
4c3b3608 1394 fi
1395
1396 if ! createCSR $Le_Domain $Le_Alt ; then
1397 _err "Create CSR error."
5ef501c5 1398 _clearup
4c3b3608 1399 return 1
1400 fi
a63b05a9 1401
4c3b3608 1402 vlist="$Le_Vlist"
1403 # verify each domain
1404 _info "Verify each domain"
1405 sep='#'
8663fb7e 1406 if [ -z "$vlist" ] ; then
4c3b3608 1407 alldomains=$(echo "$Le_Domain,$Le_Alt" | tr ',' ' ' )
a63b05a9 1408 _index=1
1409 _currentRoot=""
4c3b3608 1410 for d in $alldomains
a63b05a9 1411 do
1412 _info "Getting webroot for domain" $d
1413 _w="$(echo $Le_Webroot | cut -d , -f $_index)"
1414 _debug _w "$_w"
8663fb7e 1415 if [ "$_w" ] ; then
a63b05a9 1416 _currentRoot="$_w"
1417 fi
1418 _debug "_currentRoot" "$_currentRoot"
00a50605 1419 _index=$(_math $_index + 1)
a63b05a9 1420
1421 vtype="$VTYPE_HTTP"
dceb3aca 1422 if _startswith "$_currentRoot" "dns" ; then
a63b05a9 1423 vtype="$VTYPE_DNS"
1424 fi
e22bcf7c 1425
1426 if [ "$_currentRoot" = "$W_TLS" ] ; then
1427 vtype="$VTYPE_TLS"
1428 fi
1429
4c3b3608 1430 _info "Getting token for domain" $d
c4d8fd83 1431
1432 if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" ; then
1433 _err "Can not get domain token."
1434 _clearup
1435 return 1
1436 fi
1437
8663fb7e 1438 if [ ! -z "$code" ] && [ ! "$code" = '201' ] ; then
4c3b3608 1439 _err "new-authz error: $response"
1440 _clearup
1441 return 1
1442 fi
1443
230234e7 1444 entry="$(printf "$response" | egrep -o '\{[^{]*"type":"'$vtype'"[^}]*')"
4c3b3608 1445 _debug entry "$entry"
19539575 1446 if [ -z "$entry" ] ; then
1447 _err "Error, can not get domain token $d"
1448 _clearup
1449 return 1
1450 fi
4c3b3608 1451 token="$(printf "$entry" | egrep -o '"token":"[^"]*' | cut -d : -f 2 | tr -d '"')"
1452 _debug token $token
1453
1454 uri="$(printf "$entry" | egrep -o '"uri":"[^"]*'| cut -d : -f 2,3 | tr -d '"' )"
1455 _debug uri $uri
1456
1457 keyauthorization="$token.$thumbprint"
1458 _debug keyauthorization "$keyauthorization"
1459
a63b05a9 1460 dvlist="$d$sep$keyauthorization$sep$uri$sep$vtype$sep$_currentRoot"
4c3b3608 1461 _debug dvlist "$dvlist"
1462
1463 vlist="$vlist$dvlist,"
1464
1465 done
1466
1467 #add entry
1468 dnsadded=""
1469 ventries=$(echo "$vlist" | tr ',' ' ' )
1470 for ventry in $ventries
1471 do
1472 d=$(echo $ventry | cut -d $sep -f 1)
1473 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
a63b05a9 1474 vtype=$(echo $ventry | cut -d $sep -f 4)
1475 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
8663fb7e 1476 if [ "$vtype" = "$VTYPE_DNS" ] ; then
4c3b3608 1477 dnsadded='0'
1478 txtdomain="_acme-challenge.$d"
1479 _debug txtdomain "$txtdomain"
c1c7d87b 1480 txt="$(echo -n $keyauthorization | _digest "sha256" | _urlencode)"
4c3b3608 1481 _debug txt "$txt"
1482 #dns
1483 #1. check use api
1484 d_api=""
8663fb7e 1485 if [ -f "$LE_WORKING_DIR/$d/$_currentRoot" ] ; then
a63b05a9 1486 d_api="$LE_WORKING_DIR/$d/$_currentRoot"
8663fb7e 1487 elif [ -f "$LE_WORKING_DIR/$d/$_currentRoot.sh" ] ; then
a63b05a9 1488 d_api="$LE_WORKING_DIR/$d/$_currentRoot.sh"
8663fb7e 1489 elif [ -f "$LE_WORKING_DIR/$_currentRoot" ] ; then
a63b05a9 1490 d_api="$LE_WORKING_DIR/$_currentRoot"
8663fb7e 1491 elif [ -f "$LE_WORKING_DIR/$_currentRoot.sh" ] ; then
a63b05a9 1492 d_api="$LE_WORKING_DIR/$_currentRoot.sh"
8663fb7e 1493 elif [ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot" ] ; then
a63b05a9 1494 d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot"
8663fb7e 1495 elif [ -f "$LE_WORKING_DIR/dnsapi/$_currentRoot.sh" ] ; then
a63b05a9 1496 d_api="$LE_WORKING_DIR/dnsapi/$_currentRoot.sh"
4c3b3608 1497 fi
1498 _debug d_api "$d_api"
1499
8663fb7e 1500 if [ "$d_api" ] ; then
4c3b3608 1501 _info "Found domain api file: $d_api"
1502 else
1503 _err "Add the following TXT record:"
1504 _err "Domain: $txtdomain"
1505 _err "TXT value: $txt"
1506 _err "Please be aware that you prepend _acme-challenge. before your domain"
1507 _err "so the resulting subdomain will be: $txtdomain"
1508 continue
1509 fi
4c3b3608 1510
73b8b120 1511 (
8663fb7e 1512 if ! . $d_api ; then
73b8b120 1513 _err "Load file $d_api error. Please check your api file and try again."
1514 return 1
1515 fi
1516
158f22f7 1517 addcommand="${_currentRoot}_add"
d53289d7 1518 if ! _exists $addcommand ; then
73b8b120 1519 _err "It seems that your api file is not correct, it must have a function named: $addcommand"
1520 return 1
1521 fi
1522
1523 if ! $addcommand $txtdomain $txt ; then
1524 _err "Error add txt for domain:$txtdomain"
1525 return 1
1526 fi
1527 )
4c3b3608 1528
8663fb7e 1529 if [ "$?" != "0" ] ; then
5ef501c5 1530 _clearup
4c3b3608 1531 return 1
1532 fi
1533 dnsadded='1'
1534 fi
1535 done
1536
8663fb7e 1537 if [ "$dnsadded" = '0' ] ; then
4d2f38b0 1538 _savedomainconf "Le_Vlist" "$vlist"
4c3b3608 1539 _debug "Dns record not added yet, so, save to $DOMAIN_CONF and exit."
1540 _err "Please add the TXT records to the domains, and retry again."
5ef501c5 1541 _clearup
4c3b3608 1542 return 1
1543 fi
1544
1545 fi
1546
8663fb7e 1547 if [ "$dnsadded" = '1' ] ; then
0e38c60d 1548 if [ -z "$Le_DNSSleep" ] ; then
1549 Le_DNSSleep=60
1550 else
1551 _savedomainconf "Le_DNSSleep" "$Le_DNSSleep"
1552 fi
1553
1554 _info "Sleep $Le_DNSSleep seconds for the txt records to take effect"
1555 sleep $Le_DNSSleep
4c3b3608 1556 fi
1557
1558 _debug "ok, let's start to verify"
a63b05a9 1559
4c3b3608 1560 ventries=$(echo "$vlist" | tr ',' ' ' )
1561 for ventry in $ventries
1562 do
1563 d=$(echo $ventry | cut -d $sep -f 1)
1564 keyauthorization=$(echo $ventry | cut -d $sep -f 2)
1565 uri=$(echo $ventry | cut -d $sep -f 3)
a63b05a9 1566 vtype=$(echo $ventry | cut -d $sep -f 4)
1567 _currentRoot=$(echo $ventry | cut -d $sep -f 5)
4c3b3608 1568 _info "Verifying:$d"
1569 _debug "d" "$d"
1570 _debug "keyauthorization" "$keyauthorization"
1571 _debug "uri" "$uri"
1572 removelevel=""
e22bcf7c 1573 token="$(printf "%s" "$keyauthorization" | cut -d '.' -f 1)"
a63b05a9 1574
1575 _debug "_currentRoot" "$_currentRoot"
1576
1577
8663fb7e 1578 if [ "$vtype" = "$VTYPE_HTTP" ] ; then
1579 if [ "$_currentRoot" = "no" ] ; then
4c3b3608 1580 _info "Standalone mode server"
1581 _startserver "$keyauthorization" &
8663fb7e 1582 if [ "$?" != "0" ] ; then
5ef501c5 1583 _clearup
6fc1447f 1584 return 1
1585 fi
4c3b3608 1586 serverproc="$!"
1587 sleep 2
1588 _debug serverproc $serverproc
6fc1447f 1589
4c3b3608 1590 else
8663fb7e 1591 if [ "$_currentRoot" = "apache" ] ; then
6f930641 1592 wellknown_path="$ACME_DIR"
1593 else
a63b05a9 1594 wellknown_path="$_currentRoot/.well-known/acme-challenge"
8663fb7e 1595 if [ ! -d "$_currentRoot/.well-known" ] ; then
6f930641 1596 removelevel='1'
8663fb7e 1597 elif [ ! -d "$_currentRoot/.well-known/acme-challenge" ] ; then
6f930641 1598 removelevel='2'
1599 else
1600 removelevel='3'
1601 fi
4c3b3608 1602 fi
6f930641 1603
4c3b3608 1604 _debug wellknown_path "$wellknown_path"
6f930641 1605
4c3b3608 1606 _debug "writing token:$token to $wellknown_path/$token"
1607
1608 mkdir -p "$wellknown_path"
7939b419 1609 printf "%s" "$keyauthorization" > "$wellknown_path/$token"
8663fb7e 1610 if [ ! "$usingApache" ] ; then
a63b05a9 1611 webroot_owner=$(_stat $_currentRoot)
df886ffa 1612 _debug "Changing owner/group of .well-known to $webroot_owner"
a63b05a9 1613 chown -R $webroot_owner "$_currentRoot/.well-known"
df886ffa 1614 fi
4c3b3608 1615
1616 fi
e22bcf7c 1617
1618 elif [ "$vtype" = "$VTYPE_TLS" ] ; then
1619 #create A
1620 #_hash_A="$(printf "%s" $token | _digest "sha256" "hex" )"
1621 #_debug2 _hash_A "$_hash_A"
1622 #_x="$(echo $_hash_A | cut -c 1-32)"
1623 #_debug2 _x "$_x"
1624 #_y="$(echo $_hash_A | cut -c 33-64)"
1625 #_debug2 _y "$_y"
1626 #_SAN_A="$_x.$_y.token.acme.invalid"
1627 #_debug2 _SAN_A "$_SAN_A"
1628
1629 #create B
1630 _hash_B="$(printf "%s" $keyauthorization | _digest "sha256" "hex" )"
1631 _debug2 _hash_B "$_hash_B"
1632 _x="$(echo $_hash_B | cut -c 1-32)"
1633 _debug2 _x "$_x"
1634 _y="$(echo $_hash_B | cut -c 33-64)"
1635 _debug2 _y "$_y"
1636
1637 #_SAN_B="$_x.$_y.ka.acme.invalid"
1638
1639 _SAN_B="$_x.$_y.acme.invalid"
1640 _debug2 _SAN_B "$_SAN_B"
1641
1642 if ! _starttlsserver "$_SAN_B" "$_SAN_A" "$Le_TLSPort" "$keyauthorization" ; then
1643 _err "Start tls server error."
1644 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1645 _clearup
1646 return 1
1647 fi
4c3b3608 1648 fi
1649
c4d8fd83 1650 if ! _send_signed_request $uri "{\"resource\": \"challenge\", \"keyAuthorization\": \"$keyauthorization\"}" ; then
1651 _err "$d:Can not get challenge: $response"
1652 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1653 _clearup
1654 return 1
1655 fi
4c3b3608 1656
8663fb7e 1657 if [ ! -z "$code" ] && [ ! "$code" = '202' ] ; then
c60883ef 1658 _err "$d:Challenge error: $response"
a63b05a9 1659 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1660 _clearup
1661 return 1
1662 fi
1663
6fc1447f 1664 waittimes=0
8663fb7e 1665 if [ -z "$MAX_RETRY_TIMES" ] ; then
6fc1447f 1666 MAX_RETRY_TIMES=30
1667 fi
1668
2ee5d873 1669 while true ; do
00a50605 1670 waittimes=$(_math $waittimes + 1)
8663fb7e 1671 if [ "$waittimes" -ge "$MAX_RETRY_TIMES" ] ; then
6fc1447f 1672 _err "$d:Timeout"
1673 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
1674 _clearup
1675 return 1
1676 fi
1677
4c3b3608 1678 _debug "sleep 5 secs to verify"
1679 sleep 5
1680 _debug "checking"
9aaf36cd 1681 response="$(_get $uri)"
8663fb7e 1682 if [ "$?" != "0" ] ; then
c60883ef 1683 _err "$d:Verify error:$response"
a63b05a9 1684 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1685 _clearup
1686 return 1
1687 fi
9aaf36cd 1688 _debug2 original "$response"
1689
1690 response="$(echo "$response" | _normalizeJson )"
7012b91f 1691 _debug2 response "$response"
4c3b3608 1692
7d91e35f 1693 status=$(echo $response | egrep -o '"status":"[^"]*' | cut -d : -f 2 | tr -d '"')
8663fb7e 1694 if [ "$status" = "valid" ] ; then
4c3b3608 1695 _info "Success"
1696 _stopserver $serverproc
1697 serverproc=""
a63b05a9 1698 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1699 break;
1700 fi
1701
8663fb7e 1702 if [ "$status" = "invalid" ] ; then
6e180343 1703 error="$(echo $response | tr -d "\r\n" | egrep -o '"error":{[^}]*}')"
b7ec6789 1704 _debug2 error "$error"
1705 errordetail="$(echo $error | grep -o '"detail": *"[^"]*"' | cut -d '"' -f 4)"
1706 _debug2 errordetail "$errordetail"
1707 if [ "$errordetail" ] ; then
1708 _err "$d:Verify error:$errordetail"
1709 else
1710 _err "$d:Verify error:$error"
1711 fi
a63b05a9 1712 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1713 _clearup
1714 return 1;
1715 fi
1716
8663fb7e 1717 if [ "$status" = "pending" ] ; then
4c3b3608 1718 _info "Pending"
1719 else
1720 _err "$d:Verify error:$response"
a63b05a9 1721 _clearupwebbroot "$_currentRoot" "$removelevel" "$token"
4c3b3608 1722 _clearup
1723 return 1
1724 fi
1725
1726 done
1727
1728 done
1729
1730 _clearup
1731 _info "Verify finished, start to sign."
fa8311dc 1732 der="$(_getfile "${CSR_PATH}" "${BEGIN_CSR}" "${END_CSR}" | tr -d "\r\n" | _urlencode)"
c4d8fd83 1733
1734 if ! _send_signed_request "$API/acme/new-cert" "{\"resource\": \"new-cert\", \"csr\": \"$der\"}" "needbase64" ; then
1735 _err "Sign failed."
1736 return 1
1737 fi
4c3b3608 1738
1739
bbbdcb09 1740 Le_LinkCert="$(grep -i -o '^Location.*$' $HTTP_HEADER | head -1 | tr -d "\r\n" | cut -d " " -f 2)"
4d2f38b0 1741 _savedomainconf "Le_LinkCert" "$Le_LinkCert"
4c3b3608 1742
8663fb7e 1743 if [ "$Le_LinkCert" ] ; then
88fab7d6 1744 echo "$BEGIN_CERT" > "$CERT_PATH"
c60883ef 1745 _get "$Le_LinkCert" | _base64 "multiline" >> "$CERT_PATH"
88fab7d6 1746 echo "$END_CERT" >> "$CERT_PATH"
4c3b3608 1747 _info "Cert success."
1748 cat "$CERT_PATH"
1749
1750 _info "Your cert is in $CERT_PATH"
caf1fc10 1751 cp "$CERT_PATH" "$CERT_FULLCHAIN_PATH"
281aa349 1752
8663fb7e 1753 if [ ! "$USER_PATH" ] || [ ! "$IN_CRON" ] ; then
281aa349 1754 USER_PATH="$PATH"
1755 _saveaccountconf "USER_PATH" "$USER_PATH"
1756 fi
4c3b3608 1757 fi
1758
1759
8663fb7e 1760 if [ -z "$Le_LinkCert" ] ; then
eae29099 1761 response="$(echo $response | _dbase64 "multiline" | _normalizeJson )"
4c3b3608 1762 _err "Sign failed: $(echo "$response" | grep -o '"detail":"[^"]*"')"
1763 return 1
1764 fi
1765
4d2f38b0 1766 _cleardomainconf "Le_Vlist"
4c3b3608 1767
bbbdcb09 1768 Le_LinkIssuer=$(grep -i '^Link' $HTTP_HEADER | head -1 | cut -d " " -f 2| cut -d ';' -f 1 | tr -d '<>' )
4d2f38b0 1769 _savedomainconf "Le_LinkIssuer" "$Le_LinkIssuer"
4c3b3608 1770
8663fb7e 1771 if [ "$Le_LinkIssuer" ] ; then
88fab7d6 1772 echo "$BEGIN_CERT" > "$CA_CERT_PATH"
c60883ef 1773 _get "$Le_LinkIssuer" | _base64 "multiline" >> "$CA_CERT_PATH"
88fab7d6 1774 echo "$END_CERT" >> "$CA_CERT_PATH"
4c3b3608 1775 _info "The intermediate CA cert is in $CA_CERT_PATH"
caf1fc10 1776 cat "$CA_CERT_PATH" >> "$CERT_FULLCHAIN_PATH"
1777 _info "And the full chain certs is there: $CERT_FULLCHAIN_PATH"
4c3b3608 1778 fi
1779
1780 Le_CertCreateTime=$(date -u "+%s")
4d2f38b0 1781 _savedomainconf "Le_CertCreateTime" "$Le_CertCreateTime"
4c3b3608 1782
1783 Le_CertCreateTimeStr=$(date -u )
4d2f38b0 1784 _savedomainconf "Le_CertCreateTimeStr" "$Le_CertCreateTimeStr"
4c3b3608 1785
523c7682 1786 if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ] || [ "$Le_RenewalDays" -gt "$MAX_RENEW" ] ; then
1787 Le_RenewalDays=$MAX_RENEW
054cb72e 1788 else
1789 _savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
13d7cae9 1790 fi
1791
1792 if [ "$Le_Insecure" ] ; then
1793 _savedomainconf "Le_Insecure" "$Le_Insecure"
1794 fi
00a50605 1795
1796 Le_NextRenewTime=$(_math $Le_CertCreateTime + $Le_RenewalDays \* 24 \* 60 \* 60)
4d2f38b0 1797 _savedomainconf "Le_NextRenewTime" "$Le_NextRenewTime"
4c3b3608 1798
1799 Le_NextRenewTimeStr=$( _time2str $Le_NextRenewTime )
4d2f38b0 1800 _savedomainconf "Le_NextRenewTimeStr" "$Le_NextRenewTimeStr"
4c3b3608 1801
1802
01f54558 1803 _output="$(installcert $Le_Domain "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath" 2>&1)"
1804 _ret="$?"
1805 if [ "$_ret" = "9" ] ; then
ca2a96b3 1806 #ignore the empty install error.
1807 return 0
1808 fi
01f54558 1809 if [ "$_ret" != "0" ] ; then
1810 _err "$_output"
1811 return 1
1812 fi
4c3b3608 1813}
1814
1815renew() {
1816 Le_Domain="$1"
8663fb7e 1817 if [ -z "$Le_Domain" ] ; then
a7b7355d 1818 _err "Usage: $PROJECT_ENTRY --renew -d domain.com"
4c3b3608 1819 return 1
1820 fi
1821
1822 _initpath $Le_Domain
5aa146a5 1823 _info "Renew: $Le_Domain"
8663fb7e 1824 if [ ! -f "$DOMAIN_CONF" ] ; then
4c3b3608 1825 _info "$Le_Domain is not a issued domain, skip."
1826 return 0;
1827 fi
1828
8663fb7e 1829 . "$DOMAIN_CONF"
1830 if [ -z "$FORCE" ] && [ "$Le_NextRenewTime" ] && [ "$(date -u "+%s" )" -lt "$Le_NextRenewTime" ] ; then
4c3b3608 1831 _info "Skip, Next renewal time is: $Le_NextRenewTimeStr"
cc179731 1832 return $RENEW_SKIP
4c3b3608 1833 fi
1834
1835 IS_RENEW="1"
a63b05a9 1836 issue "$Le_Webroot" "$Le_Domain" "$Le_Alt" "$Le_Keylength" "$Le_RealCertPath" "$Le_RealKeyPath" "$Le_RealCACertPath" "$Le_ReloadCmd" "$Le_RealFullChainPath"
4c3b3608 1837 local res=$?
1838 IS_RENEW=""
1839
1840 return $res
1841}
1842
cc179731 1843#renewAll [stopRenewOnError]
4c3b3608 1844renewAll() {
1845 _initpath
cc179731 1846 _stopRenewOnError="$1"
1847 _debug "_stopRenewOnError" "$_stopRenewOnError"
1848 _ret="0"
b2817897 1849 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
4c3b3608 1850 d=$(echo $d | cut -d '/' -f 1)
5aa146a5 1851 (
4d2f38b0 1852 renew "$d"
1853 )
cc179731 1854 rc="$?"
1855 _debug "Return code: $rc"
1856 if [ "$rc" != "0" ] ; then
1857 if [ "$rc" = "$RENEW_SKIP" ] ; then
1858 _info "Skipped $d"
1859 elif [ "$_stopRenewOnError" ] ; then
1860 _err "Error renew $d, stop now."
1861 return $rc
1862 else
1863 _ret="$rc"
1864 _err "Error renew $d, Go ahead to next one."
1865 fi
1866 fi
4c3b3608 1867 done
cc179731 1868 return $_ret
4c3b3608 1869}
1870
dcf4f8f6 1871
6d7eda3e 1872list() {
dcf4f8f6 1873 local _raw="$1"
6d7eda3e 1874 _initpath
dcf4f8f6 1875
1876 _sep="|"
1877 if [ "$_raw" ] ; then
1878 printf "Main_Domain${_sep}SAN_Domains${_sep}Created${_sep}Renew\n"
1879 for d in $(ls -F ${CERT_HOME}/ | grep [^.].*[.].*/$ ) ; do
1880 d=$(echo $d | cut -d '/' -f 1)
1881 (
1882 _initpath $d
1883 if [ -f "$DOMAIN_CONF" ] ; then
1884 . "$DOMAIN_CONF"
1885 printf "$Le_Domain${_sep}$Le_Alt${_sep}$Le_CertCreateTimeStr${_sep}$Le_NextRenewTimeStr\n"
1886 fi
1887 )
1888 done
1889 else
1890 list "raw" | column -t -s "$_sep"
1891 fi
6d7eda3e 1892
1893
1894}
1895
4c3b3608 1896installcert() {
1897 Le_Domain="$1"
8663fb7e 1898 if [ -z "$Le_Domain" ] ; then
a7b7355d 1899 echo "Usage: $PROJECT_ENTRY --installcert -d domain.com [--certpath cert-file-path] [--keypath key-file-path] [--capath ca-cert-file-path] [ --reloadCmd reloadCmd] [--fullchainpath fullchain-path]"
4c3b3608 1900 return 1
1901 fi
1902
1903 Le_RealCertPath="$2"
1904 Le_RealKeyPath="$3"
1905 Le_RealCACertPath="$4"
1906 Le_ReloadCmd="$5"
a63b05a9 1907 Le_RealFullChainPath="$6"
4c3b3608 1908
1909 _initpath $Le_Domain
1910
4d2f38b0 1911 _savedomainconf "Le_RealCertPath" "$Le_RealCertPath"
1912 _savedomainconf "Le_RealCACertPath" "$Le_RealCACertPath"
1913 _savedomainconf "Le_RealKeyPath" "$Le_RealKeyPath"
1914 _savedomainconf "Le_ReloadCmd" "$Le_ReloadCmd"
1915 _savedomainconf "Le_RealFullChainPath" "$Le_RealFullChainPath"
4c3b3608 1916
4d2f38b0 1917 if [ "$Le_RealCertPath" = "no" ] ; then
1918 Le_RealCertPath=""
1919 fi
1920 if [ "$Le_RealKeyPath" = "no" ] ; then
1921 Le_RealKeyPath=""
1922 fi
1923 if [ "$Le_RealCACertPath" = "no" ] ; then
1924 Le_RealCACertPath=""
1925 fi
1926 if [ "$Le_ReloadCmd" = "no" ] ; then
1927 Le_ReloadCmd=""
1928 fi
1929 if [ "$Le_RealFullChainPath" = "no" ] ; then
1930 Le_RealFullChainPath=""
1931 fi
1932
1933 _installed="0"
8663fb7e 1934 if [ "$Le_RealCertPath" ] ; then
4d2f38b0 1935 _installed=1
1936 _info "Installing cert to:$Le_RealCertPath"
8663fb7e 1937 if [ -f "$Le_RealCertPath" ] ; then
ff3bce32 1938 cp "$Le_RealCertPath" "$Le_RealCertPath".bak
4c3b3608 1939 fi
1940 cat "$CERT_PATH" > "$Le_RealCertPath"
1941 fi
1942
8663fb7e 1943 if [ "$Le_RealCACertPath" ] ; then
4d2f38b0 1944 _installed=1
1945 _info "Installing CA to:$Le_RealCACertPath"
8663fb7e 1946 if [ "$Le_RealCACertPath" = "$Le_RealCertPath" ] ; then
4c3b3608 1947 echo "" >> "$Le_RealCACertPath"
1948 cat "$CA_CERT_PATH" >> "$Le_RealCACertPath"
1949 else
8663fb7e 1950 if [ -f "$Le_RealCACertPath" ] ; then
ff3bce32 1951 cp "$Le_RealCACertPath" "$Le_RealCACertPath".bak
78552b18 1952 fi
4c3b3608 1953 cat "$CA_CERT_PATH" > "$Le_RealCACertPath"
1954 fi
1955 fi
1956
1957
8663fb7e 1958 if [ "$Le_RealKeyPath" ] ; then
4d2f38b0 1959 _installed=1
1960 _info "Installing key to:$Le_RealKeyPath"
8663fb7e 1961 if [ -f "$Le_RealKeyPath" ] ; then
ff3bce32 1962 cp "$Le_RealKeyPath" "$Le_RealKeyPath".bak
4c3b3608 1963 fi
1964 cat "$CERT_KEY_PATH" > "$Le_RealKeyPath"
1965 fi
a63b05a9 1966
8663fb7e 1967 if [ "$Le_RealFullChainPath" ] ; then
4d2f38b0 1968 _installed=1
1969 _info "Installing full chain to:$Le_RealFullChainPath"
8663fb7e 1970 if [ -f "$Le_RealFullChainPath" ] ; then
ff3bce32 1971 cp "$Le_RealFullChainPath" "$Le_RealFullChainPath".bak
a63b05a9 1972 fi
1973 cat "$CERT_FULLCHAIN_PATH" > "$Le_RealFullChainPath"
1974 fi
4c3b3608 1975
8663fb7e 1976 if [ "$Le_ReloadCmd" ] ; then
4d2f38b0 1977 _installed=1
4c3b3608 1978 _info "Run Le_ReloadCmd: $Le_ReloadCmd"
4d2f38b0 1979 if (cd "$DOMAIN_PATH" && eval "$Le_ReloadCmd") ; then
1980 _info "Reload success."
1981 else
1982 _err "Reload error for :$Le_Domain"
1983 fi
1984 fi
1985
1986 if [ "$_installed" = "0" ] ; then
1987 _err "Nothing to install. You don't specify any parameter."
ca2a96b3 1988 return 9
4c3b3608 1989 fi
1990
1991}
1992
1993installcronjob() {
1994 _initpath
77546ea5 1995 if ! _exists "crontab" ; then
1996 _err "crontab doesn't exist, so, we can not install cron jobs."
1997 _err "All your certs will not be renewed automatically."
a7b7355d 1998 _err "You must add your own cron job to call '$PROJECT_ENTRY --cron' everyday."
77546ea5 1999 return 1
2000 fi
2001
4c3b3608 2002 _info "Installing cron job"
a7b7355d 2003 if ! crontab -l | grep "$PROJECT_ENTRY --cron" ; then
8663fb7e 2004 if [ -f "$LE_WORKING_DIR/$PROJECT_ENTRY" ] ; then
a7b7355d 2005 lesh="\"$LE_WORKING_DIR\"/$PROJECT_ENTRY"
4c3b3608 2006 else
a7b7355d 2007 _err "Can not install cronjob, $PROJECT_ENTRY not found."
4c3b3608 2008 return 1
2009 fi
a7b7355d 2010 crontab -l | { cat; echo "0 0 * * * $lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"; } | crontab -
4c3b3608 2011 fi
8663fb7e 2012 if [ "$?" != "0" ] ; then
4c3b3608 2013 _err "Install cron job failed. You need to manually renew your certs."
2014 _err "Or you can add cronjob by yourself:"
a7b7355d 2015 _err "$lesh --cron --home \"$LE_WORKING_DIR\" > /dev/null"
4c3b3608 2016 return 1
2017 fi
2018}
2019
2020uninstallcronjob() {
37db5b81 2021 if ! _exists "crontab" ; then
2022 return
2023 fi
4c3b3608 2024 _info "Removing cron job"
a7b7355d 2025 cr="$(crontab -l | grep "$PROJECT_ENTRY --cron")"
8663fb7e 2026 if [ "$cr" ] ; then
a7b7355d 2027 crontab -l | sed "/$PROJECT_ENTRY --cron/d" | crontab -
2028 LE_WORKING_DIR="$(echo "$cr" | cut -d ' ' -f 9 | tr -d '"')"
4c3b3608 2029 _info LE_WORKING_DIR "$LE_WORKING_DIR"
2030 fi
2031 _initpath
a7b7355d 2032
4c3b3608 2033}
2034
6cb415f5 2035revoke() {
2036 Le_Domain="$1"
8663fb7e 2037 if [ -z "$Le_Domain" ] ; then
a7b7355d 2038 echo "Usage: $PROJECT_ENTRY --revoke -d domain.com"
6cb415f5 2039 return 1
2040 fi
2041
2042 _initpath $Le_Domain
8663fb7e 2043 if [ ! -f "$DOMAIN_CONF" ] ; then
6cb415f5 2044 _err "$Le_Domain is not a issued domain, skip."
2045 return 1;
2046 fi
2047
8663fb7e 2048 if [ ! -f "$CERT_PATH" ] ; then
6cb415f5 2049 _err "Cert for $Le_Domain $CERT_PATH is not found, skip."
2050 return 1
2051 fi
2052
2053 cert="$(_getfile "${CERT_PATH}" "${BEGIN_CERT}" "${END_CERT}"| tr -d "\r\n" | _urlencode)"
2054
8663fb7e 2055 if [ -z "$cert" ] ; then
6cb415f5 2056 _err "Cert for $Le_Domain is empty found, skip."
2057 return 1
2058 fi
2059
2060 data="{\"resource\": \"revoke-cert\", \"certificate\": \"$cert\"}"
2061 uri="$API/acme/revoke-cert"
2062
2063 _info "Try domain key first."
2064 if _send_signed_request $uri "$data" "" "$CERT_KEY_PATH"; then
8663fb7e 2065 if [ -z "$response" ] ; then
6cb415f5 2066 _info "Revoke success."
2067 rm -f $CERT_PATH
2068 return 0
2069 else
2070 _err "Revoke error by domain key."
2071 _err "$resource"
2072 fi
2073 fi
2074
2075 _info "Then try account key."
2076
2077 if _send_signed_request $uri "$data" "" "$ACCOUNT_KEY_PATH" ; then
8663fb7e 2078 if [ -z "$response" ] ; then
6cb415f5 2079 _info "Revoke success."
2080 rm -f $CERT_PATH
2081 return 0
2082 else
2083 _err "Revoke error."
2084 _debug "$resource"
2085 fi
2086 fi
2087 return 1
2088}
4c3b3608 2089
2090# Detect profile file if not specified as environment variable
2091_detect_profile() {
a63b05a9 2092 if [ -n "$PROFILE" -a -f "$PROFILE" ] ; then
4c3b3608 2093 echo "$PROFILE"
2094 return
2095 fi
2096
2097 local DETECTED_PROFILE
2098 DETECTED_PROFILE=''
2099 local SHELLTYPE
2100 SHELLTYPE="$(basename "/$SHELL")"
2101
8663fb7e 2102 if [ "$SHELLTYPE" = "bash" ] ; then
2103 if [ -f "$HOME/.bashrc" ] ; then
4c3b3608 2104 DETECTED_PROFILE="$HOME/.bashrc"
8663fb7e 2105 elif [ -f "$HOME/.bash_profile" ] ; then
4c3b3608 2106 DETECTED_PROFILE="$HOME/.bash_profile"
2107 fi
8663fb7e 2108 elif [ "$SHELLTYPE" = "zsh" ] ; then
4c3b3608 2109 DETECTED_PROFILE="$HOME/.zshrc"
2110 fi
2111
8663fb7e 2112 if [ -z "$DETECTED_PROFILE" ] ; then
2113 if [ -f "$HOME/.profile" ] ; then
4c3b3608 2114 DETECTED_PROFILE="$HOME/.profile"
8663fb7e 2115 elif [ -f "$HOME/.bashrc" ] ; then
4c3b3608 2116 DETECTED_PROFILE="$HOME/.bashrc"
8663fb7e 2117 elif [ -f "$HOME/.bash_profile" ] ; then
4c3b3608 2118 DETECTED_PROFILE="$HOME/.bash_profile"
8663fb7e 2119 elif [ -f "$HOME/.zshrc" ] ; then
4c3b3608 2120 DETECTED_PROFILE="$HOME/.zshrc"
2121 fi
2122 fi
2123
8663fb7e 2124 if [ ! -z "$DETECTED_PROFILE" ] ; then
4c3b3608 2125 echo "$DETECTED_PROFILE"
2126 fi
2127}
2128
2129_initconf() {
2130 _initpath
8663fb7e 2131 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
d53289d7 2132 echo "#ACCOUNT_CONF_PATH=xxxx
2133
2134#Account configurations:
4c3b3608 2135#Here are the supported macros, uncomment them to make them take effect.
d53289d7 2136
4c3b3608 2137#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account.
5fd3f21b 2138#ACCOUNT_KEY_PATH=\"/path/to/account.key\"
b2817897 2139#CERT_HOME=\"/path/to/cert/home\"
4c3b3608 2140
2141#STAGE=1 # Use the staging api
2142#FORCE=1 # Force to issue cert
2143#DEBUG=1 # Debug mode
2144
166096dc 2145#ACCOUNT_KEY_HASH=account key hash
2146
8814a348 2147#USER_AGENT=\"$USER_AGENT\"
281aa349 2148
2149#USER_PATH=""
2150
4c3b3608 2151#dns api
2152#######################
2153#Cloudflare:
2154#api key
3d49985a 2155#CF_Key=\"sdfsdfsdfljlbjkljlkjsdfoiwje\"
4c3b3608 2156#account email
3d49985a 2157#CF_Email=\"xxxx@sss.com\"
4c3b3608 2158
2159#######################
2160#Dnspod.cn:
2161#api key id
3d49985a 2162#DP_Id=\"1234\"
4c3b3608 2163#api key
3d49985a 2164#DP_Key=\"sADDsdasdgdsf\"
4c3b3608 2165
2166#######################
2167#Cloudxns.com:
3d49985a 2168#CX_Key=\"1234\"
4c3b3608 2169#
3d49985a 2170#CX_Secret=\"sADDsdasdgdsf\"
4c3b3608 2171
2172 " > $ACCOUNT_CONF_PATH
2173 fi
2174}
2175
c8e9a31e 2176# nocron
c60883ef 2177_precheck() {
c8e9a31e 2178 _nocron="$1"
2179
c60883ef 2180 if ! _exists "curl" && ! _exists "wget"; then
2181 _err "Please install curl or wget first, we need to access http resources."
4c3b3608 2182 return 1
2183 fi
2184
c8e9a31e 2185 if [ -z "$_nocron" ] ; then
2186 if ! _exists "crontab" ; then
2187 _err "It is recommended to install crontab first. try to install 'cron, crontab, crontabs or vixie-cron'."
2188 _err "We need to set cron job to renew the certs automatically."
2189 _err "Otherwise, your certs will not be able to be renewed automatically."
2190 if [ -z "$FORCE" ] ; then
2191 _err "Please add '--force' and try install again to go without crontab."
2192 _err "./$PROJECT_ENTRY --install --force"
2193 return 1
2194 fi
77546ea5 2195 fi
4c3b3608 2196 fi
2197
c60883ef 2198 if ! _exists "openssl" ; then
2199 _err "Please install openssl first."
2200 _err "We need openssl to generate keys."
4c3b3608 2201 return 1
2202 fi
2203
c60883ef 2204 if ! _exists "nc" ; then
2205 _err "It is recommended to install nc first, try to install 'nc' or 'netcat'."
2206 _err "We use nc for standalone server if you use standalone mode."
2207 _err "If you don't use standalone mode, just ignore this warning."
2208 fi
2209
2210 return 0
2211}
2212
0a7c9364 2213_setShebang() {
2214 _file="$1"
2215 _shebang="$2"
2216 if [ -z "$_shebang" ] ; then
2217 _err "Usage: file shebang"
2218 return 1
2219 fi
2220 cp "$_file" "$_file.tmp"
2221 echo "$_shebang" > "$_file"
2222 sed -n 2,99999p "$_file.tmp" >> "$_file"
2223 rm -f "$_file.tmp"
2224}
2225
94dc5f33 2226_installalias() {
2227 _initpath
2228
2229 _envfile="$LE_WORKING_DIR/$PROJECT_ENTRY.env"
2230 if [ "$_upgrading" ] && [ "$_upgrading" = "1" ] ; then
2231 echo "$(cat $_envfile)" | sed "s|^LE_WORKING_DIR.*$||" > "$_envfile"
2232 echo "$(cat $_envfile)" | sed "s|^alias le.*$||" > "$_envfile"
2233 echo "$(cat $_envfile)" | sed "s|^alias le.sh.*$||" > "$_envfile"
2234 fi
2235
1786a5e5 2236 _setopt "$_envfile" "export LE_WORKING_DIR" "=" "\"$LE_WORKING_DIR\""
94dc5f33 2237 _setopt "$_envfile" "alias $PROJECT_ENTRY" "=" "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
2238
2239 _profile="$(_detect_profile)"
2240 if [ "$_profile" ] ; then
2241 _debug "Found profile: $_profile"
2242 _setopt "$_profile" ". \"$_envfile\""
2243 _info "OK, Close and reopen your terminal to start using $PROJECT_NAME"
2244 else
2245 _info "No profile is found, you will need to go into $LE_WORKING_DIR to use $PROJECT_NAME"
2246 fi
2247
2248
2249 #for csh
2250 _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh"
94dc5f33 2251 _csh_profile="$HOME/.cshrc"
2252 if [ -f "$_csh_profile" ] ; then
6626371d 2253 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
2254 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
94dc5f33 2255 _setopt "$_csh_profile" "source \"$_cshfile\""
2256 fi
acafa585 2257
2258 #for tcsh
2259 _tcsh_profile="$HOME/.tcshrc"
2260 if [ -f "$_tcsh_profile" ] ; then
2261 _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\""
2262 _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\""
2263 _setopt "$_tcsh_profile" "source \"$_cshfile\""
2264 fi
94dc5f33 2265
2266}
2267
c8e9a31e 2268# nocron
c60883ef 2269install() {
c8e9a31e 2270 _nocron="$1"
c60883ef 2271 if ! _initpath ; then
2272 _err "Install failed."
4c3b3608 2273 return 1
2274 fi
52677b0a 2275 if [ "$_nocron" ] ; then
2276 _debug "Skip install cron job"
2277 fi
2278
c8e9a31e 2279 if ! _precheck "$_nocron" ; then
c60883ef 2280 _err "Pre-check failed, can not install."
4c3b3608 2281 return 1
2282 fi
c60883ef 2283
6cc11ffb 2284 #convert from le
8663fb7e 2285 if [ -d "$HOME/.le" ] ; then
6cc11ffb 2286 for envfile in "le.env" "le.sh.env"
2287 do
8663fb7e 2288 if [ -f "$HOME/.le/$envfile" ] ; then
6cc11ffb 2289 if grep "le.sh" "$HOME/.le/$envfile" >/dev/null ; then
2290 _upgrading="1"
2291 _info "You are upgrading from le.sh"
2292 _info "Renaming \"$HOME/.le\" to $LE_WORKING_DIR"
2293 mv "$HOME/.le" "$LE_WORKING_DIR"
2294 mv "$LE_WORKING_DIR/$envfile" "$LE_WORKING_DIR/$PROJECT_ENTRY.env"
2295 break;
2296 fi
2297 fi
2298 done
2299 fi
2300
4c3b3608 2301 _info "Installing to $LE_WORKING_DIR"
635695ec 2302
4a0f23e2 2303 if ! mkdir -p "$LE_WORKING_DIR" ; then
90035252 2304 _err "Can not create working dir: $LE_WORKING_DIR"
4a0f23e2 2305 return 1
2306 fi
2307
762978f8 2308 chmod 700 "$LE_WORKING_DIR"
2309
a7b7355d 2310 cp $PROJECT_ENTRY "$LE_WORKING_DIR/" && chmod +x "$LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 2311
8663fb7e 2312 if [ "$?" != "0" ] ; then
a7b7355d 2313 _err "Install failed, can not copy $PROJECT_ENTRY"
4c3b3608 2314 return 1
2315 fi
2316
a7b7355d 2317 _info "Installed to $LE_WORKING_DIR/$PROJECT_ENTRY"
4c3b3608 2318
94dc5f33 2319 _installalias
4c3b3608 2320
8663fb7e 2321 if [ -d "dnsapi" ] ; then
6ed1c718 2322 mkdir -p $LE_WORKING_DIR/dnsapi
2323 cp dnsapi/* $LE_WORKING_DIR/dnsapi/
2324 fi
d53289d7 2325
8663fb7e 2326 if [ ! -f "$ACCOUNT_CONF_PATH" ] ; then
4c3b3608 2327 _initconf
2328 fi
6cc11ffb 2329
8663fb7e 2330 if [ "$_DEFAULT_ACCOUNT_CONF_PATH" != "$ACCOUNT_CONF_PATH" ] ; then
635695ec 2331 _setopt "$_DEFAULT_ACCOUNT_CONF_PATH" "ACCOUNT_CONF_PATH" "=" "\"$ACCOUNT_CONF_PATH\""
6cc11ffb 2332 fi
2333
8663fb7e 2334 if [ "$_DEFAULT_CERT_HOME" != "$CERT_HOME" ] ; then
b2817897 2335 _saveaccountconf "CERT_HOME" "$CERT_HOME"
2336 fi
2337
8663fb7e 2338 if [ "$_DEFAULT_ACCOUNT_KEY_PATH" != "$ACCOUNT_KEY_PATH" ] ; then
b2817897 2339 _saveaccountconf "ACCOUNT_KEY_PATH" "$ACCOUNT_KEY_PATH"
2340 fi
2341
c8e9a31e 2342 if [ -z "$_nocron" ] ; then
2343 installcronjob
2344 fi
0a7c9364 2345
641989fd 2346 if [ -z "$NO_DETECT_SH" ] ; then
2347 #Modify shebang
2348 if _exists bash ; then
2349 _info "Good, bash is installed, change the shebang to use bash as prefered."
2350 _shebang='#!/usr/bin/env bash'
2351 _setShebang "$LE_WORKING_DIR/$PROJECT_ENTRY" "$_shebang"
2352 if [ -d "$LE_WORKING_DIR/dnsapi" ] ; then
2353 for _apifile in $(ls "$LE_WORKING_DIR/dnsapi/"*.sh) ; do
2354 _setShebang "$_apifile" "$_shebang"
2355 done
2356 fi
0a7c9364 2357 fi
2358 fi
2359
4c3b3608 2360 _info OK
2361}
2362
52677b0a 2363# nocron
4c3b3608 2364uninstall() {
52677b0a 2365 _nocron="$1"
2366 if [ -z "$_nocron" ] ; then
2367 uninstallcronjob
2368 fi
4c3b3608 2369 _initpath
2370
2371 _profile="$(_detect_profile)"
8663fb7e 2372 if [ "$_profile" ] ; then
7203a1c1 2373 text="$(cat $_profile)"
94dc5f33 2374 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" > "$_profile"
4c3b3608 2375 fi
2376
94dc5f33 2377 _csh_profile="$HOME/.cshrc"
2378 if [ -f "$_csh_profile" ] ; then
2379 text="$(cat $_csh_profile)"
2380 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_csh_profile"
2381 fi
2382
acafa585 2383 _tcsh_profile="$HOME/.tcshrc"
2384 if [ -f "$_tcsh_profile" ] ; then
2385 text="$(cat $_tcsh_profile)"
2386 echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_tcsh_profile"
2387 fi
2388
a7b7355d 2389 rm -f $LE_WORKING_DIR/$PROJECT_ENTRY
4c3b3608 2390 _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself."
2391
2392}
2393
2394cron() {
281aa349 2395 IN_CRON=1
4c3b3608 2396 renewAll
cc179731 2397 _ret="$?"
281aa349 2398 IN_CRON=""
cc179731 2399 return $_ret
4c3b3608 2400}
2401
2402version() {
a63b05a9 2403 echo "$PROJECT"
2404 echo "v$VER"
4c3b3608 2405}
2406
2407showhelp() {
2408 version
a7b7355d 2409 echo "Usage: $PROJECT_ENTRY command ...[parameters]....
a63b05a9 2410Commands:
2411 --help, -h Show this help message.
2412 --version, -v Show version info.
a7b7355d 2413 --install Install $PROJECT_NAME to your system.
2414 --uninstall Uninstall $PROJECT_NAME, and uninstall the cron job.
52677b0a 2415 --upgrade Upgrade $PROJECT_NAME to the latest code from $PROJECT
a63b05a9 2416 --issue Issue a cert.
2417 --installcert Install the issued cert to apache/nginx or any other server.
2418 --renew, -r Renew a cert.
2419 --renewAll Renew all the certs
2420 --revoke Revoke a cert.
6d7eda3e 2421 --list List all the certs
a63b05a9 2422 --installcronjob Install the cron job to renew certs, you don't need to call this. The 'install' command can automatically install the cron job.
2423 --uninstallcronjob Uninstall the cron job. The 'uninstall' command can do this automatically.
2424 --cron Run cron job to renew all the certs.
2425 --toPkcs Export the certificate and key to a pfx file.
2426 --createAccountKey, -cak Create an account private key, professional use.
2427 --createDomainKey, -cdk Create an domain private key, professional use.
2428 --createCSR, -ccsr Create CSR , professional use.
2429
2430Parameters:
2431 --domain, -d domain.tld Specifies a domain, used to issue, renew or revoke etc.
2432 --force, -f Used to force to install or force to renew a cert immediately.
2433 --staging, --test Use staging server, just for test.
2434 --debug Output debug info.
2435
2436 --webroot, -w /path/to/webroot Specifies the web root folder for web root mode.
2437 --standalone Use standalone mode.
e22bcf7c 2438 --tls Use standalone tls mode.
a63b05a9 2439 --apache Use apache mode.
eccec5f6 2440 --dns [dns_cf|dns_dp|dns_cx|/path/to/api/file] Use dns mode or dns api.
0e38c60d 2441 --dnssleep [60] The time in seconds to wait for all the txt records to take effect in dns api mode. Default 60 seconds.
a63b05a9 2442
2443 --keylength, -k [2048] Specifies the domain key length: 2048, 3072, 4096, 8192 or ec-256, ec-384.
2444 --accountkeylength, -ak [2048] Specifies the account key length.
2445
2446 These parameters are to install the cert to nginx/apache or anyother server after issue/renew a cert:
2447
2448 --certpath /path/to/real/cert/file After issue/renew, the cert will be copied to this path.
2449 --keypath /path/to/real/key/file After issue/renew, the key will be copied to this path.
2450 --capath /path/to/real/ca/file After issue/renew, the intermediate cert will be copied to this path.
2451 --fullchainpath /path/to/fullchain/file After issue/renew, the fullchain cert will be copied to this path.
2452
2453 --reloadcmd \"service nginx reload\" After issue/renew, it's used to reload the server.
2454
2455 --accountconf Specifies a customized account config file.
635695ec 2456 --home Specifies the home dir for $PROJECT_NAME .
39c8f79f 2457 --certhome Specifies the home dir to save all the certs, only valid for '--install' command.
635695ec 2458 --useragent Specifies the user agent string. it will be saved for future use too.
b5eb4b90 2459 --accountemail Specifies the account email for registering, Only valid for the '--install' command.
06625071 2460 --accountkey Specifies the account key path, Only valid for the '--install' command.
523c7682 2461 --days Specifies the days to renew the cert when using '--issue' command. The max value is $MAX_RENEW days.
39c8f79f 2462 --httpport Specifies the standalone listening port. Only valid if the server is behind a reverse proxy or load balancer.
e22bcf7c 2463 --tlsport Specifies the standalone tls listening port. Only valid if the server is behind a reverse proxy or load balancer.
dcf4f8f6 2464 --listraw Only used for '--list' command, list the certs in raw format.
c8e9a31e 2465 --stopRenewOnError, -se Only valid for '--renewall' command. Stop if one cert has error in renewal.
13d7cae9 2466 --insecure Do not check the server certificate, in some devices, the api server's certificate may not be trusted.
c8e9a31e 2467 --nocron Only valid for `--install` command, which means: do not install the default cron job. In this case, the certs will not be renewed automatically.
4c3b3608 2468 "
2469}
2470
52677b0a 2471# nocron
4a0f23e2 2472_installOnline() {
2473 _info "Installing from online archive."
52677b0a 2474 _nocron="$1"
8663fb7e 2475 if [ ! "$BRANCH" ] ; then
4a0f23e2 2476 BRANCH="master"
2477 fi
2478 _initpath
2479 target="$PROJECT/archive/$BRANCH.tar.gz"
2480 _info "Downloading $target"
2481 localname="$BRANCH.tar.gz"
2482 if ! _get "$target" > $localname ; then
2483 _debug "Download error."
2484 return 1
2485 fi
2486 _info "Extracting $localname"
2487 tar xzf $localname
6cc11ffb 2488 cd "$PROJECT_NAME-$BRANCH"
a7b7355d 2489 chmod +x $PROJECT_ENTRY
52677b0a 2490 if ./$PROJECT_ENTRY install "$_nocron" ; then
4a0f23e2 2491 _info "Install success!"
2492 fi
2493
2494 cd ..
6cc11ffb 2495 rm -rf "$PROJECT_NAME-$BRANCH"
4a0f23e2 2496 rm -f "$localname"
2497}
2498
52677b0a 2499upgrade() {
2500 if (
2501 cd $LE_WORKING_DIR
2502 _installOnline "nocron"
2503 ) ; then
2504 _info "Upgrade success!"
2505 else
2506 _err "Upgrade failed!"
2507 fi
2508}
a63b05a9 2509
2510_process() {
2511 _CMD=""
2512 _domain=""
2513 _altdomains="no"
2514 _webroot=""
2515 _keylength="no"
2516 _accountkeylength="no"
2517 _certpath="no"
2518 _keypath="no"
2519 _capath="no"
2520 _fullchainpath="no"
4d2f38b0 2521 _reloadcmd=""
a63b05a9 2522 _password=""
635695ec 2523 _accountconf=""
2524 _useragent=""
b5eb4b90 2525 _accountemail=""
2526 _accountkey=""
b2817897 2527 _certhome=""
39c8f79f 2528 _httpport=""
e22bcf7c 2529 _tlsport=""
0e38c60d 2530 _dnssleep=""
dcf4f8f6 2531 _listraw=""
cc179731 2532 _stopRenewOnError=""
13d7cae9 2533 _insecure=""
c8e9a31e 2534 _nocron=""
8663fb7e 2535 while [ ${#} -gt 0 ] ; do
a63b05a9 2536 case "${1}" in
2537
2538 --help|-h)
2539 showhelp
2540 return
2541 ;;
2542 --version|-v)
2543 version
2544 return
2545 ;;
2546 --install)
2547 _CMD="install"
2548 ;;
2549 --uninstall)
2550 _CMD="uninstall"
2551 ;;
52677b0a 2552 --upgrade)
2553 _CMD="upgrade"
2554 ;;
a63b05a9 2555 --issue)
2556 _CMD="issue"
2557 ;;
2558 --installcert|-i)
2559 _CMD="installcert"
2560 ;;
2561 --renew|-r)
2562 _CMD="renew"
2563 ;;
4d2f38b0 2564 --renewAll|--renewall)
a63b05a9 2565 _CMD="renewAll"
2566 ;;
2567 --revoke)
2568 _CMD="revoke"
2569 ;;
6d7eda3e 2570 --list)
2571 _CMD="list"
2572 ;;
a63b05a9 2573 --installcronjob)
2574 _CMD="installcronjob"
2575 ;;
2576 --uninstallcronjob)
2577 _CMD="uninstallcronjob"
2578 ;;
2579 --cron)
2580 _CMD="cron"
2581 ;;
2582 --toPkcs)
2583 _CMD="toPkcs"
2584 ;;
2585 --createAccountKey|--createaccountkey|-cak)
2586 _CMD="createAccountKey"
2587 ;;
2588 --createDomainKey|--createdomainkey|-cdk)
2589 _CMD="createDomainKey"
2590 ;;
2591 --createCSR|--createcsr|-ccr)
2592 _CMD="createCSR"
2593 ;;
2594
2595
2596 --domain|-d)
2597 _dvalue="$2"
2598
ee1737a5 2599 if [ "$_dvalue" ] ; then
2600 if _startswith "$_dvalue" "-" ; then
2601 _err "'$_dvalue' is not a valid domain for parameter '$1'"
2602 return 1
2603 fi
2604
2605 if [ -z "$_domain" ] ; then
2606 _domain="$_dvalue"
a63b05a9 2607 else
ee1737a5 2608 if [ "$_altdomains" = "no" ] ; then
2609 _altdomains="$_dvalue"
2610 else
2611 _altdomains="$_altdomains,$_dvalue"
2612 fi
a63b05a9 2613 fi
2614 fi
ee1737a5 2615
a63b05a9 2616 shift
2617 ;;
2618
2619 --force|-f)
2620 FORCE="1"
2621 ;;
2622 --staging|--test)
2623 STAGE="1"
2624 ;;
2625 --debug)
8663fb7e 2626 if [ -z "$2" ] || _startswith "$2" "-" ; then
a63b05a9 2627 DEBUG="1"
2628 else
2629 DEBUG="$2"
2630 shift
6fc1447f 2631 fi
a63b05a9 2632 ;;
a63b05a9 2633 --webroot|-w)
2634 wvalue="$2"
8663fb7e 2635 if [ -z "$_webroot" ] ; then
a63b05a9 2636 _webroot="$wvalue"
2637 else
2638 _webroot="$_webroot,$wvalue"
2639 fi
2640 shift
2641 ;;
2642 --standalone)
2643 wvalue="no"
8663fb7e 2644 if [ -z "$_webroot" ] ; then
a63b05a9 2645 _webroot="$wvalue"
2646 else
2647 _webroot="$_webroot,$wvalue"
2648 fi
2649 ;;
2650 --apache)
2651 wvalue="apache"
8663fb7e 2652 if [ -z "$_webroot" ] ; then
a63b05a9 2653 _webroot="$wvalue"
2654 else
2655 _webroot="$_webroot,$wvalue"
2656 fi
2657 ;;
e22bcf7c 2658 --tls)
2659 wvalue="$W_TLS"
2660 if [ -z "$_webroot" ] ; then
2661 _webroot="$wvalue"
2662 else
2663 _webroot="$_webroot,$wvalue"
2664 fi
2665 ;;
a63b05a9 2666 --dns)
2667 wvalue="dns"
dceb3aca 2668 if ! _startswith "$2" "-" ; then
a63b05a9 2669 wvalue="$2"
2670 shift
2671 fi
8663fb7e 2672 if [ -z "$_webroot" ] ; then
a63b05a9 2673 _webroot="$wvalue"
2674 else
2675 _webroot="$_webroot,$wvalue"
2676 fi
2677 ;;
0e38c60d 2678 --dnssleep)
2679 _dnssleep="$2"
2680 Le_DNSSleep="$_dnssleep"
2681 shift
2682 ;;
2683
a63b05a9 2684 --keylength|-k)
2685 _keylength="$2"
2ce87fe2 2686 if [ "$_accountkeylength" = "no" ] ; then
2687 _accountkeylength="$2"
2688 fi
a63b05a9 2689 shift
2690 ;;
2691 --accountkeylength|-ak)
2ce87fe2 2692 _accountkeylength="$2"
a63b05a9 2693 shift
2694 ;;
2695
2696 --certpath)
2697 _certpath="$2"
2698 shift
2699 ;;
2700 --keypath)
2701 _keypath="$2"
2702 shift
2703 ;;
2704 --capath)
2705 _capath="$2"
2706 shift
2707 ;;
2708 --fullchainpath)
2709 _fullchainpath="$2"
2710 shift
2711 ;;
635695ec 2712 --reloadcmd|--reloadCmd)
a63b05a9 2713 _reloadcmd="$2"
2714 shift
2715 ;;
2716 --password)
2717 _password="$2"
2718 shift
2719 ;;
2720 --accountconf)
635695ec 2721 _accountconf="$2"
2722 ACCOUNT_CONF_PATH="$_accountconf"
a7b7355d 2723 shift
a63b05a9 2724 ;;
a7b7355d 2725 --home)
a63b05a9 2726 LE_WORKING_DIR="$2"
a7b7355d 2727 shift
a63b05a9 2728 ;;
b2817897 2729 --certhome)
2730 _certhome="$2"
2731 CERT_HOME="$_certhome"
2732 shift
2733 ;;
635695ec 2734 --useragent)
2735 _useragent="$2"
2736 USER_AGENT="$_useragent"
2737 shift
2738 ;;
b5eb4b90 2739 --accountemail )
2740 _accountemail="$2"
2741 ACCOUNT_EMAIL="$_accountemail"
2742 shift
2743 ;;
2744 --accountkey )
2745 _accountkey="$2"
2746 ACCOUNT_KEY_PATH="$_accountkey"
2747 shift
2748 ;;
06625071 2749 --days )
2750 _days="$2"
2751 Le_RenewalDays="$_days"
2752 shift
2753 ;;
39c8f79f 2754 --httpport )
2755 _httpport="$2"
2756 Le_HTTPPort="$_httpport"
2757 shift
2758 ;;
e22bcf7c 2759 --tlsport )
2760 _tlsport="$2"
2761 Le_TLSPort="$_tlsport"
2762 shift
2763 ;;
2764
dcf4f8f6 2765 --listraw )
2766 _listraw="raw"
2767 ;;
cc179731 2768 --stopRenewOnError|--stoprenewonerror|-se )
2769 _stopRenewOnError="1"
2770 ;;
13d7cae9 2771 --insecure)
2772 _insecure="1"
2773 Le_Insecure="$_insecure"
2774 ;;
c8e9a31e 2775 --nocron)
2776 _nocron="1"
2777 ;;
a63b05a9 2778 *)
2779 _err "Unknown parameter : $1"
2780 return 1
2781 ;;
2782 esac
2783
2784 shift 1
2785 done
2786
2787
2788 case "${_CMD}" in
c8e9a31e 2789 install) install "$_nocron" ;;
a63b05a9 2790 uninstall) uninstall ;;
52677b0a 2791 upgrade) upgrade ;;
a63b05a9 2792 issue)
70a55875 2793 issue "$_webroot" "$_domain" "$_altdomains" "$_keylength" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath"
a63b05a9 2794 ;;
2795 installcert)
3ed4102a 2796 installcert "$_domain" "$_certpath" "$_keypath" "$_capath" "$_reloadcmd" "$_fullchainpath"
a63b05a9 2797 ;;
2798 renew)
2799 renew "$_domain"
2800 ;;
2801 renewAll)
cc179731 2802 renewAll "$_stopRenewOnError"
a63b05a9 2803 ;;
2804 revoke)
2805 revoke "$_domain"
2806 ;;
6d7eda3e 2807 list)
dcf4f8f6 2808 list "$_listraw"
6d7eda3e 2809 ;;
a63b05a9 2810 installcronjob) installcronjob ;;
2811 uninstallcronjob) uninstallcronjob ;;
2812 cron) cron ;;
2813 toPkcs)
2814 toPkcs "$_domain" "$_password"
2815 ;;
2816 createAccountKey)
2817 createAccountKey "$_domain" "$_accountkeylength"
2818 ;;
2819 createDomainKey)
2820 createDomainKey "$_domain" "$_keylength"
2821 ;;
2822 createCSR)
2823 createCSR "$_domain" "$_altdomains"
2824 ;;
2825
2826 *)
2827 _err "Invalid command: $_CMD"
2828 showhelp;
2829 return 1
2830 ;;
2831 esac
d3595686 2832 _ret="$?"
2833 if [ "$_ret" != "0" ] ; then
2834 return $_ret
2835 fi
a63b05a9 2836
8663fb7e 2837 if [ "$_useragent" ] ; then
635695ec 2838 _saveaccountconf "USER_AGENT" "$_useragent"
2839 fi
8663fb7e 2840 if [ "$_accountemail" ] ; then
b5eb4b90 2841 _saveaccountconf "ACCOUNT_EMAIL" "$_accountemail"
2842 fi
b2817897 2843
635695ec 2844
a63b05a9 2845}
2846
2847
8663fb7e 2848if [ "$INSTALLONLINE" ] ; then
d1f97fc8 2849 INSTALLONLINE=""
4a0f23e2 2850 _installOnline $BRANCH
2851 exit
2852fi
4c3b3608 2853
8663fb7e 2854if [ -z "$1" ] ; then
4c3b3608 2855 showhelp
2856else
036e9d10 2857 if echo "$1" | grep "^-" >/dev/null 2>&1 ; then
a63b05a9 2858 _process "$@"
2859 else
2860 "$@"
2861 fi
4c3b3608 2862fi
a63b05a9 2863
2864