#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH


# shell version
# ====================
SHELL_VERSION="2.8.4"
# ====================


# current path
CUR_DIR=$( pwd )


# base url
methods="Online"
BASE_URL="https://github.com/loyess/Shell/raw/master"
if [ -e install ] && [ -e prepare ] && [ -e service ] && [ -e templates ] && [ -e utils ] && [ -e webServer ]; then
    methods="Local"
    BASE_URL="${CUR_DIR}" 
fi


# bbr
TEDDYSUN_BBR_SCRIPT_URL="https://raw.githubusercontent.com/teddysun/across/master/bbr.sh"


# Humanization config PATH
HUMAN_CONFIG="/etc/shadowsocks/humanization.conf"


# script env dir
SS_PLUGINS_SCRIPT_ENV_DIR="/root/.ssPluginsScriptEnv"
SS_PLUGINS_SCRIPT_ENV_VARIABLES_FILE="${SS_PLUGINS_SCRIPT_ENV_DIR}/env.info"


# shadowsocks config
SHADOWSOCKS_CONFIG="/etc/shadowsocks/config.json"

# shadowsocks-libev config and init
SHADOWSOCKS_LIBEV_INSTALL_PATH="/usr/local/bin"
SHADOWSOCKS_LIBEV_BIN_PATH="/usr/local/bin/ss-server"
SHADOWSOCKS_LIBEV_INIT="/etc/init.d/shadowsocks-libev"
SHADOWSOCKS_LIBEV_INIT_LOCAL="./service/shadowsocks-libev.sh"
SHADOWSOCKS_LIBEV_INIT_ONLINE="${BASE_URL}/service/shadowsocks-libev.sh"

# shadowsocks-rust config and init
SHADOWSOCKS_RUST_INSTALL_PATH="/usr/local/bin"
SHADOWSOCKS_RUST_BIN_PATH="/usr/local/bin/ssservice"
SHADOWSOCKS_RUST_INIT="/etc/init.d/shadowsocks-rust"
SHADOWSOCKS_RUST_INIT_LOCAL="./service/shadowsocks-rust.sh"
SHADOWSOCKS_RUST_INIT_ONLINE="${BASE_URL}/service/shadowsocks-rust.sh"

# go-shadowsocks2 config and init
GO_SHADOWSOCKS2_INSTALL_PATH="/usr/local/bin"
GO_SHADOWSOCKS2_BIN_PATH="/usr/local/bin/go-shadowsocks2"
GO_SHADOWSOCKS2_INIT="/etc/init.d/go-shadowsocks2"
GO_SHADOWSOCKS2_INIT_LOCAL="./service/go-shadowsocks2.sh"
GO_SHADOWSOCKS2_INIT_ONLINE="${BASE_URL}/service/go-shadowsocks2.sh"
GO_SHADOWSOCKS2_VERSION_FILE="/etc/shadowsocks/go-shadowsocks2.v"


# v2ray-plugin
V2RAY_PLUGIN_INSTALL_PATH="/usr/local/bin"
V2RAY_PLUGIN_BIN_PATH="/usr/local/bin/v2ray-plugin"


# kcptun
KCPTUN_INSTALL_PATH="/usr/local/kcptun"
KCPTUN_BIN_PATH="/usr/local/kcptun/kcptun-server"
KCPTUN_INIT="/etc/init.d/kcptun"
KCPTUN_CONFIG="/etc/kcptun/config.json"
KCPTUN_INIT_LOCAL="./service/kcptun.sh"
KCPTUN_INIT_ONLINE="${BASE_URL}/service/kcptun.sh"


# simple-obfs
SIMPLE_OBFS_INSTALL_PATH="/usr/local/bin"
SIMPLE_OBFS_BIN_PATH="/usr/local/bin/obfs-server"


# goquiet
GOQUIET_INSTALL_PATH="/usr/local/bin"
GOQUIET_BIN_PATH="/usr/local/bin/gq-server"


# cloak
CLOAK_INSTALL_PATH="/usr/local/bin"
CLOAK_SERVER_BIN_PATH="/usr/local/bin/ck-server"
CLOAK_CLIENT_BIN_PATH="/usr/local/bin/ck-client"
CLOAK_INIT="/etc/init.d/cloak"
CLOAK_INIT_LOCAL="./service/cloak.sh"
CLOAK_INIT_ONLINE="${BASE_URL}/service/cloak.sh"
CK_DB_PATH="/etc/cloak"
CK_CLIENT_CONFIG="/etc/cloak/ckclient.json"
CK_SERVER_CONFIG="/etc/cloak/ckserver.json"


# mos-tls-tunnel
MTT_VERSION_FILE="/etc/shadowsocks/mtt.v"
MTT_INSTALL_PATH="/usr/local/bin"
MTT_BIN_PATH="/usr/local/bin/mtt-server"


# rabbit-tcp
RABBIT_INSTALL_PATH="/usr/local/bin"
RABBIT_BIN_PATH="/usr/local/bin/rabbit-tcp"
RABBIT_INIT="/etc/init.d/rabbit-tcp"
RABBIT_CONFIG="/etc/rabbit-tcp/config.json"
RABBIT_VERSION_FILE="/etc/rabbit-tcp/rabbit-tcp.v"
RABBIT_INIT_LOCAL="./service/rabbit-tcp.sh"
RABBIT_INIT_ONLINE="${BASE_URL}/service/rabbit-tcp.sh"


# simple-tls
SIMPLE_TLS_INSTALL_PATH="/usr/local/bin"
SIMPLE_TLS_BIN_PATH="/usr/local/bin/simple-tls"
SIMPLE_TLS_VERSION_FILE="/etc/shadowsocks/simple-tls.v"


# gost-plugin
GOST_PLUGIN_INSTALL_PATH="/usr/local/bin"
GOST_PLUGIN_BIN_PATH="/usr/local/bin/gost-plugin"
GOST_PLUGIN_VERSION_FILE="/etc/shadowsocks/gost-plugin.v"


