From f758e75a843641b9e04d02ad7a6369419b087b57 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Tue, 8 Oct 2024 15:10:03 +0200 Subject: [PATCH] random path - set cert to panel random port make random path if empty Show Existing Domains set cert to panel --- install.sh | 82 +++++++--- main.go | 109 ++++++++++--- web/service/setting.go | 18 +++ x-ui.sh | 347 +++++++++++++++++++++++++++++------------ 4 files changed, 411 insertions(+), 145 deletions(-) diff --git a/install.sh b/install.sh index 428120ac..29272c8d 100755 --- a/install.sh +++ b/install.sh @@ -117,38 +117,76 @@ install_dependencies() { esac } -#This function will be called when user installed x-ui out of security +gen_random_string() { + local length="$1" + local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' 0 { + err := settingService.SetPort(port) + if err != nil { + fmt.Println("Failed to set port:", err) + } else { + fmt.Printf("Port set successfully: %v\n", port) + } + } + + if username != "" || password != "" { + err := userService.UpdateFirstUser(username, password) + if err != nil { + fmt.Println("Failed to update username and password:", err) + } else { + fmt.Println("Username and password updated successfully") + } + } + + if webBasePath != "" { + err := settingService.SetBasePath(webBasePath) + if err != nil { + fmt.Println("Failed to set base URI path:", err) + } else { + fmt.Println("Base URI path set successfully") + } + } +} +func updateCert(publicKey string, privateKey string) { err := database.InitDB(config.GetDBPath()) if err != nil { fmt.Println(err) return } - settingService := service.SettingService{} + if (privateKey != "" && publicKey != "") || (privateKey == "" && publicKey == "") { + settingService := service.SettingService{} + err = settingService.SetCertFile(publicKey) + if err != nil { + fmt.Println("set certificate public key failed:", err) + } else { + fmt.Println("set certificate public key success") + } - if port > 0 { - err := settingService.SetPort(port) + err = settingService.SetKeyFile(privateKey) if err != nil { - fmt.Println("set port failed:", err) + fmt.Println("set certificate private key failed:", err) } else { - fmt.Printf("set port %v success", port) - } - } - if username != "" || password != "" { - userService := service.UserService{} - err := userService.UpdateFirstUser(username, password) - if err != nil { - fmt.Println("set username and password failed:", err) - } else { - fmt.Println("set username and password success") + fmt.Println("set certificate private key success") } + } else { + fmt.Println("both public and private key should be entered.") } } @@ -256,6 +305,9 @@ func main() { var port int var username string var password string + var webBasePath string + var webCertFile string + var webKeyFile string var tgbottoken string var tgbotchatid string var enabletgbot bool @@ -267,7 +319,10 @@ func main() { settingCmd.IntVar(&port, "port", 0, "set panel port") settingCmd.StringVar(&username, "username", "", "set login username") settingCmd.StringVar(&password, "password", "", "set login password") - settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegram bot token") + settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel") + settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel") + settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel") + settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot") settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegram bot cron time") settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "set telegram bot chat id") settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "enable telegram bot notify") @@ -307,7 +362,7 @@ func main() { if reset { resetSetting() } else { - updateSetting(port, username, password) + updateSetting(port, username, password, webBasePath) } if show { showSetting(show) @@ -318,6 +373,18 @@ func main() { if enabletgbot { updateTgbotEnableSts(enabletgbot) } + case "cert": + err := settingCmd.Parse(os.Args[2:]) + if err != nil { + fmt.Println(err) + return + } + if reset { + updateCert("", "") + } else { + updateCert(webCertFile, webKeyFile) + } + default: fmt.Println("Invalid subcommands") fmt.Println() diff --git a/web/service/setting.go b/web/service/setting.go index 4dcc3614..5e447554 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -283,10 +283,18 @@ func (s *SettingService) SetPort(port int) error { return s.setInt("webPort", port) } +func (s *SettingService) SetCertFile(webCertFile string) error { + return s.setString("webCertFile", webCertFile) +} + func (s *SettingService) GetCertFile() (string, error) { return s.getString("webCertFile") } +func (s *SettingService) SetKeyFile(webKeyFile string) error { + return s.setString("webKeyFile", webKeyFile) +} + func (s *SettingService) GetKeyFile() (string, error) { return s.getString("webKeyFile") } @@ -318,6 +326,16 @@ func (s *SettingService) GetSecret() ([]byte, error) { return []byte(secret), err } +func (s *SettingService) SetBasePath(basePath string) error { + if !strings.HasPrefix(basePath, "/") { + basePath = "/" + basePath + } + if !strings.HasSuffix(basePath, "/") { + basePath += "/" + } + return s.setString("webBasePath", basePath) +} + func (s *SettingService) GetBasePath() (string, error) { basePath, err := s.getString("webBasePath") if err != nil { diff --git a/x-ui.sh b/x-ui.sh index 23afec23..f245befb 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -34,7 +34,6 @@ fi echo "The OS release is: $release" - os_version="" os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) @@ -158,7 +157,7 @@ custom_version() { if [ -z "$panel_version" ]; then echo "Panel version cannot be empty. Exiting." - exit 1 + exit 1 fi download_link="https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh" @@ -172,7 +171,7 @@ custom_version() { # Function to handle the deletion of the script file delete_script() { - rm "$0" # Remove the script file itself + rm "$0" # Remove the script file itself exit 1 } @@ -214,6 +213,31 @@ reset_user() { confirm_restart } +gen_random_string() { + local length="$1" + local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' /dev/null 2>&1 + + echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}" + echo -e "${green}Please use the new web base path to access the panel.${plain}" + restart +} + reset_config() { confirm "Are you sure you want to reset all panel settings,Account data will not be lost,Username and password will not change" "n" if [[ $? != 0 ]]; then @@ -452,15 +476,23 @@ show_xray_status() { } install_acme() { - cd ~ - LOGI "install acme..." - curl https://get.acme.sh | sh + # Check if acme.sh is already installed + if command -v ~/.acme.sh/acme.sh &>/dev/null; then + LOGI "acme.sh is already installed." + return 0 + fi + + LOGI "Installing acme.sh..." + cd ~ || return 1 # Ensure you can change to the home directory + + curl -s https://get.acme.sh | sh if [ $? -ne 0 ]; then - LOGE "install acme failed" + LOGE "Installation of acme.sh failed." return 1 else - LOGI "install acme succeed" + LOGI "Installation of acme.sh succeeded." fi + return 0 } @@ -468,20 +500,100 @@ ssl_cert_issue_main() { echo -e "${green}\t1.${plain} Get SSL" echo -e "${green}\t2.${plain} Revoke" echo -e "${green}\t3.${plain} Force Renew" + echo -e "${green}\t4.${plain} Show Existing Domains" + echo -e "${green}\t5.${plain} Set Cert paths for the panel" + echo -e "${green}\t0.${plain} Back to Main Menu" + read -p "Choose an option: " choice case "$choice" in - 1) ssl_cert_issue ;; - 2) - local domain="" - read -p "Please enter your domain name to revoke the certificate: " domain - ~/.acme.sh/acme.sh --revoke -d ${domain} - LOGI "Certificate revoked" - ;; - 3) - local domain="" - read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain - ~/.acme.sh/acme.sh --renew -d ${domain} --force ;; - *) echo "Invalid choice" ;; + 0) + show_menu + ;; + 1) + ssl_cert_issue + ;; + 2) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "No certificates found to revoke." + else + echo "Existing domains:" + echo "$domains" + read -p "Please enter a domain from the list to revoke the certificate: " domain + if echo "$domains" | grep -qw "$domain"; then + ~/.acme.sh/acme.sh --revoke -d ${domain} + LOGI "Certificate revoked for domain: $domain" + else + echo "Invalid domain entered." + fi + fi + ;; + 3) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "No certificates found to renew." + else + echo "Existing domains:" + echo "$domains" + read -p "Please enter a domain from the list to renew the SSL certificate: " domain + if echo "$domains" | grep -qw "$domain"; then + ~/.acme.sh/acme.sh --renew -d ${domain} --force + LOGI "Certificate forcefully renewed for domain: $domain" + else + echo "Invalid domain entered." + fi + fi + ;; + 4) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "No certificates found." + else + echo "Existing domains and their paths:" + for domain in $domains; do + local cert_path="/root/cert/${domain}/fullchain.pem" + local key_path="/root/cert/${domain}/privkey.pem" + if [[ -f "${cert_path}" && -f "${key_path}" ]]; then + echo -e "Domain: ${domain}" + echo -e "\tCertificate Path: ${cert_path}" + echo -e "\tPrivate Key Path: ${key_path}" + else + echo -e "Domain: ${domain} - Certificate or Key missing." + fi + done + fi + ;; + 5) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "No certificates found." + else + echo "Available domains:" + echo "$domains" + read -p "Please choose a domain to set the panel paths: " domain + + if echo "$domains" | grep -qw "$domain"; then + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then + /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + echo "Panel paths set for domain: $domain" + echo " - Certificate File: $webCertFile" + echo " - Private Key File: $webKeyFile" + restart + else + echo "Certificate or private key not found for domain: $domain." + fi + else + echo "Invalid domain entered." + fi + fi + ;; + + *) + echo "Invalid choice" + ;; esac } @@ -495,6 +607,7 @@ ssl_cert_issue() { exit 1 fi fi + # install socat second case "${release}" in ubuntu | debian | armbian) @@ -503,7 +616,7 @@ ssl_cert_issue() { centos | almalinux | rocky | oracle) yum -y update && yum -y install socat ;; - fedora) + fedora | amzn) dnf -y update && dnf -y install socat ;; arch | manjaro | parch) @@ -521,23 +634,23 @@ ssl_cert_issue() { LOGI "install socat succeed..." fi - # get the domain here,and we need verify it + # get the domain here, and we need to verify it local domain="" - read -p "Please enter your domain name:" domain - LOGD "your domain is:${domain},check it..." - # here we need to judge whether there exists cert already - local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') + read -p "Please enter your domain name: " domain + LOGD "Your domain is: ${domain}, checking it..." - if [ ${currentCert} == ${domain} ]; then + # check if there already exists a certificate + local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') + if [ "${currentCert}" == "${domain}" ]; then local certInfo=$(~/.acme.sh/acme.sh --list) - LOGE "system already has certs here,can not issue again,current certs details:" + LOGE "System already has certificates for this domain. Cannot issue again. Current certificate details:" LOGI "$certInfo" exit 1 else - LOGI "your domain is ready for issuing cert now..." + LOGI "Your domain is ready for issuing certificates now..." fi - # create a directory for install cert + # create a directory for the certificate certPath="/root/cert/${domain}" if [ ! -d "$certPath" ]; then mkdir -p "$certPath" @@ -546,48 +659,70 @@ ssl_cert_issue() { mkdir -p "$certPath" fi - # get needed port here + # get the port number for the standalone server local WebPort=80 - read -p "please choose which port do you use,default will be 80 port:" WebPort + read -p "Please choose which port to use (default is 80): " WebPort if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then - LOGE "your input ${WebPort} is invalid,will use default port" + LOGE "Your input ${WebPort} is invalid, will use default port 80." + WebPort=80 fi - LOGI "will use port:${WebPort} to issue certs,please make sure this port is open..." - # NOTE:This should be handled by user - # open the port and kill the occupied progress + LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open." + + # issue the certificate ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt - ~/.acme.sh/acme.sh --issue -d ${domain} --standalone --httpport ${WebPort} + ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} if [ $? -ne 0 ]; then - LOGE "issue certs failed,please check logs" + LOGE "Issuing certificate failed, please check logs." rm -rf ~/.acme.sh/${domain} exit 1 else - LOGE "issue certs succeed,installing certs..." + LOGE "Issuing certificate succeeded, installing certificates..." fi - # install cert + + # install the certificate ~/.acme.sh/acme.sh --installcert -d ${domain} \ --key-file /root/cert/${domain}/privkey.pem \ --fullchain-file /root/cert/${domain}/fullchain.pem if [ $? -ne 0 ]; then - LOGE "install certs failed,exit" + LOGE "Installing certificate failed, exiting." rm -rf ~/.acme.sh/${domain} exit 1 else - LOGI "install certs succeed,enable auto renew..." + LOGI "Installing certificate succeeded, enabling auto renew..." fi + # enable auto-renew ~/.acme.sh/acme.sh --upgrade --auto-upgrade if [ $? -ne 0 ]; then - LOGE "auto renew failed, certs details:" + LOGE "Auto renew failed, certificate details:" ls -lah cert/* chmod 755 $certPath/* exit 1 else - LOGI "auto renew succeed, certs details:" + LOGI "Auto renew succeeded, certificate details:" ls -lah cert/* chmod 755 $certPath/* fi + + # Prompt user to set panel paths after successful certificate installation + read -p "Would you like to set this certificate for the panel? (y/n): " setPanel + if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then + /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + LOGI "Panel paths set for domain: $domain" + LOGI " - Certificate File: $webCertFile" + LOGI " - Private Key File: $webKeyFile" + restart + else + LOGE "Error: Certificate or private key file not found for domain: $domain." + fi + else + LOGI "Skipping panel path setting." + fi } ssl_cert_issue_CF() { @@ -643,8 +778,8 @@ ssl_cert_issue_CF() { LOGI "Certificate issued Successfully, Installing..." fi ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \ - --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \ - --fullchain-file /root/cert/fullchain.cer + --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \ + --fullchain-file /root/cert/fullchain.cer if [ $? -ne 0 ]; then LOGE "Certificate installation failed, script exiting..." exit 1 @@ -867,32 +1002,32 @@ update_geo() { read -p "Select: " select case "$select" in - 0) - show_menu - ;; + 0) + show_menu + ;; - 1) - wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - echo -e "${green}Files are updated.${plain}" - confirm_restart - ;; + 1) + wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + echo -e "${green}Files are updated.${plain}" + confirm_restart + ;; - 2) - wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" - echo -e "${green}Files are updated.${plain}" - confirm_restart - ;; + 2) + wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n" + echo -e "${green}Files are updated.${plain}" + confirm_restart + ;; - *) - LOGE "Please enter a correct number [0-2]\n" - update_geo - ;; + *) + LOGE "Please enter a correct number [0-2]\n" + update_geo + ;; esac } @@ -933,12 +1068,13 @@ run_speedtest() { show_usage() { echo "X-UI Control Menu Usage" echo "------------------------------------------" - echo "SUBCOMMANDS:" + echo "SUBCOMMANDS:" echo "x-ui - Admin Management Script" echo "x-ui start - Start" echo "x-ui stop - Stop" echo "x-ui restart - Restart" echo "x-ui status - Current Status" + echo "x-ui settings - Current Settings" echo "x-ui enable - Enable Autostart on OS Startup" echo "x-ui disable - Disable Autostart on OS Startup" echo "x-ui log - Check Logs" @@ -961,26 +1097,27 @@ show_menu() { ${green}4.${plain} Uninstall ———————————————— ${green}5.${plain} Reset Username and Password - ${green}6.${plain} Reset Panel Settings - ${green}7.${plain} Set Panel Port - ${green}8.${plain} View Panel Settings + ${green}6.${plain} Reset Web Base Path + ${green}7.${plain} Reset Panel Settings + ${green}8.${plain} Set Panel Port + ${green}9.${plain} View Panel Settings ———————————————— - ${green}9.${plain} Start - ${green}10.${plain} Stop - ${green}11.${plain} Restart - ${green}12.${plain} Check State - ${green}13.${plain} Check Logs + ${green}10.${plain} Start + ${green}11.${plain} Stop + ${green}12.${plain} Restart + ${green}13.${plain} Check State + ${green}14.${plain} Check Logs ———————————————— - ${green}14.${plain} Enable Autostart - ${green}15.${plain} Disable Autostart + ${green}15.${plain} Enable Autostart + ${green}16.${plain} Disable Autostart ———————————————— - ${green}16.${plain} SSL Certificate Management - ${green}17.${plain} Cloudflare SSL Certificate - ${green}18.${plain} Firewall Management + ${green}17.${plain} SSL Certificate Management + ${green}18.${plain} Cloudflare SSL Certificate + ${green}19.${plain} Firewall Management ———————————————— - ${green}19.${plain} Enable or Disable BBR - ${green}20.${plain} Update Geo Files - ${green}21.${plain} Speedtest by Ookla + ${green}20.${plain} Enable or Disable BBR + ${green}21.${plain} Update Geo Files + ${green}22.${plain} Speedtest by Ookla " show_status echo && read -p "Please enter your selection [0-21]: " num @@ -1005,55 +1142,58 @@ show_menu() { check_install && reset_user ;; 6) - check_install && reset_config + check_install && reset_webbasepath ;; 7) - check_install && set_port + check_install && reset_config ;; 8) - check_install && check_config + check_install && set_port ;; 9) - check_install && start + check_install && check_config ;; 10) - check_install && stop + check_install && start ;; 11) - check_install && restart + check_install && stop ;; 12) - check_install && status + check_install && restart ;; 13) - check_install && show_log + check_install && status ;; 14) - check_install && enable + check_install && show_log ;; 15) - check_install && disable + check_install && enable ;; 16) - ssl_cert_issue_main + check_install && disable ;; 17) - ssl_cert_issue_CF + ssl_cert_issue_main ;; 18) - firewall_menu + ssl_cert_issue_CF ;; 19) - bbr_menu + firewall_menu ;; 20) - update_geo + bbr_menu ;; 21) + update_geo + ;; + 22) run_speedtest ;; *) - LOGE "Please enter the correct number [0-21]" + LOGE "Please enter the correct number [0-22]" ;; esac } @@ -1072,6 +1212,9 @@ if [[ $# > 0 ]]; then "status") check_install 0 && status 0 ;; + "settings") + check_install 0 && check_config 0 + ;; "enable") check_install 0 && enable 0 ;;