From 661f05837c803ff4bd078e1f051816b102b56a99 Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 12 Oct 2016 21:48:18 +0800 Subject: [PATCH 01/10] minor --- acme.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index 9ff54d32..b56d7698 100755 --- a/acme.sh +++ b/acme.sh @@ -3965,13 +3965,15 @@ _process() { if [ "${_CMD}" != "install" ] ; then __initHome - if [ "$_log" ] && [ -z "$_logfile" ] ; then - _logfile="$DEFAULT_LOG_FILE" + if [ "$_log" ]; then + if [ -z "$_logfile" ] ; then + _logfile="$DEFAULT_LOG_FILE" + fi fi if [ "$_logfile" ] ; then _saveaccountconf "LOG_FILE" "$_logfile" + LOG_FILE="$_logfile" fi - LOG_FILE="$_logfile" if [ "$_log_level" ] ; then _saveaccountconf "LOG_LEVEL" "$_log_level" From d9130c985242c89a2aee0a3b9807da335da08c1d Mon Sep 17 00:00:00 2001 From: neilpang Date: Wed, 12 Oct 2016 22:38:28 +0800 Subject: [PATCH 02/10] minor --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index b56d7698..16e8d32c 100755 --- a/acme.sh +++ b/acme.sh @@ -93,7 +93,7 @@ _printargs() { _log() { [ -z "$LOG_FILE" ] && return - _printargs "$@" >> "$LOG_FILE" + _printargs "$@" >> $LOG_FILE } _info() { From caa2e45a8c163e1758fdb00cf15ccd8c1f65b594 Mon Sep 17 00:00:00 2001 From: Peter Lyons Date: Wed, 12 Oct 2016 20:14:36 -0600 Subject: [PATCH 03/10] use RFC2606 example.com domain in docs (#327) https://www.rfc-editor.org/rfc/rfc2606.txt --- README.md | 36 ++++++++++++++++++------------------ acme.sh | 2 +- dnsapi/README.md | 12 ++++++------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 609de73c..e86392ec 100644 --- a/README.md +++ b/README.md @@ -112,23 +112,23 @@ root@v1:~# acme.sh -h **Example 1:** Single domain. ```bash -acme.sh --issue -d aa.com -w /home/wwwroot/aa.com +acme.sh --issue -d example.com -w /home/wwwroot/example.com ``` **Example 2:** Multiple domains in the same cert. ```bash -acme.sh --issue -d aa.com -d www.aa.com -d cp.aa.com -w /home/wwwroot/aa.com +acme.sh --issue -d example.com -d www.example.com -d cp.example.com -w /home/wwwroot/example.com ``` -The parameter `/home/wwwroot/aa.com` is the web root folder. You **MUST** have `write access` to this folder. +The parameter `/home/wwwroot/example.com` is the web root folder. You **MUST** have `write access` to this folder. -Second argument **"aa.com"** is the main domain you want to issue cert for. +Second argument **"example.com"** is the main domain you want to issue cert for. You must have at least a domain there. -You must point and bind all the domains to the same webroot dir: `/home/wwwroot/aa.com`. +You must point and bind all the domains to the same webroot dir: `/home/wwwroot/example.com`. -Generate/issued certs will be placed in `~/.acme.sh/aa.com/` +Generate/issued certs will be placed in `~/.acme.sh/example.com/` The issued cert will be renewed every **60** days automatically. @@ -140,7 +140,7 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert After you issue a cert, you probably want to install/copy the cert to your nginx/apache or other servers you may be using. ```bash -acme.sh --installcert -d aa.com \ +acme.sh --installcert -d example.com \ --certpath /path/to/certfile/in/apache/nginx \ --keypath /path/to/keyfile/in/apache/nginx \ --capath /path/to/ca/certfile/apache/nginx \ @@ -161,7 +161,7 @@ The cert will be `renewed every **60** days by default` (which is configurable). The tcp `80` port **MUST** be free to listen, otherwise you will be prompted to free the `80` port and try again. ```bash -acme.sh --issue --standalone -d aa.com -d www.aa.com -d cp.aa.com +acme.sh --issue --standalone -d example.com -d www.example.com -d cp.example.com ``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert @@ -175,7 +175,7 @@ acme.sh supports `tls-sni-01` validation. The tcp `443` port **MUST** be free to listen, otherwise you will be prompted to free the `443` port and try again. ```bash -acme.sh --issue --tls -d aa.com -d www.aa.com -d cp.aa.com +acme.sh --issue --tls -d example.com -d www.example.com -d cp.example.com ``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert @@ -191,7 +191,7 @@ Particularly, if you are running an apache server, you should use apache mode in Just set string "apache" as the second argument, it will force use of apache plugin automatically. ``` -acme.sh --issue --apache -d aa.com -d www.aa.com -d user.aa.com +acme.sh --issue --apache -d example.com -d www.example.com -d user.example.com ``` More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert @@ -201,18 +201,18 @@ More examples: https://github.com/Neilpang/acme.sh/wiki/How-to-issue-a-cert Support the `dns-01` challenge. ```bash -acme.sh --issue --dns -d aa.com -d www.aa.com -d user.aa.com +acme.sh --issue --dns -d example.com -d www.example.com -d user.example.com ``` You should get the output like below: ``` Add the following txt record: -Domain:_acme-challenge.aa.com +Domain:_acme-challenge.example.com Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c Add the following txt record: -Domain:_acme-challenge.www.aa.com +Domain:_acme-challenge.www.example.com Txt value:9ihDbjxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Please add those txt records to the domains. Waiting for the dns to take effect. @@ -222,7 +222,7 @@ Please add those txt records to the domains. Waiting for the dns to take effect. Then just rerun with `renew` argument: ```bash -acme.sh --renew -d aa.com +acme.sh --renew -d example.com ``` Ok, it's finished. @@ -264,13 +264,13 @@ For example: ### Single domain ECC cerfiticate: ```bash -acme.sh --issue -w /home/wwwroot/aa.com -d aa.com --keylength ec-256 +acme.sh --issue -w /home/wwwroot/example.com -d example.com --keylength ec-256 ``` SAN multi domain ECC certificate: ```bash -acme.sh --issue -w /home/wwwroot/aa.com -d aa.com -d www.aa.com --keylength ec-256 +acme.sh --issue -w /home/wwwroot/example.com -d example.com -d www.example.com --keylength ec-256 ``` Please look at the last parameter above. @@ -289,12 +289,12 @@ No, you don't need to renew the certs manually. All the certs will be renewed a However, you can also force to renew any cert: ``` -acme.sh --renew -d aa.com --force +acme.sh --renew -d example.com --force ``` or, for ECC cert: ``` -acme.sh --renew -d aa.com --force --ecc +acme.sh --renew -d example.com --force --ecc ``` # 11. How to upgrade `acme.sh` diff --git a/acme.sh b/acme.sh index 16e8d32c..09920730 100755 --- a/acme.sh +++ b/acme.sh @@ -3130,7 +3130,7 @@ _initconf() { #Account configurations: #Here are the supported macros, uncomment them to make them take effect. -#ACCOUNT_EMAIL=aaa@aaa.com # the account email used to register account. +#ACCOUNT_EMAIL=aaa@example.com # the account email used to register account. #ACCOUNT_KEY_PATH=\"/path/to/account.key\" #CERT_HOME=\"/path/to/cert/home\" diff --git a/dnsapi/README.md b/dnsapi/README.md index e17406e3..94603154 100644 --- a/dnsapi/README.md +++ b/dnsapi/README.md @@ -15,7 +15,7 @@ export CF_Email="xxxx@sss.com" Ok, let's issue cert now: ``` -acme.sh --issue --dns dns_cf -d aa.com -d www.aa.com +acme.sh --issue --dns dns_cf -d example.com -d www.example.com ``` The `CF_Key` and `CF_Email` will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. @@ -37,7 +37,7 @@ export DP_Key="sADDsdasdgdsf" Ok, let's issue cert now: ``` -acme.sh --issue --dns dns_dp -d aa.com -d www.aa.com +acme.sh --issue --dns dns_dp -d example.com -d www.example.com ``` The `DP_Id` and `DP_Key` will be saved in `~/.acme.sh/account.conf`, when next time you use dnspod.cn api, it will reuse this key. @@ -58,7 +58,7 @@ export CX_Secret="sADDsdasdgdsf" Ok, let's issue cert now: ``` -acme.sh --issue --dns dns_cx -d aa.com -d www.aa.com +acme.sh --issue --dns dns_cx -d example.com -d www.example.com ``` The `CX_Key` and `CX_Secret` will be saved in `~/.acme.sh/account.conf`, when next time you use Cloudxns.com api, it will reuse this key. @@ -84,7 +84,7 @@ export GD_Secret="asdfsdafdsfdsfdsfdsfdsafd" Ok, let's issue cert now: ``` -acme.sh --issue --dns dns_gd -d aa.com -d www.aa.com +acme.sh --issue --dns dns_gd -d example.com -d www.example.com ``` The `GD_Key` and `GD_Secret` will be saved in `~/.acme.sh/account.conf`, when next time you use cloudflare api, it will reuse this key. @@ -107,7 +107,7 @@ export PDNS_Ttl=60 Ok, let's issue cert now: ``` -acme.sh --issue --dns dns_pdns -d aa.com -d www.aa.com +acme.sh --issue --dns dns_pdns -d example.com -d www.example.com ``` The `PDNS_Url`, `PDNS_ServerId`, `PDNS_Token` and `PDNS_Ttl` will be saved in `~/.acme.sh/account.conf`. @@ -127,7 +127,7 @@ Let's assume you want to name it 'myapi', 3. Then you can use your api to issue cert like: ``` -acme.sh --issue --dns dns_myapi -d aa.com -d www.aa.com +acme.sh --issue --dns dns_myapi -d example.com -d www.example.com ``` For more details, please check our sample script: [dns_myapi.sh](dns_myapi.sh) From c9febbdd876888e2b16c1b6b57f7658c63bea91e Mon Sep 17 00:00:00 2001 From: neil Date: Wed, 19 Oct 2016 22:14:42 +0800 Subject: [PATCH 04/10] fix for centos ncat and debian default netcat (#330) --- acme.sh | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 09920730..0ea9584f 100755 --- a/acme.sh +++ b/acme.sh @@ -1231,14 +1231,29 @@ _startserver() { _debug "_NC" "$_NC" + #for centos ncat + if _contains "$nchelp" "nmap.org" ; then + _debug "Using ncat: nmap.org" + if [ "$DEBUG" ] ; then + if printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ; then + return + fi + else + if printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1; then + return + fi + fi + _err "ncat listen error." + fi + # while true ; do if [ "$DEBUG" ] ; then - if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ; then - printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort ; + if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort ; then + printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort ; fi else - if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1; then - printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1 + if ! printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC -p $Le_HTTPPort > /dev/null 2>&1; then + printf "HTTP/1.1 200 OK\r\n\r\n$content" | $_NC $Le_HTTPPort > /dev/null 2>&1 fi fi if [ "$?" != "0" ] ; then From ecf0a710e13cc233d897c6634e0f632dbae2f0ed Mon Sep 17 00:00:00 2001 From: neil Date: Fri, 21 Oct 2016 18:26:35 +0800 Subject: [PATCH 05/10] fix typo --- acme.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acme.sh b/acme.sh index 0ea9584f..1e6b0a56 100755 --- a/acme.sh +++ b/acme.sh @@ -939,13 +939,13 @@ _post() { elif _exists "wget" ; then _debug "WGET" "$WGET" if [ "$needbase64" ] ; then - if [ "$httpmethod"="POST" ] ; then + if [ "$httpmethod" = "POST" ] ; then response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" else response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER" | _base64)" fi else - if [ "$httpmethod"="POST" ] ; then + if [ "$httpmethod" = "POST" ] ; then response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --post-data="$body" "$url" 2>"$HTTP_HEADER")" else response="$($WGET -S -O - --user-agent="$USER_AGENT" --header "$_H5" --header "$_H4" --header "$_H3" --header "$_H2" --header "$_H1" --method $httpmethod --body-data="$body" "$url" 2>"$HTTP_HEADER")" From 9774b01b0ed721bc49caf915ad6a50c85eacec85 Mon Sep 17 00:00:00 2001 From: neil Date: Sun, 23 Oct 2016 14:56:52 +0800 Subject: [PATCH 06/10] 2.6.1 support IDN (#335) * Support IDN * fix deactivate idn name * 2.6.1 support IDN --- acme.sh | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/acme.sh b/acme.sh index 1e6b0a56..5a0ee4e7 100755 --- a/acme.sh +++ b/acme.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -VER=2.6.0 +VER=2.6.1 PROJECT_NAME="acme.sh" @@ -482,6 +482,42 @@ _createkey() { fi } + +#domain +_is_idn() { + _is_idn_d="$1" + echo "$_is_idn_d" | grep "[^0-9a-zA-Z.,]" >/dev/null 2>&1 +} + +#aa.com +#aa.com,bb.com,cc.com +_idn() { + __idn_d="$1" + if ! _is_idn "$__idn_d" ; then + printf "%s" "$__idn_d" + return 0 + fi + + if _exists idn ; then + if _contains "$__idn_d" ',' ; then + _i_first="1" + for f in $(echo "$__idn_d" | tr ',' ' ') ; do + [ -z "$f" ] && continue + if [ -z "$_i_first" ] ; then + printf "%s" "," + else + _i_first="" + fi + idn "$f" | tr -d "\r\n" + done + else + idn "$__idn_d" | tr -d "\r\n" + fi + else + _err "Please install idn to process IDN names." + fi +} + #_createcsr cn san_list keyfile csrfile conf _createcsr() { _debug _createcsr @@ -502,6 +538,8 @@ _createcsr() { #single domain _info "Single domain" "$domain" else + domainlist="$(_idn $domainlist)" + _debug2 domainlist "$domainlist" if _contains "$domainlist" "," ; then alt="DNS:$(echo $domainlist | sed "s/,/,DNS:/g")" else @@ -515,7 +553,10 @@ _createcsr() { _savedomainconf Le_OCSP_Stable "$Le_OCSP_Stable" printf -- "\nbasicConstraints = CA:FALSE\n1.3.6.1.5.5.7.1.24=DER:30:03:02:01:05" >> "$csrconf" fi - openssl req -new -sha256 -key "$csrkey" -subj "/CN=$domain" -config "$csrconf" -out "$csr" + + _csr_cn="$(_idn "$domain")" + _debug2 _csr_cn "$_csr_cn" + openssl req -new -sha256 -key "$csrkey" -subj "/CN=$_csr_cn" -config "$csrconf" -out "$csr" } #_signcsr key csr conf cert @@ -2144,7 +2185,7 @@ issue() { _info "Getting new-authz for domain" $d - if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$d\"}}" ; then + if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$d")\"}}" ; then _err "Can not get domain token." _clearup _on_issue_err @@ -3027,7 +3068,7 @@ _deactivate() { do _info "Deactivate: $_d_domain" _d_i="$(_math $_d_i + 1)" - if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$_d_domain\"}}" ; then + if ! _send_signed_request "$API/acme/new-authz" "{\"resource\": \"new-authz\", \"identifier\": {\"type\": \"dns\", \"value\": \"$(_idn "$_d_domain")\"}}" ; then _err "Can not get domain token." return 1 fi @@ -3727,6 +3768,10 @@ _process() { _err "'$_dvalue' is not a valid domain for parameter '$1'" return 1 fi + if _is_idn "$_dvalue" && ! _exists idn ; then + _err "It seems that $_dvalue is an IDN( Internationalized Domain Names), please install 'idn' command first." + return 1 + fi if [ -z "$_domain" ] ; then _domain="$_dvalue" From aba5c634ae3865280c3496fabf48b5790434040d Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 23 Oct 2016 15:04:52 +0800 Subject: [PATCH 07/10] minor, add output info for installing alias. fix https://github.com/Neilpang/acme.sh/issues/332 --- acme.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acme.sh b/acme.sh index 5a0ee4e7..b283ff6b 100755 --- a/acme.sh +++ b/acme.sh @@ -3310,6 +3310,7 @@ _installalias() { _profile="$(_detect_profile)" if [ "$_profile" ] ; then _debug "Found profile: $_profile" + _info "Installing alias to '$_profile'" _setopt "$_profile" ". \"$_envfile\"" _info "OK, Close and reopen your terminal to start using $PROJECT_NAME" else @@ -3321,6 +3322,7 @@ _installalias() { _cshfile="$LE_WORKING_DIR/$PROJECT_ENTRY.csh" _csh_profile="$HOME/.cshrc" if [ -f "$_csh_profile" ] ; then + _info "Installing alias to '$_csh_profile'" _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\"" _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" _setopt "$_csh_profile" "source \"$_cshfile\"" @@ -3329,6 +3331,7 @@ _installalias() { #for tcsh _tcsh_profile="$HOME/.tcshrc" if [ -f "$_tcsh_profile" ] ; then + _info "Installing alias to '$_tcsh_profile'" _setopt "$_cshfile" "setenv LE_WORKING_DIR" " " "\"$LE_WORKING_DIR\"" _setopt "$_cshfile" "alias $PROJECT_ENTRY" " " "\"$LE_WORKING_DIR/$PROJECT_ENTRY\"" _setopt "$_tcsh_profile" "source \"$_cshfile\"" From 9aa3be7f9f585ad25f40eed66483f82db0d433a1 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 23 Oct 2016 15:10:09 +0800 Subject: [PATCH 08/10] add _uninstallalias --- acme.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/acme.sh b/acme.sh index b283ff6b..4096288d 100755 --- a/acme.sh +++ b/acme.sh @@ -3447,26 +3447,36 @@ uninstall() { fi _initpath + _uninstallalias + + rm -f $LE_WORKING_DIR/$PROJECT_ENTRY + _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself." + +} + +_uninstallalias() { + _initpath + _profile="$(_detect_profile)" if [ "$_profile" ] ; then + _info "Uninstalling alias from: '$_profile'" text="$(cat $_profile)" echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.env\"$||" > "$_profile" fi _csh_profile="$HOME/.cshrc" if [ -f "$_csh_profile" ] ; then + _info "Uninstalling alias from: '$_csh_profile'" text="$(cat $_csh_profile)" echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_csh_profile" fi _tcsh_profile="$HOME/.tcshrc" if [ -f "$_tcsh_profile" ] ; then + _info "Uninstalling alias from: '$_csh_profile'" text="$(cat $_tcsh_profile)" echo "$text" | sed "s|^.*\"$LE_WORKING_DIR/$PROJECT_NAME.csh\"$||" > "$_tcsh_profile" fi - - rm -f $LE_WORKING_DIR/$PROJECT_ENTRY - _info "The keys and certs are in $LE_WORKING_DIR, you can remove them by yourself." } From 049be10406501f9f9d294cd18841818ff1e3d226 Mon Sep 17 00:00:00 2001 From: neilpang Date: Sun, 23 Oct 2016 20:36:32 +0800 Subject: [PATCH 09/10] fix idn to support European chars https://github.com/Neilpang/acme.sh/issues/331#issuecomment-255583889 --- acme.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 4096288d..3eccc3c7 100755 --- a/acme.sh +++ b/acme.sh @@ -486,7 +486,10 @@ _createkey() { #domain _is_idn() { _is_idn_d="$1" - echo "$_is_idn_d" | grep "[^0-9a-zA-Z.,]" >/dev/null 2>&1 + _debug2 _is_idn_d "$_is_idn_d" + _idn_temp=$(printf "%s" "$_is_idn_d" | tr -d "[0-9a-zA-Z.,-]") + _debug2 _idn_temp "$_idn_temp" + [ "$_idn_temp" ] } #aa.com From 9910ff5fa17caac7f95a93d16d2dc59eebf64117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20L=C3=BCscher?= Date: Tue, 25 Oct 2016 14:49:22 +0200 Subject: [PATCH 10/10] Allow saved password strings to have special characters. (#334) --- acme.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme.sh b/acme.sh index 3eccc3c7..a1d19fd6 100755 --- a/acme.sh +++ b/acme.sh @@ -1224,7 +1224,7 @@ _saveaccountconf() { _sckey="$1" _scvalue="$2" if [ "$ACCOUNT_CONF_PATH" ] ; then - _setopt "$ACCOUNT_CONF_PATH" "$_sckey" "=" "\"$_scvalue\"" + _setopt "$ACCOUNT_CONF_PATH" "$_sckey" "=" "'$_scvalue'" else _err "ACCOUNT_CONF_PATH is empty, can not save $_sckey=$_scvalue" fi