# xray-plugin
XRAY_PLUGIN_INSTALL_PATH="/usr/local/bin"
XRAY_PLUGIN_BIN_PATH="/usr/local/bin/xray-plugin"
XRAY_PLUGIN_VERSION_FILE="/etc/shadowsocks/xray-plugin.v"


# qtun
QTUN_INSTALL_PATH="/usr/local/bin"
QTUN_BIN_PATH="/usr/local/bin/qtun-server"
QTUN_VERSION_FILE="/etc/shadowsocks/qtun.v"


# gun
GUN_INSTALL_PATH="/usr/local/bin"
GUN_BIN_PATH="/usr/local/bin/gun-server"
GUN_VERSION_FILE="/etc/shadowsocks/gun.v"


SS_PLUGINS_NAME=(
v2ray-plugin
kcptun
simple-obfs
goquiet
cloak
mos-tls-tunnel
rabbit-tcp
simple-tls
gost-plugin
xray-plugin
qtun
gun
)


# file exist：install
WEB_INSTALL_MARK="/root/.WebInstallMark"


# caddy
CADDY_INSTALL_PATH="/usr/local/caddy"
CADDY_BIN_PATH="/usr/local/caddy/caddy"
CADDY_CONF_FILE="/usr/local/caddy/Caddyfile"
CADDY_VERSION_FILE="/usr/local/caddy/caddy.v"
CADDY_INIT="/etc/init.d/caddy"
CADDY_INIT_LOCAL="./service/caddy.sh"
CADDY_INIT_ONLINE="${BASE_URL}/service/caddy.sh"
CADDY_V2_INIT_LOCAL="./service/caddy2.sh"
CADDY_V2_INIT_ONLINE="${BASE_URL}/service/caddy2.sh"


# nginx
NGINX_BIN_PATH="/usr/sbin/nginx"
NGINX_CONFIG="/etc/nginx/nginx.conf"


# RE
EMAIL_RE="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$"
DOMAIN_RE="^(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$"
IPV4_RE="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"
IPV4_PORT_RE="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\:443$"
HTTPS_DOMAIN_RE="^(https:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$"
IPV6_RE="^\s*((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4}){0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?\s*$"


# Font color and background color
Green="\033[32m" && Red="\033[31m" && Yellow="\033[0;33m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && suffix="\033[0m"
Info="${Green}[信息]${suffix}"
Error="${Red}[错误]${suffix}"
Point="${Red}[提示]${suffix}"
Tip="${Green}[注意]${suffix}"
Warning="${Yellow}[警告]${suffix}"
Separator_1="——————————————————————————————"


# Root permission
[ $EUID -ne 0 ] && echo -e "[${Red}Error${suffix}] This script must be run as root!" && exit 1


usage() {
    echo "Usage:"
    echo "  ./ss-plugins.sh [options...] [args...]"
    echo
    echo "Available Options:"
    echo "  install          安装"
    echo "  uninstall        卸载"
    echo "  update           升级"
    echo "  start            启动"
    echo "  stop             关闭"
    echo "  restart          重启"
    echo "  status           查看状态"
    echo "  script           升级脚本"
    echo "  show             可视化配置"
    echo "  log              查看日志文件"
    echo "  catcfg           查看原始配置文件"
    echo "  uid              添加一个新的uid用户(Cloak)"
    echo "  cert             为.cf .ga .gq .ml .tk申请证书(90天)"
    echo "  link             用新添加的uid生成一个新的SS://链接(Cloak)"
    echo "  scan             用ss://链接在当前终端上生成一个可供扫描的二维码"
    echo "  help             打印帮助信息并退出"
    exit $1
}

_echo(){
    case $1 in
    -u|upon)
        echo
        echo -e "${2}"
        ;;
    -d|down)
        echo -e "${2}"
        echo
        ;;
    -r|red)
        echo
        echo -e "${Red}${2}${suffix}"
        echo
        ;;
    -g|green)
        echo
        echo -e "${Green}${2}${suffix}"
        echo
        ;;
    -i|info)
        echo -e "${Info} ${2}"
        ;;
    -e|error)
        echo -e "${Error} ${2}"
        ;;
    -t|tip)
        echo
        echo -e "${Tip} ${2}"
        echo
        ;;
    -w|warning)
        echo -e "${Warning} ${2}"
        ;;
    *)
        echo
        echo -e "${1}"
        echo
        ;;
    esac
}

_read(){
    case $1 in
    -u|upon)
        echo
        read -e -p "${2}" inputInfo
        ;;
    -d|down)
        read -e -p "${2}" inputInfo
        echo
        ;;
    *)
        echo
        read -e -p "${1}" inputInfo
        echo
        ;;
    esac
}

status_init(){
    if [ -e "${SHADOWSOCKS_LIBEV_BIN_PATH}" ]; then
        ssName="Shadowsocks-libev"
        ssPath="${SHADOWSOCKS_LIBEV_BIN_PATH}"
        ssPid=`ps -ef | grep -v grep | grep $SHADOWSOCKS_LIBEV_BIN_PATH | awk '{print $2}'`
    elif [ -e "${SHADOWSOCKS_RUST_BIN_PATH}" ]; then
        ssName="Shadowsocks-rust"
        ssPath="${SHADOWSOCKS_RUST_BIN_PATH}"
        ssPid=`ps -ef | grep -v grep | grep $SHADOWSOCKS_RUST_BIN_PATH | awk '{print $2}'`
    elif [ -e "${GO_SHADOWSOCKS2_BIN_PATH}" ]; then
        ssName="Go-shadowsocks2"
        ssPath="${GO_SHADOWSOCKS2_BIN_PATH}"
        ssPid=`ps -ef | grep -v grep | grep $GO_SHADOWSOCKS2_BIN_PATH | awk '{print $2}'`
    fi

    if [ -e "${V2RAY_PLUGIN_BIN_PATH}" ]; then
        pluginName="V2ray-plugin"
        pluginPath="${V2RAY_PLUGIN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep v2ray-plugin | awk '{print $2}'`
    elif [ -e "${KCPTUN_BIN_PATH}" ]; then
        pluginName="KcpTun"
        pluginPath="${KCPTUN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep kcptun-server | awk '{print $2}'`
    elif [ -e "${SIMPLE_OBFS_BIN_PATH}" ]; then
        pluginName="Simple-obfs"
        pluginPath="${SIMPLE_OBFS_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep obfs-server | awk '{print $2}'`
    elif [ -e "${GOQUIET_BIN_PATH}" ]; then
        pluginName="GoQuiet"
        pluginPath="${GOQUIET_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep gq-server | awk '{print $2}'`
    elif [ -e "${CLOAK_SERVER_BIN_PATH}" ]; then
        pluginName="Cloak"
        pluginPath="${CLOAK_SERVER_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep ck-server | awk '{print $2}'`
    elif [ -e "${MTT_BIN_PATH}" ]; then
        pluginName="Mos-tls-tunnel"
        pluginPath="${MTT_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep mtt-server | awk '{print $2}'`
    elif [ -e "${RABBIT_BIN_PATH}" ]; then
        pluginName="Rabbit-Tcp"
        pluginPath="${RABBIT_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep rabbit-tcp | awk '{print $2}'`
    elif [ -e "${SIMPLE_TLS_BIN_PATH}" ]; then
        pluginName="Simple-tls"
        pluginPath="${SIMPLE_TLS_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep simple-tls | awk '{print $2}'`
    elif [ -e "${GOST_PLUGIN_BIN_PATH}" ]; then
        pluginName="Gost-plugin"
        pluginPath="${GOST_PLUGIN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep gost-plugin | awk '{print $2}'`
    elif [ -e "${XRAY_PLUGIN_BIN_PATH}" ]; then
        pluginName="Xray-plugin"
        pluginPath="${XRAY_PLUGIN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep xray-plugin | awk '{print $2}'`
    elif [ -e "${QTUN_BIN_PATH}" ]; then
        pluginName="qtun"
        pluginPath="${QTUN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep qtun-server | awk '{print $2}'`
    elif [ -e "${GUN_BIN_PATH}" ]; then
        pluginName="gun"
        pluginPath="${GUN_BIN_PATH}"
        pluginPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep gun-server | awk '{print $2}'`
    fi

    if [ -e "${CADDY_BIN_PATH}" ] && [ -e "${WEB_INSTALL_MARK}" ]; then
        webName="Caddy"
        webPath="${CADDY_BIN_PATH}"
        webPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep caddy | awk '{print $2}'`
    elif [ -e "${NGINX_BIN_PATH}" ] && [ -e "${WEB_INSTALL_MARK}" ]; then
        webName="Nginx"
        webPath="${NGINX_BIN_PATH}"
        webPid=`ps -ef | grep -vE 'grep|-plugin-opts' | grep nginx.conf | awk '{print $2}'`
    fi
}

status_menu(){
    local NoInstall=" 当前状态: ${Red}未安装${suffix}"
    local InstallStart=" 当前状态: ${Green}已安装${suffix} 并 ${Green}已启动${suffix}"
    local InstallNoStart=" 当前状态: ${Green}已安装${suffix} 但 ${Red}未启动${suffix}"

    status_init

    if [ -e "${ssPath}" ] && [ -e "${pluginPath}" ] && [ -e "${webPath}" ]; then
        if [ -n "${ssPid}" ] && [ -n "${pluginPid}" ] && [ -n "${webPid}" ]; then
            echo -e "${InstallStart}"
        else
            echo -e "${InstallNoStart}"
        fi
    elif [ -e "${ssPath}" ] && [ -e "${pluginPath}" ]; then
        if [ -n "${ssPid}" ] && [ -n "${pluginPid}" ]; then
            echo -e "${InstallStart}"
        else
            echo -e "${InstallNoStart}"
        fi
    elif [ -e "${ssPath}" ]; then
        if [ -n "${ssPid}" ]; then
            echo -e "${InstallStart}"
        else
            echo -e "${InstallNoStart}"
        fi
    else
        echo -e "${NoInstall}"
    fi
}

check_sys(){
    local checkType=$1
    local value=$2

    local release=''
    local systemPackage=''

    if [ -f /etc/redhat-release ]; then
        release="centos"
        systemPackage="yum"
    elif grep -Eqi "debian|raspbian" /etc/issue; then
        release="debian"
        systemPackage="apt"
    elif grep -Eqi "ubuntu" /etc/issue; then
        release="ubuntu"
        systemPackage="apt"
    elif grep -Eqi "centos|red hat|redhat" /etc/issue; then
        release="centos"
        systemPackage="yum"
    elif grep -Eqi "debian|raspbian" /proc/version; then
        release="debian"
        systemPackage="apt"
    elif grep -Eqi "ubuntu" /proc/version; then
        release="ubuntu"
        systemPackage="apt"
    elif grep -Eqi "centos|red hat|redhat" /proc/version; then
        release="centos"
        systemPackage="yum"
    fi

    if [ "${checkType}" == "sysRelease" ]; then
        if [ "${value}" == "${release}" ]; then
            return 0
        else
            return 1
        fi
    elif [ "${checkType}" == "packageManager" ]; then
        if [ "${value}" == "${systemPackage}" ]; then
            return 0
        else
            return 1
        fi
    fi
}

check_arch(){
    case "$(uname -m)" in
      'amd64' | 'x86_64')
        ARCH='amd64'
        ;;
      'armv8' | 'aarch64')
        ARCH='arm64'
        ;;
      *)
        echo "[${Red}Error${suffix}] The architecture is not supported."
        exit 1
        ;;
    esac
}

package_install(){
    local package_name=$1
    
    if check_sys packageManager yum; then
        yum install -y $1 > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            _echo -e "安装 $1 失败."
            exit 1
        fi
    elif check_sys packageManager apt; then
        apt-get -y update > /dev/null 2>&1
        apt-get -y install $1 > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            _echo -e "安装 $1 失败."
            exit 1
        fi
    fi
    _echo -i "$1 安装完成."
}

improt_package(){
    local package=$1
    local sh_file=$2
    
    if [ ! "$(command -v curl)" ]; then
        package_install "curl" > /dev/null 2>&1
    fi
    
    if [ "${methods}" = "Online" ]; then
        source <(curl -sL "${BASE_URL}/${package}/${sh_file}")
    else
        cd "${CUR_DIR}"
        source "${BASE_URL}/${package}/${sh_file}"
    fi
}

disable_selinux(){
    if [ -s /etc/selinux/config ] && grep -q 'SELINUX=enforcing' /etc/selinux/config; then
        sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
        setenforce 0
    fi
}

install_check(){
    if check_sys packageManager yum || check_sys packageManager apt; then
        if centosversion 5; then
            return 1
        fi
        return 0
    else
        return 1
    fi
}

is_ipv4_or_ipv6(){
    ip=$1
    
    if [ -n "$(echo $ip | grep -E $IPV4_RE)" ] || [ -n "$(echo $ip | grep -E $IPV6_RE)" ]; then
        return 0
    else
        return 1
    fi
}

version_ge(){
    test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"
}

version_gt(){
    test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"
}

check_latest_version(){
    local current_v=$1
    local latest_v=$2
    if version_gt "${latest_v}" "${current_v}"; then
        return 0
    else
        return 1
    fi
}

check_port_occupy(){
    local port=$1
    
    if [ ! "$(command -v lsof)" ]; then
        package_install "lsof" > /dev/null 2>&1
    fi
    if [ `lsof -i:"${port}" | grep -v google_ | grep -v COMMAND | wc -l` -ne 0 ];then
        # Occupied
        return 0
    else
        # Unoccupied
        return 1
    fi
}

kill_process_if_port_occupy(){
    local port=$1

    while true
    do
        if ! check_port_occupy "${port}"; then
            break
        fi
        _echo -g "占用${port}端口的进程信息如下: "
        lsof -i:"${port}"
        _read "检测到${port}端口被占用，是否kill此进程(默认: n) [y/n]: "
        local yn="${inputInfo}"
        [ -z "${yn}" ] && yn="N"
        case "${yn:0:1}" in
            y|Y)
                lsof -i:"${port}" | grep -v grep | grep "${port}" | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1
                _echo -t "占用${port}端口的进程已被杀死."
                break
                ;;
            n|N)
                _echo -e "端口${port}被占用，终止运行."
                exit 1
                ;;
            *)
                _echo -e "输入有误，请重新输入!"
                continue
                ;;
        esac
    done
}

reset_if_ss_port_is_443(){
    while true
    do
        if [ "${shadowsocksport}" -ne 443 ]; then
            break
        fi
        gen_random_prot
        if check_port_occupy "${ran_prot}"; then
            continue
        fi
        shadowsocksport="${ran_prot}"
        break
    done
}

check_script_update(){
    local isShow="${1:-"show"}"

    SHELL_VERSION_NEW=$(wget --no-check-certificate -qO- "https://git.io/fjlbl"|grep 'SHELL_VERSION="'|awk -F "=" '{print $NF}'|sed 's/\"//g'|head -1)
    [ -z "${SHELL_VERSION_NEW}" ] && _echo -e "无法链接到 Github !" && exit 0
    if version_gt "${SHELL_VERSION_NEW}" "${SHELL_VERSION}"; then
        _echo -u "${Green}当前脚本版本为：${SHELL_VERSION} 检测到有新版本可更新.${suffix}"
        _echo -d "按任意键开始…或按Ctrl+C取消"
        char=`get_char`
        wget -N --no-check-certificate -O ss-plugins.sh "https://git.io/fjlbl" && chmod +x ss-plugins.sh
        echo -e "脚本已更新为最新版本[ ${SHELL_VERSION_NEW} ] !(注意：因为更新方式为直接覆盖当前运行的脚本，所以可能下面会提示一些报错，无视即可)" && exit 0
    else
        if [ "${isShow}" = "show" ]; then
            _echo -i "当前脚本版本为: ${SHELL_VERSION} 未检测到更新版本."
        fi
    fi
}

url_encode() {
    local length="${#1}"
    for (( i = 0; i < length; i++ )); do
        local c="${1:i:1}"
        case $c in
            [a-zA-Z0-9.~_-])
                printf "%s" "$c"
                ;;
            *)
                printf "%%%02X" "'$c"
                ;;
        esac
    done
}

get_ip(){
    local IP=$( ip addr | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | egrep -v "^192\.168|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-2]\.|^10\.|^127\.|^255\.|^0\." | head -n 1 )
    [ -z "${IP}" ] && IP=$( wget -qO- -t1 -T2 ipv4.icanhazip.com )
    [ -z "${IP}" ] && IP=$( wget -qO- -t1 -T2 ipinfo.io/ip )
    echo "${IP}"
}

get_ipv6(){
    local ipv6=$(wget -qO- -t1 -T2 ipv6.icanhazip.com)
    [ -z "${ipv6}" ] && return 1 || return 0
}

get_char(){
    SAVEDSTTY=$(stty -g)
    stty -echo
    stty cbreak
    dd if=/dev/tty bs=1 count=1 2> /dev/null
    stty -raw
    stty echo
    stty $SAVEDSTTY
}

get_domain_ip(){
    local domain=$1
    local ipv4Re="((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"

    if [ ! "$(command -v nslookup)" ]; then
        if check_sys packageManager yum; then
            package_install "bind-utils" > /dev/null 2>&1
        elif check_sys packageManager apt; then
            package_install "dnsutils" > /dev/null 2>&1
        fi
    fi

    domain_ip=`nslookup ${domain} | grep -E 'Name:' -A 1 | grep -oE $ipv4Re | tail -1`
    if [ -n "${domain_ip}" ]; then
        return 0
    else
        return 1
    fi
}

get_str_base64_encode(){
    echo -n $1 | base64 -w0
}

gen_random_prot(){
    ran_prot="$(shuf -i 9000-19999 -n 1)"
}

gen_random_str(){
    ran_str5="$(head -c 100 /dev/urandom | tr -dc a-z0-9A-Z | head -c 5)"
    ran_str12="$(head -c 100 /dev/urandom | tr -dc a-z0-9A-Z | head -c 12)"
}

gen_credentials(){
    while true
    do
        ckauid=$(ck-server -u)
        IFS=, read ckpub ckpv <<< $(ck-server -k)
        
        # filter "+" from ckauid and ckpub
        if [[ $(echo ${ckauid} | grep "+") || $(echo ${ckpub} | grep "+") ]]; then
            continue
        fi
        break
    done
}

get_version(){
    if [ -s /etc/redhat-release ]; then
        grep -oE  "[0-9.]+" /etc/redhat-release
    else
        grep -oE  "[0-9.]+" /etc/issue
    fi
}

get_env_variable(){
    local varName=$1
    local keyValuePair

    if [ -e "${SS_PLUGINS_SCRIPT_ENV_VARIABLES_FILE}" ]; then
        keyValuePair=$(grep "${varName}" "${SS_PLUGINS_SCRIPT_ENV_VARIABLES_FILE}")
        [ -n "${keyValuePair}" ] && export "${keyValuePair}" && return 0
    else
        return 1
    fi
}

write_env_variable(){
    local keyValuePairText=$1

    [ ! -e "${SS_PLUGINS_SCRIPT_ENV_DIR}" ] && mkdir -p "${SS_PLUGINS_SCRIPT_ENV_DIR}"
    echo "${keyValuePairText}" >> "${SS_PLUGINS_SCRIPT_ENV_VARIABLES_FILE}"
}

centosversion(){
    if check_sys sysRelease centos; then
        local code=$1
        local version="$(get_version)"
        local main_ver="${version%%.*}"
        if [ "$main_ver" == "$code" ]; then
            return 0
        else
            return 1
        fi
    else
        return 1
    fi
}

judge_is_num(){
    local aNum=$1

    expr "${aNum}" + 1 &>/dev/null
    if [ $? -ne 0 ]; then
        return 1
    else
        return 0
    fi
}

judge_is_zero_begin_num(){
    local aNum=$1

    if [ "${aNum:0:1}" = 0 ]; then
        return 0
    else
        return 1
    fi
}

judge_is_equal_num(){
    local numOne=$1
    local numTwo=$2

    if [ "${numOne}" -eq "${numTwo}" ]; then
        return 0
    else
        return 1
    fi
}

judge_num_in_range(){
    local aNum=$1
    local maxNum=$2

    if [ "${aNum}" -lt 1 ] || [ "${aNum}" -gt "${maxNum}" ]; then
        return 1
    else
        return 0
    fi
}

judge_is_nul_str(){
    local str=$1

    if [ -z "${str}" ]; then
        return 0
    else
        return 1
    fi
}

judge_is_domain(){
    local domain=$1

    if [ -z "$(echo ${domain} | grep -E ${DOMAIN_RE})" ]; then
        return 1
    else
        return 0
    fi
}

judge_is_valid_domain(){
    local domain=$1

    if judge_is_https_begin_site "${domain}"; then
        domain=$(echo ${domain} | sed 's/https:\/\///g')
    fi
    if get_domain_ip "${domain}"; then
        return 0
    else
        return 1
    fi
}

judge_is_ip_colon_port_format(){
    # Match: ip:443 format
    local ipColonPort=$1

    if [ -z "$(echo ${ipColonPort} | grep -E ${IPV4_PORT_RE})" ]; then
        return 1
    else
        return 0
    fi
}

judge_is_path(){
    local path=$1

    if [ -z "$(echo ${path} | grep -E '^\/(\w+\/?)+$')" ]; then
        return 1
    else
        return 0
    fi
}

judge_is_https_begin_site(){
    local domainWithProtocolHeader=$1

    if [ -z "$(echo ${domainWithProtocolHeader} | grep -E ${HTTPS_DOMAIN_RE})" ]; then
        return 1
    else
        return 0
    fi
}

judge_domain_type(){
    local ip=$1

    if is_cdn_proxied "${ip}"; then
        echo "CDN"
    elif is_dns_only "${ip}"; then
        echo "DNS-Only"
    else
        echo "Other"
    fi
}

generate_menu(){
    local aArray=($1)
    local num=$2
    local hint

    for ((i=1;i<="${#aArray[@]}";i++ )); do
        hint="${aArray[$i-1]}"
        if [ "${i}" -le 9 ]; then
            echo -e "${Green}  ${i}.${suffix} ${hint}"
        else
            echo -e "${Green} ${i}.${suffix} ${hint}"
        fi
    done
    _read "(默认: ${aArray[${num}-1]}):"
}

generate_menu_logic(){
    # global var: inputInfo, optionValue
    local aArray=($1)
    local tipText=$2
    local defualtOptNum=$3
    local arrayLong="${#aArray[@]}"

    while true
    do
        _echo "请选择${tipText}："
        generate_menu "${aArray[*]}" "${defualtOptNum}"
        [ -z "${inputInfo}" ] && inputInfo="${defualtOptNum}"
        if ! judge_is_num "${inputInfo}"; then
            _echo -e "请输入一个数字."
            continue
        fi
        if ! judge_num_in_range "${inputInfo}" "${arrayLong}"; then
            _echo -e "请输入一个数字在 [1-${arrayLong}] 之间"
            continue
        fi
        optionValue="${aArray[$inputInfo-1]}"
        _echo -r "  selected = ${optionValue}"
        break
    done
}

config_ss(){
    local ipv6First="false"
    local server_value="\"0.0.0.0\""

    if get_ipv6; then
        local ssVer="${SS_VERSION}"
        local pluginNum="${plugin_num}"

        if [ "${ssVer}" = "ss-libev" ]; then
            server_value="[\"[::0]\",\"0.0.0.0\"]"
            case "${pluginNum}" in
                8|9|12)
                    server_value="\"0.0.0.0\""
                    ;;
            esac
        elif [ "${ssVer}" = "ss-rust" ]; then
            server_value="\"::\""
            case "${pluginNum}" in
                3)
                    server_value="\"0.0.0.0\""
                    ;;
            esac
        fi

        case "${pluginNum}" in
            4|6|7)
                server_value="\"0.0.0.0\""
                ;;
        esac

        if [ "${server_value}" != "\"0.0.0.0\"" ]; then
            ipv6First="true"
        fi
    fi

    if [ ! -d "$(dirname ${SHADOWSOCKS_CONFIG})" ]; then
        mkdir -p $(dirname ${SHADOWSOCKS_CONFIG})
    fi
    case "${plugin_num}" in
        1)
            improt_package "templates/config" "v2ray_plugin_config.sh"
            config_ss_v2ray_plugin
            ;;
        2)
            improt_package "templates/config" "kcptun_config.sh"
            config_ss_kcptun
            ;;
        3)
            improt_package "templates/config" "simple_obfs_config.sh"
            config_ss_simple_obfs
            ;;
        4)
            improt_package "templates/config" "goquiet_config.sh"
            config_ss_goquiet
            ;;
        5)
            improt_package "templates/config" "cloak_config.sh"
            config_ss_cloak
            ;;
        6)
            improt_package "templates/config" "mos_tls_tunnel_config.sh"
            config_ss_mos_tls_tunnel
            ;;
        7)
            improt_package "templates/config" "rabbit_tcp_config.sh"
            config_ss_rabbit_tcp
            ;;
        8)
            improt_package "templates/config" "simple_tls_config.sh"
            config_ss_simple_tls
            ;;
        9)
            improt_package "templates/config" "gost_plugin_config.sh"
            config_ss_gost_plugin
            ;;
        10)
            improt_package "templates/config" "xray_plugin_config.sh"
            config_ss_xray_plugin
            ;;
        11)
            improt_package "templates/config" "qtun_config.sh"
            config_ss_qtun
            ;;
        12)
            improt_package "templates/config" "gun_config.sh"
            config_ss_gun
            ;;
        *)
            ss_server_config
            ;;
    esac
}

config_webserver(){
    case "${web_flag}" in
        1)
            improt_package "webServer" "caddy_config.sh"
            caddy_config 
            ;;
        2)
            improt_package "webServer" "nginx_config.sh"
            mirror_domain=$(echo ${mirror_site} | sed 's/https:\/\///g')
            nginx_config
            ;;
    esac
}

gen_ss_links(){
    case "${plugin_num}" in
        1)
            improt_package "templates/sslinks" "v2ray_plugin_link.sh"
            gen_ss_v2ray_plugin_link
            ;;
        2)
            improt_package "templates/sslinks" "kcptun_link.sh"
            gen_ss_kcptun_link
            ;;
        3)
            improt_package "templates/sslinks" "simple_obfs_link.sh"
            gen_ss_simple_obfs_link
            ;;
        4)
            improt_package "templates/sslinks" "goquiet_link.sh"
            gen_ss_goquiet_link
            ;;
        5)
            improt_package "templates/sslinks" "cloak_link.sh"
            gen_ss_cloak_link
            ;;
        6)
            improt_package "templates/sslinks" "mos_tls_tunnel_link.sh"
            gen_ss_mos_tls_tunnel_link
            ;;
        7)
            improt_package "templates/sslinks" "rabbit_tcp_link.sh"
            gen_ss_rabbit_tcp_link
            ;;
        8)
            improt_package "templates/sslinks" "simple_tls_link.sh"
            gen_ss_simple_tls_link
            ;;
        9)
            improt_package "templates/sslinks" "gost_plugin_link.sh"
            gen_ss_gost_plugin_link
            ;;
        10)
            improt_package "templates/sslinks" "xray_plugin_link.sh"
            gen_ss_xray_plugin_link
            ;;
        11)
            improt_package "templates/sslinks" "qtun_link.sh"
            gen_ss_qtun_link
            ;;
        12)
            improt_package "templates/sslinks" "gun_link.sh"
            gen_ss_gun_link
            ;;
        *)
            clientIpOrDomain="$(get_ip)"
            ss_client_links
            ;;
    esac
}

install_completed(){
    case "${plugin_num}" in
        1|3|4|6|7|8|9|10|11|12)
            ss_plugins_show
            ;;
        2)
            ss_kcptun_show
            ;;
        5)
            ss_cloak_show
            ;;
        *)
            ss_base_show
            ;;
    esac
}

whether_to_install_the_plugin(){
    while true
    do
        _read "要安装一个插件吗？(默认: n) [y/n]:"
        local yn="${inputInfo}"
        [ -z "${yn}" ] && yn="N"
        case "${yn:0:1}" in
            y|Y)
                isInstallPlugin=Yes
                ;;
            n|N)
                isInstallPlugin=No
                ;;
            *)
                _echo -e "输入有误，请重新输入."
                continue
                ;;
        esac
        _echo -r "  selected = ${isInstallPlugin}"
        break
    done
}

choose_ss_plugin(){
    generate_menu_logic "${SS_PLUGINS_NAME[*]}" "Shadowsocks插件" "1"
    plugin_num="${inputInfo}" 
    case "${plugin_num}" in
        1)
            improt_package "prepare" "v2ray_plugin_prepare.sh"
            install_prepare_libev_v2ray
            ;;
        2)
            improt_package "prepare" "kcptun_prepare.sh"
            install_prepare_libev_kcptun
            ;;
        3)
            improt_package "prepare" "simple_obfs_prepare.sh"
            install_prepare_libev_obfs
            ;;
        4)
            improt_package "prepare" "goquiet_prepare.sh"
            install_prepare_libev_goquiet
            ;;
        5)
            improt_package "prepare" "cloak_prepare.sh"
            install_prepare_libev_cloak
            ;;
        6)
            improt_package "prepare" "mos_tls_tunnel_prepare.sh"
            install_prepare_libev_mos_tls_tunnel
            ;;
        7)
            improt_package "prepare" "rabbit_tcp_prepare.sh"
            install_prepare_libev_rabbit_tcp
            ;;
        8)
            improt_package "prepare" "simple_tls_prepare.sh"
            install_prepare_libev_simple_tls
            ;;
        9)
            improt_package "prepare" "gost_plugin_prepare.sh"
            install_prepare_libev_gost_plugin
            ;;
        10)
            improt_package "prepare" "xray_plugin_prepare.sh"
            install_prepare_libev_xray_plugin
            ;;
        11)
            improt_package "prepare" "qtun_prepare.sh"
            install_prepare_libev_qtun
            ;;
        12)
            improt_package "prepare" "gun_prepare.sh"
            install_prepare_libev_gun
            ;;
    esac
}

install_prepare(){
    check_script_update "notShow"
    improt_package "prepare" "shadowsocks_prepare.sh"
    choose_ss_install_version
    install_prepare_port
    install_prepare_password
    install_prepare_cipher
    serverTcpAndUdp="tcp_and_udp"
    firewallNeedOpenPort="${shadowsocksport}"
    whether_to_install_the_plugin
    if [ "${isInstallPlugin}" = "No" ]; then
        _echo -t "当前未选择任何插件，仅安装${SS_VERSION}."
    elif [ "${isInstallPlugin}" = "Yes" ]; then
        choose_ss_plugin
    fi
    echo
    echo "按任意键开始…或按Ctrl+C取消"
    char=`get_char`
}

install_main(){
    if [ "${SS_VERSION}" = "ss-libev" ]; then
        install_libsodium_logic
        if ! ldconfig -p | grep -wq "/usr/lib"; then
            echo "/usr/lib" > /etc/ld.so.conf.d/lib.conf
        fi
        ldconfig
        install_mbedtls_logic
    fi
    improt_package "install" "shadowsocks_install.sh"
    case "${SS_VERSION}" in
        "ss-libev")
            install_shadowsocks_libev
        ;;
        "ss-rust")
            install_shadowsocks_rust
        ;;
        "go-ss2")
            install_go_shadowsocks2
        ;;
    esac
    case "${plugin_num}" in
        1)
            improt_package "install" "v2ray_plugin_install.sh"
            install_v2ray_plugin
            serverPluginName="v2ray-plugin"
            clientPluginName="v2ray-plugin"
            ;;
        2)
            improt_package "install" "kcptun_install.sh"
            install_kcptun
            serverPluginName="kcptun-server"
            clientPluginName="kcptun"
            ;;
        3)
            improt_package "install" "simple_obfs_install.sh"
            install_simple_obfs
            serverPluginName="obfs-server"
            clientPluginName="obfs-local"
            ;;
        4)
            improt_package "install" "goquiet_install.sh"
            install_goquiet
            serverPluginName="gq-server"
            clientPluginName="gq-client"
            ;;
        5)
            improt_package "install" "cloak_install.sh"
            install_cloak
            gen_credentials
            serverPluginName="ck-server"
            clientPluginName="ck-client"
            ;;
        6)
            improt_package "install" "mos_tls_tunnel_install.sh"
            install_mos_tls_tunnel
            serverPluginName="mtt-server"
            clientPluginName="mostlstunnel"
            ;;
        7)
            improt_package "install" "rabbit_tcp_install.sh"
            install_rabbit_tcp
            serverPluginName="rabbit-tcp"
            clientPluginName="rabbit-plugin"
            ;;
        8)
            improt_package "install" "simple_tls_install.sh"
            install_simple_tls
            serverPluginName="simple-tls"
            clientPluginName="simple-tls"
            ;;
        9)
            improt_package "install" "gost_plugin_install.sh"
            install_gost_plugin
            serverPluginName="gost-plugin"
            clientPluginName="gost-plugin"
            ;;
        10)
            improt_package "install" "xray_plugin_install.sh"
            install_xray_plugin
            serverPluginName="xray-plugin"
            clientPluginName="xray-plugin"
            ;;
        11)
            improt_package "install" "qtun_install.sh"
            install_qtun
            serverPluginName="qtun-server"
            clientPluginName="qtun-client"
            ;;
        12)
            improt_package "install" "gun_install.sh"
            install_gun
            serverPluginName="gun-server"
            clientPluginName="gun-client"
            ;;
    esac
}

install_webserver(){
    case "${web_flag}" in
        1)
            improt_package "webServer" "caddy_install.sh"
            install_caddy
            touch "${WEB_INSTALL_MARK}"
        ;;
        2)
            improt_package "webServer" "nginx_install.sh"
            install_nginx
            touch "${WEB_INSTALL_MARK}"
        ;;
    esac
}

gen_random_minute(){
    local _t="$(date -u "+%s")"
    echo "$(($_t % 60))"
}

certificate_renew_by_acme(){
    if ! check_port_occupy "80"; then
        return
    fi
    do_stop > /dev/null
    "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
    do_start > /dev/null
}

add_cron_job_for_acme(){
    random_minute="$(gen_random_minute)"
    (crontab -l ; echo "${random_minute} 0 * * * \"/root/.acme.sh\"/acme.sh --cron --home \"/root/.acme.sh\" > /dev/null") | sort - | uniq - | crontab -
}

remove_cron_job_for_acme(){
    if [ -z "$(crontab -l | grep 'acme.sh --cron')" ]; then
        return
    fi
    crontab -l | sed '/acme.sh --cron/d' | crontab -
}

add_cron_job(){
    local random_minute

    if ! check_port_occupy "80"; then
        return
    fi
    remove_cron_job_for_acme
    random_minute="$(gen_random_minute)"
    (crontab -l ; echo "${random_minute} 0 * * * \"${CUR_DIR}\"/ss-plugins.sh renew > /dev/null") | sort - | uniq - | crontab -
}

sync_time(){
    if [ "${CipherMark}" = "Non-AEAD-2022" ]; then
        return
    fi
    _echo -i "开始同步时间.."
    if [ "$(command -v ntpdate)" ]; then
        ntpdate pool.ntp.org
        [ $? -eq 0 ] && _echo -i "现在时间: $(date -R) 同步完成..."
    elif [ "$(command -v chronyc)" ]; then
        chronyc -a makestep
        [ $? -eq 0 ] && _echo -i "现在时间: $(date -R) 同步完成..."
    fi
}

install_status(){
    status_init

    if [ -e "${ssPath}" ] && [ -e "${pluginPath}" ] && [ -e "${webPath}" ]; then
        echo -e "\n${Info} ${ssName} ${pluginName} ${webName} 已经安装.\n"
        exit 1
    elif [ -e "${ssPath}" ] && [ -e "${pluginPath}" ]; then
        echo -e "\n${Info} ${ssName} ${pluginName} 已经安装.\n"
        exit 1
    elif [ -e "${ssPath}" ]; then
        echo -e "\n${Info} ${ssName} 已经安装.\n"
        exit 1
    fi
}

install_step_all(){
    install_status
    disable_selinux
    install_prepare
    TEMP_DIR_PATH=$(mktemp -d)
    trap "rm -rf $TEMP_DIR_PATH; exit" 2
    improt_package "utils" "dependencies.sh"
    install_dependencies_logic
    improt_package "utils" "downloads.sh"
    download_ss_file
    download_plugins_file
    improt_package "utils" "firewalls.sh"
    config_firewall_logic
    install_main
    install_webserver
    sync_time
    add_more_entropy
    install_cleanup
    improt_package "templates" "config.sh"
    config_ss
    config_webserver
    gen_ss_links
    install_completed
    ldconfig
    do_start
    add_cron_job
    do_show
}

install_cleanup(){
    cd "${CUR_DIR}"
    rm -rf  ${TEMP_DIR_PATH}
}

do_uid(){
    improt_package "utils" "ck_user_manager.sh"
    user_manager_by_uid
}

do_link(){
    improt_package "utils" "ck_sslink.sh"
    gen_ssurl_by_uid "$1"
}

do_scan(){
    improt_package "utils" "qr_code.sh"
    gen_qr_code "$1"
}

do_show(){
    local mark=$1

    if [ ! -e "${HUMAN_CONFIG}" ]; then
        echo "The visible config not found."
        exit 1
    fi
    if [ "${mark}" = "cleanScreen" ]; then
        clear -x
    fi
    cat "${HUMAN_CONFIG}"
}

do_log(){
    improt_package "utils" "view_log.sh"
    show_log
}

do_cert(){
    improt_package "utils" "gen_certificates.sh"
    acme_get_certificate_by_manual_force "$1"
}

do_catcfg(){
    improt_package "utils" "view_config.sh"
    view_config_logic
}

do_start(){
    status_init
    if [ -z "${ssPath}" ]; then
        _echo -r "Shadowsocks未安装，请尝试安装后，再来执行此操作."
        exit 1
    fi
    improt_package "utils" "start.sh"
    start_services
}

do_stop(){
    improt_package "utils" "stop.sh"
    stop_services
}

do_restart(){
    do_stop
    do_start
}

do_status(){
    improt_package "utils" "status.sh"
    other_status
}

do_update(){
    cd "${CUR_DIR}"
    improt_package "utils" "update.sh"
    update_logic
}

do_uninstall(){
    status_init
    if [ -e "${ssPath}" ] && [ -e "${pluginPath}" ] && [ -e "${webPath}" ]; then
        local pkgName="${ssName} ${pluginName} ${webName}"
    elif [ -e "${ssPath}" ] && [ -e "${pluginPath}" ]; then
        local pkgName="${ssName} ${pluginName}"
    elif [ -e "${ssPath}" ]; then
        local pkgName="${ssName}"
    else
        local pkgName="Shadowsocks"
    fi
    _read "你确定要卸载${pkgName}吗?(默认: n) [y/n]"
    local yn="${inputInfo}"
    [ -z "${yn}" ] && yn="N"
    case "${yn:0:1}" in
        y|Y)
            improt_package "utils" "uninstall.sh"
            uninstall_services
            _echo -i "${pkgName} 卸载成功."
            ;;
        *)
            _echo -i "${pkgName} 卸载取消."
            exit 1
            ;;
    esac
}

do_install(){
    local mark

    if ! install_check; then
        echo -e "[${Red}Error${suffix}] Your OS is not supported to run it!"
        echo "Please change to CentOS 6+/Debian 7+/Ubuntu 12+ and try again."
        exit 1
    fi
    if [ -e "${SHADOWSOCKS_LIBEV_BIN_PATH}" ]; then
        mark="Shadowsocks-libev"
    elif [ -e "${SHADOWSOCKS_RUST_BIN_PATH}" ]; then
        mark="Shadowsocks-rust"
    elif [ -e "${GO_SHADOWSOCKS2_BIN_PATH}" ]; then
        mark="Go-shadowsocks2"
    else
        mark="Shadowsocks"
    fi
    echo -e " ${mark}一键脚本 ${Red}[v${SHELL_VERSION} ${methods}]${suffix}\n"
    echo -e "    ${Green}1.${suffix} BBR"
    echo -e "    ${Green}2.${suffix} Install"
    echo -e "    ${Green}3.${suffix} Uninstall\n"
    status_menu
    _read -u "请输入数字 (默认: 2)："
    menu_num="${inputInfo}"
    [ -z "${menu_num}" ] && menu_num=2
    case "${menu_num}" in
        1)   
            source <(curl -sL "${TEDDYSUN_BBR_SCRIPT_URL}")
            ;;
        2)
            install_step_all
            ;;
        3)
            do_uninstall
            ;;
        *)
            _echo -e "请输入正确的数字 [1-3]"
            ;;
    esac
}



# install and tools
check_arch
action=${1:-"install"}

case ${action} in
    install|uninstall|update|start|stop|restart)
        do_${action}
        ;;
    status)
        do_${action}
        ;;
    script)
        check_script_update
        ;;
    uid)
        do_${action} 
        ;;
    link)
        do_${action} "${2}"
        ;;
    scan)
        do_${action} "${2}"
        ;;
    show)
        do_${action} "cleanScreen"
        ;;
    log)
        do_${action}
        ;;
    cert)
        do_${action} "${2}"
        ;;
    catcfg)
        do_${action} "${2}"
        ;;
    renew)
        certificate_renew_by_acme
        ;;
    help)
        usage 0
        ;;
    *)
        usage 1
        ;;
esac