#!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH # Add domain name and email CONTAINER_PROJECT_NAME=billionmail PGSQL_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-pgsql-billionmail-1" DOVECOT_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-dovecot-billionmail-1" POSTFIX_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-postfix-billionmail-1" RSPAMD_CONTAINER_NAME="${CONTAINER_PROJECT_NAME}-rspamd-billionmail-1" create_time=$(date +%s) time=$(date +%Y_%m_%d_%H_%M_%S) if [ $(whoami) != "root" ];then echo -e "Non-root install, please try the following solutions: \n 1.Please switch to root user install \n 2.Try executing the following install commands: \n sudo bash $0 $@" exit 1 fi PWD_d=`pwd` SWITCH_TO_BILLIONMAIL_DIR(){ if [ -f "/opt/PWD-Billion-Mail.txt" ]; then DIR=$(cat /opt/PWD-Billion-Mail.txt) if [ -d "${DIR}" ]; then cd "${DIR}" echo "Enter the BillionMail project directory: ${DIR}" fi fi } if [ -s ".env" ]; then CHECK_BILLIONMAIL=$(grep "BILLIONMAIL_HOSTNAME" .env) if [ -z "${CHECK_BILLIONMAIL}" ]; then SWITCH_TO_BILLIONMAIL_DIR fi else SWITCH_TO_BILLIONMAIL_DIR fi if [ ! -s ".env" ]; then ls -al echo " The .env file does not exist. Cannot continue operation, please operate in the BillionMail project directory" exit 1 fi source .env if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then echo "The obtained .env configuration exception is empty." exit 1 fi Red_Error(){ echo '================================================='; printf '\033[1;31;40m%b\033[0m\n' "$@"; exit 1; } Command_Exists() { command -v "$@" >/dev/null 2>&1 } Docker_Compose_Check(){ if Command_Exists docker-compose ; then DOCKER_COMPOSE="docker-compose" else if Command_Exists docker; then Docker_compose="docker compose version" if $Docker_compose >/dev/null 2>&1; then DOCKER_COMPOSE="docker compose" fi else Red_Error "ERROR: Docker Compose does not exist" fi fi } Docker_Compose_Check GET_SERVICE_NAME() { # ALL_SERVICE_NAME=$(${DOCKER_COMPOSE} config --services |grep "${SERVICE}") # echo "${ALL_SERVICE_NAME}" if [[ "${SERVICE}" == "core" ]] || [[ "${SERVICE}" == "manage" ]]; then #echo "Getting the "${SERVICE}" service..." SERVICE_NAME=$(${DOCKER_COMPOSE} ps -a --format " {{.Service}} {{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) elif [[ "${SERVICE}" == "postfix" ]] || [[ "${SERVICE}" == "postfix-billionmail" ]]; then SERVICE_NAME="postfix-billionmail" elif [[ "${SERVICE}" == "dovecot" ]] || [[ "${SERVICE}" == "dovecot-billionmail" ]]; then SERVICE_NAME="dovecot-billionmail" elif [[ "${SERVICE}" == "rspamd" ]] || [[ "${SERVICE}" == "rspamd-billionmail" ]]; then SERVICE_NAME="rspamd-billionmail" elif [[ "${SERVICE}" == "pgsql" ]] || [[ "${SERVICE}" == "postgres" ]] || [[ "${SERVICE}" == "pgsql-billionmail" ]]; then SERVICE_NAME="pgsql-billionmail" elif [[ "${SERVICE}" == "redis" ]] || [[ "${SERVICE}" == "redis-billionmail" ]]; then SERVICE_NAME="redis-billionmail" elif [[ "${SERVICE}" == "webmail" ]] || [[ "${SERVICE}" == "roundcube" ]] || [[ "${SERVICE}" == "webmail-billionmail" ]]; then SERVICE_NAME="webmail-billionmail" else echo "Please use: core|postfix|dovecot|rspamd|pgsql|redis|webmail" Red_Error "ERROR: The "${SERVICE}" service does not exist" fi if [ -z "${SERVICE_NAME}" ]; then Red_Error "ERROR: The "${SERVICE}" service does not exist" fi } GET_CONTAINER_ID() { if [ -z "${CONTAINER}" ]; then CONTAINER="$1" fi if [[ "${CONTAINER}" == "core" ]] || [[ "${CONTAINER}" == "manage" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "postfix" ]] || [[ ""${CONTAINER}"" == "postfix-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/postfix:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "dovecot" ]] || [[ ""${CONTAINER}"" == "dovecot-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/dovecot:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "rspamd" ]] || [[ ""${CONTAINER}"" == "rspamd-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/rspamd:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "pgsql" ]] || [[ ""${CONTAINER}"" == "postgres" ]] || [[ ""${CONTAINER}"" == "pgsql-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "postgres:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "redis" ]] || [[ ""${CONTAINER}"" == "redis-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "redis:" | awk '{print $1}' ) elif [[ ""${CONTAINER}"" == "webmail" ]] || [[ ""${CONTAINER}"" == "roundcube" ]] || [[ ""${CONTAINER}"" == "webmail-billionmail" ]]; then CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "roundcubemail:" | awk '{print $1}' ) else echo "Please use: core|postfix|dovecot|rspamd|pgsql|redis|webmail" Red_Error "ERROR: The ""${CONTAINER}"" container does not exist" fi } Init_Email() { input="$2" # Regular expression verification email address format if [[ "$input" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then mailbox=$(echo "$input" | cut -d '@' -f 1 | tr '[:upper:]' '[:lower:]') BILLIONMAIL_HOSTNAME=$(echo "$input" | cut -d '@' -f 2 | tr '[:upper:]' '[:lower:]') else echo "Enter an email address that is not a legal one: $input" echo "Example: user@example.com" exit 1 fi } Init_Domain() { BILLIONMAIL_HOSTNAME="$2" # Regular expression check domain name format # if [[ ! "${BILLIONMAIL_HOSTNAME}" =~ ^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*$ ]]; then if [[ ! "${BILLIONMAIL_HOSTNAME}" =~ ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,62}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]]; then echo -e "\e[31m(${BILLIONMAIL_HOSTNAME}) is not a FQDN!\e[0m" echo "Please change it to a FQDN" exit 1 elif [[ "${BILLIONMAIL_HOSTNAME: -1}" == "." ]]; then echo "(${BILLIONMAIL_HOSTNAME}) is ending with a dot. This is not a valid FQDN!" exit 1 fi # Convert to lowercase BILLIONMAIL_HOSTNAME=$(echo "${BILLIONMAIL_HOSTNAME}" | tr '[:upper:]' '[:lower:]') } Domain_DKIM_record(){ if [ -z "${BILLIONMAIL_HOSTNAME}" ]; then echo "Please enter the domain name" exit 1 fi Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_domain}" ]; then echo "Error: ${BILLIONMAIL_HOSTNAME} Domain does not exist." exit 1 fi ## DKIM key generation docker exec -i -e BILLIONMAIL_HOSTNAME=${BILLIONMAIL_HOSTNAME} ${RSPAMD_CONTAINER_NAME} bash -c 'cat << "EOF" > /tmp/1.sh #!/bin/bash if [ ! -d "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/" ]; then mkdir -p "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/" fi if [ -f "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.private" ] && [ -f "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub" ]; then #echo "DKIM key already exists, skipping generation." exit 0 fi rspamadm dkim_keygen -s 'default' -b 1024 -d {domain} -k "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.private" > "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub" if [ $? -eq 0 ]; then # Define the root directory for private keys DKIM_KEYS_DIR="/var/lib/rspamd/dkim" # Configuration file path CONFIG_FILE="/etc/rspamd/local.d/dkim_signing.conf" # Check if the configuration file exists, if not, create it if [ ! -f "${CONFIG_FILE}" ]; then touch "${CONFIG_FILE}" echo "${CONFIG_FILE}" echo "domain {" > "${CONFIG_FILE}" echo "#BT_DOMAIN_DKIM_BEGIN" >> "${CONFIG_FILE}" echo "#BT_DOMAIN_DKIM_END" >> "${CONFIG_FILE}" echo "}" >> "${CONFIG_FILE}" fi # Scan the DKIM_KEYS_DIR directory to get all domains DOMAINS=() for DOMAIN_DIR in "${DKIM_KEYS_DIR}"/*; do if [ -d "${DOMAIN_DIR}" ]; then DOMAIN_NAME=$(basename "${DOMAIN_DIR}") PRIVATE_KEY_PATH="${DOMAIN_DIR}/default.private" if [ -f "${PRIVATE_KEY_PATH}" ]; then DOMAINS+=("${DOMAIN_NAME}:${PRIVATE_KEY_PATH}") else echo "Warning: Private key file ${PRIVATE_KEY_PATH} for domain ${DOMAIN_NAME} does not exist, skipping configuration." fi fi done # Add domain configurations for DOMAIN in "${DOMAINS[@]}"; do DOMAIN_NAME=$(echo "${DOMAIN}" | cut -d':' -f1) PRIVATE_KEY_PATH=$(echo "${DOMAIN}" | cut -d':' -f2) # Check if the domain already exists if grep -wq "${DOMAIN_NAME}" "${CONFIG_FILE}"; then # echo "Domain ${DOMAIN_NAME} already exists, skipping configuration." continue fi # Insert domain configuration between #BT_DOMAIN_DKIM_BEGIN and #BT_DOMAIN_DKIM_END sed -i "/^#BT_DOMAIN_DKIM_BEGIN$/a #${DOMAIN_NAME}_DKIM_BEGIN\n ${DOMAIN_NAME} {\n selectors [\n {\n path: \"${PRIVATE_KEY_PATH}\";\n selector: \"default\"\n }\n ]\n }\n#${DOMAIN_NAME}_DKIM_END" "${CONFIG_FILE}" # echo "Domain ${DOMAIN_NAME} has been successfully added to the configuration file." done # echo "DKIM configuration update completed." else echo -e "DKIM key generation failed!" exit 1 fi chmod 755 -R "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/" EOF' docker exec -i -e BILLIONMAIL_HOSTNAME=${BILLIONMAIL_HOSTNAME} ${RSPAMD_CONTAINER_NAME} bash /tmp/1.sh && rm -f /tmp/1.sh DKIM_RECORD=$(docker exec ${RSPAMD_CONTAINER_NAME} cat "/var/lib/rspamd/dkim/${BILLIONMAIL_HOSTNAME}/default.pub") # echo "DKIM RECORD: ${DKIM_RECORD}" } Domain_record() { Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}") if [ "${Check_domain}" ]; then IPV4_ADDRESS=$(curl -sS -4 --connect-timeout 10 -m 20 https://ifconfig.me) if [ -z "${IPV4_ADDRESS}" ]; then IPV4_ADDRESS=$(curl -sSk --connect-timeout 10 -m 20 https://www.aapanel.com/api/common/getClientIP) fi ipv4_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$" if [[ ${IPV4_ADDRESS} =~ ${ipv4_regex} ]]; then echo "${IPV4_ADDRESS}" >/dev/null 2>&1 elif [ -z "${IPV4_ADDRESS}" ]; then IPV4_ADDRESS="YOUR_SERVER_IPV4_ADDRESS" fi echo -e "" echo -e "\e[31mPlease add the following record to your domain name\e[0m" echo -e "===========================================================" echo -e " Type | Host record | IPv4 address |" echo -e " \e[1;33mA\e[0m | \e[1;33mmail.${BILLIONMAIL_HOSTNAME}\e[0m | \e[1;33m${IPV4_ADDRESS}\e[0m |" echo -e "===========================================================" echo -e " Type | Host record | MX priority | Record value " echo -e " \e[1;33mMX\e[0m | \e[1;33m@\e[0m | \e[1;33m10\e[0m | \e[1;33mmail.${BILLIONMAIL_HOSTNAME}\e[0m " echo -e "===========================================================" if [ "${IPV4_ADDRESS}" ]; then echo -e " Type | Host record | Record value |" echo -e " \e[1;33mTXT\e[0m | \e[1;33m@\e[0m | \e[1;33mv=spf1 +a +mx +ip4:${IPV4_ADDRESS} -all\e[0m |" else echo -e " Type | Host record | Record value |" echo -e " \e[1;33mTXT\e[0m | \e[1;33m@\e[0m | \e[1;33mv=spf1 +a +mx -all\e[0m |" fi echo -e "===========================================================" echo -e " Type | Host record | Record value |" echo -e " \e[1;33mTXT\e[0m | \e[1;33m_dmarc\e[0m | \e[1;33mv=DMARC1;p=quarantine;rua=mailto:admin@${BILLIONMAIL_HOSTNAME}\e[0m |" echo -e "===========================================================" Domain_DKIM_record if [ "${DKIM_RECORD}" ]; then DKIM_RECORD=$(echo "${DKIM_RECORD}" | awk -F'"' '{print $2 $4}' | tr -d '[:space:]') #echo "DKIM RECORD: ${DKIM_RECORD}" echo -e " Type | Host record | Record value |" echo -e " \e[1;33mTXT\e[0m | \e[1;33mdefault._domainkey\e[0m | \e[1;33m${DKIM_RECORD}\e[0m |<-- Start from \"v=DKIM1\" end, A single line." echo -e "===========================================================" else echo -e "${BILLIONMAIL_HOSTNAME} DKIM key generation failed!" fi else echo -e "Domain ${BILLIONMAIL_HOSTNAME} does not exist." fi } Select_Domain_data(){ # Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain;") # echo "${Check_domain}" Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT domain FROM domain;") echo "${Check_domain}" } Select_Email_data(){ Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT username FROM mailbox;") echo "${Check_mailbox}" } Add_Domain() { echo "Creating domain..." Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_domain}" ]; then # Create a domain docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "INSERT INTO domain (domain, a_record, mailboxes, mailbox_quota, quota, rate_limit, create_time, active) VALUES ('${BILLIONMAIL_HOSTNAME}', 'mail.${BILLIONMAIL_HOSTNAME}', 500, 5368709120, 5368709120, 12, ${create_time}, 1);" if [ $? -eq 0 ]; then echo "${BILLIONMAIL_HOSTNAME} Domain creation was successful!" Domain_record else Red_Error "Domain creation failed!" fi else echo ""${Check_domain}" Domain already exists!" fi } Del_Domain() { echo "Deleting domain..." Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_domain}" ]; then echo "Domain '${BILLIONMAIL_HOSTNAME}' does not exist!" else # Delete the domain docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "DELETE FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" if [ $? -eq 0 ]; then echo "Domain '${BILLIONMAIL_HOSTNAME}' deleted successfully!" else Red_Error "Failed to delete domain '${BILLIONMAIL_HOSTNAME}'!" fi fi } Add_Email() { echo "Creating mailbox..." Check_domain=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM domain WHERE domain = '${BILLIONMAIL_HOSTNAME}';" | grep -w "^ ${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_domain}" ]; then Red_Error "Domain '${BILLIONMAIL_HOSTNAME}' does not exist, please create it first!" fi # Create a mailbox if [ -z "${mailbox}" ]; then mailbox=$(LC_ALL=C /dev/null | head -c 6) echo "Generate mailbox: ${mailbox}" else mailbox=$(echo "${mailbox}" | tr '[:upper:]' '[:lower:]') fi Generate_mailbox_password=$(LC_ALL=C /dev/null | head -c 16) #echo "Generate_mailbox_password: ${Generate_mailbox_password}" Encrypt_mailbox_password=$(docker exec -i ${DOVECOT_CONTAINER_NAME} doveadm pw -s MD5-CRYPT -p "${Generate_mailbox_password}" | sed 's/{MD5-CRYPT}//') if [ $? -eq 0 ]; then echo "Generate_mailbox_password: ${Generate_mailbox_password}" echo "mailbox_password: ${Encrypt_mailbox_password}" else # Generate the default password after failure: BILLIONMAIL Generate_mailbox_password="BILLIONMAIL" Encrypt_mailbox_password='$1$ELBUCcYE$TbdGKBvLkFbjQguDbi3s01' echo "Generate_mailbox_password--default: ${Generate_mailbox_password}" Default_password=1 fi if [ "${Default_password}" != 1 ]; then # password_encode # Base64 encoding of strings b64_data=$(echo -n "${Generate_mailbox_password}" | base64) # Convert characters to hexadecimal password_encode=$(echo -n "${b64_data}" | while IFS= read -r -n1 char; do printf "%02x" "'$char"; done) echo "password_encode: ${password_encode}" if [ -z ${password_encode} ]; then b64_data=$(echo -n "${Generate_mailbox_password}" | base64) password_encode=$(echo -n "${b64_data}" | od -A n -t x1 | tr -d ' \n') echo "password_encode: ${password_encode}" fi else password_encode="516b6c4d54456c50546b31425355773d" fi Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" | grep -w "${mailbox}@${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_mailbox}" ]; then INSERT_mailbox='INSERT INTO mailbox (username, password, password_encode, full_name, is_admin, maildir, quota, local_part, domain, create_time, update_time, active) VALUES ( '\'${mailbox}@${BILLIONMAIL_HOSTNAME}\'', '\'${Encrypt_mailbox_password}\'', '\'${password_encode}\'', '\'${mailbox}\'', 0, '\'${mailbox}@${BILLIONMAIL_HOSTNAME}/\'', 5368709120, '\'${mailbox}\'', '\'${BILLIONMAIL_HOSTNAME}\'', '${create_time}', '${create_time}', 1 );' echo "$INSERT_mailbox" docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "$INSERT_mailbox" if [ $? -eq 0 ]; then echo "Mailbox creation was successful!" else Red_Error "Mailbox creation failed!" fi #docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" else echo ""${Check_mailbox}" Mailbox already exists!" fi echo -e "\e[31mPlease save the following information:\e[0m" echo -e "Mailbox (e-mail): \e[33m${mailbox}@${BILLIONMAIL_HOSTNAME}\e[0m \nPassword: \e[33m${Generate_mailbox_password}\e[0m" } Del_Email() { echo "Deleting mailbox..." Check_mailbox=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT * FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" | grep -w "${mailbox}@${BILLIONMAIL_HOSTNAME}") if [ -z "${Check_mailbox}" ]; then echo "Mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}' does not exist!" else docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "DELETE FROM mailbox WHERE username = '${mailbox}@${BILLIONMAIL_HOSTNAME}';" if [ $? -eq 0 ]; then echo "Mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}' deleted successfully!" else Red_Error "Failed to delete mailbox '${mailbox}@${BILLIONMAIL_HOSTNAME}'!" fi fi } Update_BillionMail() { BRANCH="main" if [ -f "update.sh" ]; then echo -e "Checking for update.sh script..." MD5_1="$(md5sum update.sh)" git fetch origin git checkout "origin/${BRANCH}" update.sh MD5_2=$(md5sum update.sh) if [[ "${MD5_1}" != "${MD5_2}" ]]; then #echo -e "\033[33m update.sh is changed, please run update.sh script again.\033[0m" chmod +x update.sh echo y | ./update.sh #exit 1 else chmod +x update.sh echo y | ./update.sh fi else Red_Error "Error: update.sh script does not exist!" fi } Default_info() { ipv4_address="" ipv6_address="" if [ "$address" = "" ];then ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://ifconfig.me 2>&1) if [ -z "${ipv4_address}" ];then ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://www.aapanel.com/api/common/getClientIP 2>&1) if [ -z "${ipv4_address}" ];then ipv4_address=$(curl -4 -sSf --connect-timeout 10 -m 15 https://www.bt.cn/Api/getIpAddress 2>&1) fi fi IPV4_REGEX="^([0-9]{1,3}\.){3}[0-9]{1,3}$" if ! [[ $ipv4_address =~ $IPV4_REGEX ]]; then ipv4_address="" fi ipv6_address=$(curl -6 -sSf --connect-timeout 10 -m 15 https://ifconfig.me 2>&1) IPV6_REGEX="^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$" if ! [[ $ipv6_address =~ $IPV6_REGEX ]]; then ipv6_address="" else if [[ ! $ipv6_address =~ ^\[ ]]; then ipv6_address="[$ipv6_address]" fi fi if [ "$address" = "" ] && [ "$ipv4_address" = "" ] && [ "$ipv6_address" = "" ];then address="SERVER_IP" echo -e "\033[33mFailed to obtain Internet IP, please use the server Internet IP+PORT to access.\033[0m" fi fi LOCAL_IP=$(ip addr | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -E -v "^127\.|^255\.|^0\." | head -n 1) echo -e "==================================================================" echo -e "\033[32mBillionMail default info!\033[0m" echo -e "==================================================================" pool=https if [ -f "core-data/billionmail_hostname.txt" ];then BILLIONMAIL_Domain=$(cat core-data/billionmail_hostname.txt) if [ "${HTTPS_PORT}" = "443" ];then echo "BillionMail Domain Address: ${pool}://${BILLIONMAIL_Domain}/${SafePath}" else echo "BillionMail Domain Address: ${pool}://${BILLIONMAIL_Domain}:${HTTPS_PORT}/${SafePath}" fi fi if [ "${ipv6_address}" ];then if [ "${HTTPS_PORT}" = "443" ];then echo "BillionMail Internet IPv6 Address: ${pool}://${ipv6_address}/${SafePath}" else echo "BillionMail Internet IPv6 Address: ${pool}://${ipv6_address}:${HTTPS_PORT}/${SafePath}" fi fi if [ "${ipv4_address}" ];then if [ "${HTTPS_PORT}" = "443" ];then echo "BillionMail Internet IPv4 Address: ${pool}://${ipv4_address}/${SafePath}" else echo "BillionMail Internet IPv4 Address: ${pool}://${ipv4_address}:${HTTPS_PORT}/${SafePath}" fi fi if [ "${address}" ];then if [ "${HTTPS_PORT}" = "443" ];then echo "BillionMail Internet Address: ${pool}://${address}/${SafePath}" else echo "BillionMail Internet Address: ${pool}://${address}:${HTTPS_PORT}/${SafePath}" fi fi if [ "${HTTPS_PORT}" = "443" ];then echo "BillionMail Internal Address: ${pool}://${LOCAL_IP}/${SafePath}" else echo "BillionMail Internal Address: ${pool}://${LOCAL_IP}:${HTTPS_PORT}/${SafePath}" fi echo -e "Username: ${ADMIN_USERNAME} \nPassword: ${ADMIN_PASSWORD}" echo -e "\033[33mWarning:\033[0m" echo -e "\033[33mIf you cannot access the BillionMail, \033[0m" echo -e "\033[33mrelease the following port ${SMTP_PORT}|${SMTPS_PORT}|${SUBMISSION_PORT}|${POP_PORT}|${IMAP_PORT}|${IMAPS_PORT}|${POPS_PORT}|${HTTP_PORT}|${HTTPS_PORT} in the security group\033[0m" echo -e "==================================================================" } # Modify the apply SSL interface access port MODIFY_HTTP_SSL_PORT() { NEW_PORT="$2" if [ -z "${NEW_PORT}" ]; then echo -e "\033[31m Let's Encrypt SSL certificate can only be applied using port 80, other ports cannot be applied.\033[0m" read -p "Please enter the new apply SSL port: " NEW_PORT fi # Verify that the input is a number if ! echo "${NEW_PORT}" | grep -qE '^[0-9]+$' || ((NEW_PORT < 1 || NEW_PORT > 65535)); then echo -e "\033[31m Error: The port number must be a number between 1-65535 \033[0m" exit 1 fi Check_Port=$(ss -tlnp | grep -E ":(${NEW_PORT})\b") if [ ! -z "${Check_Port}" ]; then echo "${Check_Port}" echo -e "\033[31m Error: The ${NEW_PORT} port is already in use. \033[0m" exit 1 fi # Perform modification sed -i 's/^HTTP_PORT=.*/HTTP_PORT='"${NEW_PORT}"'/' .env echo -e "The BillionMail apply SSL port has been modified to: ${NEW_PORT} \n Rebuild the container, please wait..." sleep 3 # Find the container ID of the core image in the current project and rebuild the container # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Rebuilding Manage Container..." docker stop ${CONTAINER_ID} docker rm -f ${CONTAINER_ID} ${DOCKER_COMPOSE} up -d else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi if [ -f "/usr/sbin/ufw" ]; then /usr/sbin/ufw allow ${NEW_PORT}/tcp >/dev/null 2>&1 /usr/sbin/ufw reload elif [ -f "/usr/sbin/firewall-cmd" ]; then /usr/sbin/firewall-cmd --permanent --zone=public --add-port=${NEW_PORT}/tcp >/dev/null 2>&1 /usr/sbin/firewall-cmd --reload elif [ -f "/etc/sysconfig/iptables" ]; then iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${NEW_PORT} -j ACCEPT service iptables save fi if [[ ${NEW_PORT} != "80" ]]; then echo -e "\033[31m Let's Encrypt SSL certificate can only be applied using port 80, other ports cannot be applied.\033[0m" fi } # Modify the management interface access port MODIFY_HTTPS_PORT() { NEW_PORT="$2" if [ -z "${NEW_PORT}" ]; then read -p "Please enter the new BillionMail management port: " NEW_PORT fi # Verify that the input is a number if ! echo "${NEW_PORT}" | grep -qE '^[0-9]+$' || ((NEW_PORT < 1 || NEW_PORT > 65535)); then echo -e "\033[31m Error: The port number must be a number between 1-65535 \033[0m" exit 1 fi Check_Port=$(ss -tlnp | grep -E ":(${NEW_PORT})\b") if [ ! -z "${Check_Port}" ]; then echo "${Check_Port}" echo -e "\033[31m Error: The ${NEW_PORT} port is already in use. \033[0m" exit 1 fi # Perform modification sed -i 's/^HTTPS_PORT=.*/HTTPS_PORT='"${NEW_PORT}"'/' .env if [ $? -ne 0 ]; then echo -e "\033[31m Error: The BillionMail management port modification failed! \033[0m" exit 1 fi echo -e "The BillionMail management port has been modified to: ${NEW_PORT} \n Rebuild the container, please wait..." sleep 3 # Find the container ID of the core image in the current project and rebuild the container # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Rebuilding Manage Container..." docker stop ${CONTAINER_ID} docker rm -f ${CONTAINER_ID} ${DOCKER_COMPOSE} up -d else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi if [ -f "/usr/sbin/ufw" ]; then /usr/sbin/ufw allow ${NEW_PORT}/tcp >/dev/null 2>&1 /usr/sbin/ufw reload elif [ -f "/usr/sbin/firewall-cmd" ]; then /usr/sbin/firewall-cmd --permanent --zone=public --add-port=${NEW_PORT}/tcp >/dev/null 2>&1 /usr/sbin/firewall-cmd --reload elif [ -f "/etc/sysconfig/iptables" ]; then iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${NEW_PORT} -j ACCEPT service iptables save fi # Display login information bash bm.sh default } # Modify time zone MODIFY_TZ() { if [ -a /etc/timezone ]; then SYSTEM_TIME_ZONE=$(cat /etc/timezone) elif [ -a /etc/localtime ]; then SYSTEM_TIME_ZONE=$(readlink /etc/localtime|sed -n 's|^.*zoneinfo/||p') fi NEW_TZ="$2" echo -e "" echo -e "See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones for a list of timezones" echo -e "Use a column named "TZ identifier" + note the column named "Notes"" echo -e "Please enter your time zone" echo -e "" if [ -z "${SYSTEM_TIME_ZONE}" ]; then read -p "Timezone: " -e NEW_TZ else read -p "Timezone [${SYSTEM_TIME_ZONE}]: " -e NEW_TZ [ -z "${NEW_TZ}" ] && NEW_TZ=${SYSTEM_TIME_ZONE} fi if [ -z "${NEW_TZ}" ]; then echo -e "\033[31m Error: Please enter the time zone \033[0m" exit 1 fi # Perform modification sed -i "s|^TZ=.*|TZ=${NEW_TZ}|" .env if [ $? -ne 0 ]; then echo -e "\033[31m Error: The BillionMail time zone modification failed! \033[0m" exit 1 fi if [ -f "postgresql-data/postgresql.conf" ]; then cp -arpf postgresql-data/postgresql.conf postgresql-data/postgresql.conf_${time} sed -i "s|^log_timezone = .*|log_timezone = \'${NEW_TZ}\'|" postgresql-data/postgresql.conf sed -i "s|^timezone = .*|timezone = \'${NEW_TZ}\'|" postgresql-data/postgresql.conf fi echo -e "The BillionMail time zone has been modified to: ${NEW_TZ} \n Rebuild the container, please wait..." sleep 3 ${DOCKER_COMPOSE} down ${DOCKER_COMPOSE} up -d } # Modify Admin Username MODIFY_ADMIN_USERNAME() { NEW_ADMIN="$2" if [ -z "${NEW_ADMIN}" ]; then read -p "Please enter the new BillionMail administrator username (minimum 5 characters): " NEW_ADMIN fi if [ -z "${NEW_ADMIN}" ]; then echo -e "\033[31mError: Administrator username is required!\033[0m" exit 1 fi if [ ${#NEW_ADMIN} -lt 5 ]; then echo -e "\033[31mError: Username must be at least 5 characters long!\033[0m" exit 1 fi if ! [[ "${NEW_ADMIN}" =~ ^[a-zA-Z0-9@#_.!+-]+$ ]]; then echo -e "\033[31mError: Username can only contain letters, numbers, symbols: @ # _ - + . !\033[0m" exit 1 fi # Perform modification sed -i "s|^ADMIN_USERNAME=.*|ADMIN_USERNAME="${NEW_ADMIN}"|" .env if [ $? -ne 0 ]; then echo -e "\033[31mError: Failed to update BillionMail administrator username!\033[0m" exit 1 fi echo -e "BillionMail administrator username has been updated. Restarting container, please wait..." sleep 3 # Find the container ID of the core image in the current project and rebuild the container # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Restarting Manage Container..." docker restart ${CONTAINER_ID} else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi # Display login information bash bm.sh default } # Modify Admin Password MODIFY_ADMIN_PASSWORD() { NEW_PASSWORD="$2" if [ -z "${NEW_PASSWORD}" ]; then read -p "Please enter the new BillionMail administrator password (minimum 5 characters): " NEW_PASSWORD fi if [ -z "${NEW_PASSWORD}" ]; then echo -e "\033[31mError: Administrator password is required!\033[0m" exit 1 fi if [ ${#NEW_PASSWORD} -lt 5 ]; then echo -e "\033[31mError: Password must be at least 5 characters long!\033[0m" exit 1 fi if ! [[ "${NEW_PASSWORD}" =~ ^[a-zA-Z0-9@#_.!+-]+$ ]]; then echo -e "\033[31mError: Password can only contain letters, numbers, symbols: @ # _ - + . !\033[0m" exit 1 fi # Perform modification sed -i "s|^ADMIN_PASSWORD=.*|ADMIN_PASSWORD="${NEW_PASSWORD}"|" .env if [ $? -ne 0 ]; then echo -e "\033[31mError: Failed to update BillionMail administrator password!\033[0m" exit 1 fi echo -e "BillionMail administrator password has been updated. Restarting container, please wait..." sleep 3 # Find the container ID of the core image in the current project and rebuild the container #CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Restarting Manage Container..." docker restart ${CONTAINER_ID} else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi # Display login information bash bm.sh default } # Modify Safe entrance MODIFY_SAFE_ENTRANCE() { NEW_ENTRANCE="$2" if [ -z "${NEW_ENTRANCE}" ]; then read -p "Please enter the new BillionMail security entrance path (minimum 5 characters): " NEW_ENTRANCE fi if [ -z "${NEW_ENTRANCE}" ]; then echo -e "\033[31mError: Security entrance path is required!\033[0m" exit 1 fi if [ ${#NEW_ENTRANCE} -lt 5 ]; then echo -e "\033[31mError: Security entrance path must be at least 5 characters long!\033[0m" exit 1 fi if ! [[ "${NEW_ENTRANCE}" =~ ^[a-zA-Z0-9]+$ ]]; then echo -e "\033[31mError: Security entrance path can only contain letters and numbers!\033[0m" exit 1 fi # Perform modification sed -i "s|^SafePath=.*|SafePath=${NEW_ENTRANCE}|" .env if [ $? -ne 0 ]; then echo -e "\033[31mError: Failed to update BillionMail security entrance path!\033[0m" exit 1 fi echo -e "BillionMail security entrance path has been updated. Restarting container, please wait..." sleep 3 # Find the container ID of the core image in the current project and rebuild the container # CONTAINER_ID=$(${DOCKER_COMPOSE} ps -a --format "{{.ID}} {{.Image}}" |grep "/core:" | awk '{print $1}' ) CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Restarting Manage Container..." docker restart ${CONTAINER_ID} else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi # Display login information bash bm.sh default } # Rebuild the BillionMail project REBUILD_PROJECT() { echo "Rebuilding BillionMail..." echo -e "\033[31mWarning: This operation will rebuild all containers, service will be unavailable during the rebuild process. \033[0m" read -p "Are you sure you want to continue? (yes/no): " NEW_REBUILD if [[ "$NEW_REBUILD" =~ ^(yes|y|Y)$ ]]; then sleep 3 ${DOCKER_COMPOSE} down ${DOCKER_COMPOSE} up -d echo "Rebuild completed." else echo "Rebuild cancel." fi } # Restart the BillionMail project RESTART_PROJECT() { echo "Restarting BillionMail..." sleep 1 ${DOCKER_COMPOSE} restart } # Stop the BillionMail project STOP_PROJECT() { echo "Stop BillionMail..." sleep 1 ${DOCKER_COMPOSE} stop } START_PROJECT() { echo "Start BillionMail..." sleep 1 ${DOCKER_COMPOSE} up -d } DOWN_PROJECT() { echo "Stop all BillionMail services and delete all BillionMail containers..." sleep 3 ${DOCKER_COMPOSE} down } # Restart the specified service RESTART_SERVICE() { SERVICE="$2" if [ -z "${SERVICE}" ]; then echo "" echo "Support input: postfix|core|dovecot|rspamd|redis|webmail|pgsql" read -p "Please enter service: " SERVICE fi GET_SERVICE_NAME ${SERVICE} echo "Restarting ${SERVICE} service..." sleep 3 ${DOCKER_COMPOSE} restart ${SERVICE_NAME} } RESTART_SERVICE_ADMIN() { SERVICE="core" GET_SERVICE_NAME ${SERVICE} echo "Restarting ${SERVICE} service..." sleep 3 ${DOCKER_COMPOSE} restart ${SERVICE_NAME} } # Get container log GET_CONTAINER_LOG() { CONTAINER="$2" if [ -z "${CONTAINER}" ]; then echo "" echo "Support input: postfix|core|dovecot|rspamd|redis|webmail|pgsql" read -p "Please enter container: " CONTAINER fi log_num="$3" follow="" if [ "$log_num" == "-f" ]; then follow="-f" log_num="$4" elif [ "$4" == "-f" ]; then follow="-f" fi if [[ "$log_num" =~ ^[0-9]+$ ]]; then log_num="${log_num}" else log_num=25 fi GET_CONTAINER_ID ${CONTAINER} if [ -z "${CONTAINER_ID}" ]; then Red_Error "ERROR: The "${CONTAINER}" container does not exist, Please execute '${DOCKER_COMPOSE} up -d' to start BillionMail" fi docker logs ${follow} --tail="${log_num}" ${CONTAINER_ID} } # Get file log GET_FILE_LOG() { NAME_FILE="$2" if [ -z "${NAME_FILE}" ]; then echo "" echo "Support input: postfix|core|core-access|core-error|dovecot|rspamd|fail2ban" read -p "Please enter name: " NAME_FILE fi log_num="$3" follow="" if [ "$log_num" == "-f" ]; then follow="-f" log_num="$4" elif [ "$4" == "-f" ]; then follow="-f" fi if [[ "$log_num" =~ ^[0-9]+$ ]]; then log_num="${log_num}" else log_num=25 fi # Define log files declare -A LOG_FILES=( ["core"]="$(date +%Y-%m-%d).log" ["core-access"]="access-$(date +%Y%m%d).log" ["core-error"]="error-$(date +%Y%m%d).log" ["postfix"]="mail.log" ["dovecot"]="mail.log" ["rspamd"]="rspamd.log" ["fail2ban"]="fail2ban.log" ) # Define log directory mapping declare -A LOG_DIRS=( ["core"]="core" ["core-access"]="core" ["core-error"]="core" ["postfix"]="postfix" ["dovecot"]="dovecot" ["rspamd"]="rspamd" ["fail2ban"]="fail2ban" ) # Check whether the service name is valid if [[ ! " ${!LOG_FILES[@]} " =~ " ${NAME_FILE} " ]]; then echo "How to use: $0 log_file core 100" Red_Error "Error: Invalid name. Please enter name: (postfix|core|core-access|core-error|dovecot|rspamd|fail2ban) " fi # Get log file name and directory LOG_FILE="${LOG_FILES[$NAME_FILE]}" LOG_DIR="${LOG_DIRS[$NAME_FILE]}" # Check and display log files if [ -f "logs/${LOG_DIR}/${LOG_FILE}" ]; then tail ${follow} -n ${log_num} "logs/${LOG_DIR}/${LOG_FILE}" elif [ -f "data/logs/${LOG_DIR}/${LOG_FILE}" ]; then tail ${follow} -n ${log_num} "data/logs/${LOG_DIR}/${LOG_FILE}" else Red_Error "logs file does not exist!" fi } # Get service TOP GET_SERVICE_TOP() { SERVICE="$2" if [ -z "${SERVICE}" ]; then read -p "Please enter service: " SERVICE fi GET_SERVICE_NAME ${SERVICE} ${DOCKER_COMPOSE} top ${SERVICE_NAME} } GET_SERVICE_TOP_ALL() { ${DOCKER_COMPOSE} top } GET_SERVICE_PS() { ${DOCKER_COMPOSE} ps } # Get containers status GET_CONTAINER_STATUS() { CONTAINERS=("core" "postfix" "dovecot" "rspamd" "pgsql" "redis" "webmail") for cc in "${CONTAINERS[@]}"; do CONTAINER="${cc}" GET_CONTAINER_ID "${CONTAINER}" status=$(docker inspect --format='{{.State.Status}}' "${CONTAINER_ID}" 2>/dev/null) if [ "$status" = "running" ]; then echo -e "\033[32m ${CONTAINER} container is running\033[0m" else echo -e "\033[31m ${CONTAINER} container is not running. Status: ${status} \033[0m" fi done } CLEAR_OLD_IMAGE() { # Remove only outdated versions of images defined in docker-compose.yml # Check if docker-compose.yml exists if [ ! -f "docker-compose.yml" ]; then Red_Error "Error: docker-compose.yml file not found" fi # Extract complete image names (including tags) defined in compose file # echo "Extracting billionmail images from docker-compose.yml..." # COMPOSE_IMAGES=$(grep -oP 'image:\s*\K[^"\s]+' docker-compose.yml | sort -u) COMPOSE_IMAGES=$(grep -oP 'image:\s*\K(?:billionmail|ghcr\.io/aapanel)[^"\s]+' docker-compose.yml | sort -u) # Check if any images were found if [ -z "${COMPOSE_IMAGES}" ]; then echo "Warning: No image definitions found in docker-compose.yml" exit 1 fi # Get all local Docker images (excluding images) # echo "Getting all local Docker images..." ALL_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v ":") # Find outdated images to remove IMAGES_TO_REMOVE="" while IFS= read -r compose_image; do # Extract image name (without tag) image_name=$(echo "${compose_image}" | awk -F: '{print $1}') # Extract tag defined in compose file compose_tag=$(echo "${compose_image}" | awk -F: '{print $2}') # Find all local images with the same name local_images=$(echo "${ALL_IMAGES}" | grep "^${image_name}:") # Filter images with different tags for local_image in ${local_images}; do local_tag=$(echo "${local_image}" | awk -F: '{print $2}') # If local tag differs from compose tag, add to removal list if [ "${local_tag}" != "${compose_tag}" ]; then IMAGES_TO_REMOVE="${IMAGES_TO_REMOVE} ${local_image}" fi done done <<< "${COMPOSE_IMAGES}" # Display results if [ -z "${IMAGES_TO_REMOVE}" ]; then echo "" echo "No outdated images found to remove" exit 0 fi echo -e "\n--- Outdated images to remove ---" echo "${IMAGES_TO_REMOVE}" | tr ' ' '\n' | grep -v '^$' IMAGE_COUNT=$(echo "${IMAGES_TO_REMOVE}" | wc -w) echo -e "\nTotal: ${IMAGE_COUNT} outdated images to remove" # Ask for confirmation read -p "Remove these outdated images? (y/n): " confirm if [ "${confirm}" = "y" ] || [ "${confirm}" = "Y" ]; then sleep 5 for image in ${IMAGES_TO_REMOVE}; do echo "Removing image: ${image}" docker rmi "${image}" || echo "Warning: Failed to remove ${image}" done echo "Outdated images removed successfully" echo -e "For deeper Docker system cleanup, ensure all containers to keep are running before executing:\n docker system prune" else echo "Operation cancelled" fi } # Turn off IP access whitelist restrictions CANCEL_IP_WHITELIST_LIMIT() { # Perform modification sed -i 's/^IP_WHITELIST_ENABLE=.*/IP_WHITELIST_ENABLE=false/' .env echo -e "The BillionMail IP access whitelist Restrictions: Closed \n Restart the container, please wait..." sleep 3 CONTAINER="core" GET_CONTAINER_ID ${CONTAINER} if [ "${CONTAINER_ID}" ]; then echo "Rebuilding Manage Container..." docker stop ${CONTAINER_ID} docker rm -f ${CONTAINER_ID} ${DOCKER_COMPOSE} up -d else echo "The "core" container does not exist" echo "Starting BillionMail..." ${DOCKER_COMPOSE} up -d fi } APPLY_MULTI_IP() { echo "๐Ÿš€ Starting multi-IP configuration application..." # Initialize_Project echo "๐Ÿ“ Current directory: $PWD_d" PROJECT_DIR="$PWD_d" # Temporary backup path (under project root) BACKUP_COMPOSE="docker-compose.yml.bak.$(date +%Y%m%d-%H%M%S)" # ๐Ÿ” Load .env file ENV_FILE="./.env" if [ -f "$ENV_FILE" ]; then echo "๐Ÿ” Loading environment variables: $ENV_FILE" source "$ENV_FILE" if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then Red_Error "โŒ .env configuration error, database settings are empty" fi else Red_Error "โŒ .env file not found: $ENV_FILE, please ensure configuration exists" fi # Database configuration DB_HOST="127.0.0.1" DB_PORT="25432" DB_NAME="${DBNAME}" DB_USER="${DBUSER}" DB_PASS="${DBPASS}" # Record initial state for rollback ORIGINAL_COMPOSE="docker-compose.yml" ADDNETWORK_COMPOSE="docker-compose_addnetwork.yml" # Create temporary file TEMP_FILE=$(mktemp) || { Red_Error "โŒ Unable to create temporary file"; } # ============ Cleanup and rollback functions ============ cleanup_apply() { if [[ -n "$TEMP_FILE" && -f "$TEMP_FILE" ]]; then rm -f "$TEMP_FILE" fi } rollback_compose() { echo "โš ๏ธ Error detected, rolling back docker-compose.yml..." if [[ -f "$BACKUP_COMPOSE" ]]; then cp "$BACKUP_COMPOSE" "$ORIGINAL_COMPOSE" echo "โœ… Successfully rolled back to original configuration" else echo "โŒ Backup file $BACKUP_COMPOSE does not exist, unable to rollback!" fi } # Set trap to handle exceptions and cleanup trap 'echo "๐Ÿ’ฅ Script execution failed, triggering rollback"; rollback_compose; cleanup_apply; exit 1' ERR trap 'cleanup_apply' EXIT # ============ 1. Check if new compose file exists ============ echo "๐Ÿ” Checking if docker-compose_addnetwork.yml exists..." if [ ! -f "${ADDNETWORK_COMPOSE}" ]; then Red_Error "โŒ Error: ${ADDNETWORK_COMPOSE} file does not exist, please verify" fi # ============ 2. Backup and replace docker-compose.yml ============ echo "๐Ÿ”„ Backing up and replacing docker-compose.yml" if [[ -f "$ORIGINAL_COMPOSE" ]]; then cp "$ORIGINAL_COMPOSE" "$BACKUP_COMPOSE" echo "โœ… Backup created as: $BACKUP_COMPOSE" fi # Use cp instead of mv to keep original file for repeated execution cp "$ADDNETWORK_COMPOSE" "$ORIGINAL_COMPOSE" echo "โœ… docker-compose.yml has been updated" # ============ 3. Insert domain โ†’ smtp_name mapping into bm_domain_smtp_transport ============ echo "๐Ÿ”„ Reading data from bm_multi_ip_domain and inserting into bm_domain_smtp_transport..." # Query database for domain and smtp_server_name using secure query method if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A -F'|' \ -c "SELECT domain, smtp_server_name FROM bm_multi_ip_domain WHERE active = 1;" > "$TEMP_FILE"; then Red_Error "โŒ Database query failed, please check connection or table structure" fi # Check if temporary file has content if [[ ! -s "$TEMP_FILE" ]]; then echo "โš ๏ธ No active multi-IP domain configurations found, skipping sender rule setup" else echo "๐Ÿ“‹ Found $(wc -l < "$TEMP_FILE") configuration records" # Safely process query results while IFS='|' read -r domain smtp_name; do # Clean whitespace and validate domain=$(echo "$domain" | xargs) smtp_name=$(echo "$smtp_name" | xargs) # Skip empty lines or invalid data [[ -z "$domain" || -z "$smtp_name" ]] && continue # Validate domain format (basic security check) if [[ ! "$domain" =~ ^[a-zA-Z0-9.-]+$ ]]; then echo "โš ๏ธ Skipping invalid domain format: $domain" continue fi atype="dedicated_ip" domain_with_at="@${domain}" echo "๐Ÿ” Checking if sender rule exists for domain: $domain_with_at..." # Use secure SQL query escaped_domain=$(printf '%s\n' "$domain_with_at" | sed "s/'/''/g") EXISTS=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT 1 FROM bm_domain_smtp_transport WHERE domain = '$escaped_domain';") # If exists, delete it if [[ -n "$EXISTS" && "$EXISTS" != "" ]]; then echo "๐ŸŸก Sender rule already exists for domain: $domain_with_at, deleting..." if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \ -c "DELETE FROM bm_domain_smtp_transport WHERE domain = '$escaped_domain';"; then Red_Error "โŒ Failed to delete old record: $domain_with_at" fi echo "โœ… Old rule deleted: $domain_with_at" fi # Execute insertion (using secure string escaping) echo "๐Ÿ“ Inserting: $domain_with_at โ†’ $smtp_name" escaped_smtp=$(printf '%s\n' "$smtp_name" | sed "s/'/''/g") escaped_atype=$(printf '%s\n' "$atype" | sed "s/'/''/g") if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \ -c "INSERT INTO bm_domain_smtp_transport (atype, domain, smtp_name) VALUES ('$escaped_atype', '$escaped_domain', '$escaped_smtp');"; then Red_Error "โŒ Insertion failed: $domain_with_at" fi echo "โœ… Successfully inserted: $domain_with_at โ†’ $smtp_name" done < "$TEMP_FILE" echo "๐Ÿ“ All domain mappings have been successfully written to bm_domain_smtp_transport" fi # Update status of all records in bm_multi_ip_domain table to 'applied' echo "๐Ÿ”„ Resetting multi-IP domain status to 'applied'..." # First, query how many records need updating CURRENT_DOMAIN_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status != 'applied';" 2>/dev/null || echo "0") echo "๐Ÿ“Š Current number of domains needing status reset: ${CURRENT_DOMAIN_COUNT}" if [[ "${CURRENT_DOMAIN_COUNT}" -gt 0 ]]; then # Execute update operation if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \ -c "UPDATE bm_multi_ip_domain SET status = 'applied';" >/dev/null 2>&1; then echo "โœ… Successfully reset ${CURRENT_DOMAIN_COUNT} multi-IP domain statuses to 'applied'" else echo "โŒ Failed to reset multi-IP domain statuses" exit 1 fi else echo "โœ… All multi-IP domain statuses are already 'applied'" fi # ============ 4. Restart services ============ echo "๐Ÿ”„ Restarting BillionMail services" if ! ${DOCKER_COMPOSE} down; then Red_Error "โŒ docker-compose down failed" fi if ! ${DOCKER_COMPOSE} up -d; then Red_Error "โŒ docker-compose up -d failed: docker-compose.yml has errors, please check" fi # Wait for services to start and check health status echo "๐Ÿ”„ Waiting for services to start..." sleep 5 echo "๐Ÿ“Š Checking service status..." ${DOCKER_COMPOSE} ps # Check if critical services are running properly CORE_STATUS=$(${DOCKER_COMPOSE} ps core-billionmail --format "{{.State}}" 2>/dev/null || echo "missing") POSTFIX_STATUS=$(${DOCKER_COMPOSE} ps postfix-billionmail --format "{{.State}}" 2>/dev/null || echo "missing") if [[ "$CORE_STATUS" != "running" || "$POSTFIX_STATUS" != "running" ]]; then echo "โš ๏ธ Warning: Some critical services may not have started properly" echo " Core status: $CORE_STATUS" echo " Postfix status: $POSTFIX_STATUS" echo "๐Ÿ’ก Please check 'docker compose logs' for details" fi # ============ 5. Execution Summary ============ echo "" echo "๐ŸŽ‰ =============== Execution Summary ===============" echo "โœ… All operations completed! Services have been successfully restarted" echo "๐Ÿ“ Original configuration backed up as: $BACKUP_COMPOSE" echo "๐Ÿ”„ New configuration applied: $ORIGINAL_COMPOSE" echo "๐Ÿ“‹ Source configuration file preserved: $ADDNETWORK_COMPOSE" echo "๐Ÿ• Completion time: $(date '+%Y-%m-%d %H:%M:%S')" # Optional: Clean up old backups (keep latest 3) OLD_BACKUPS=$(find . -name 'docker-compose.yml.bak.*' -type f | wc -l) if [[ $OLD_BACKUPS -gt 3 ]]; then echo "๐Ÿงน Cleaning up old backup files (keeping latest 3)..." find . -name 'docker-compose.yml.bak.*' -type f | sort -r | tail -n +4 | xargs rm -f 2>/dev/null || true fi echo "=========================================" # Successfully completed, remove ERR trap trap - ERR } FIX_MULTI_IP() { echo "๐Ÿ”ง Starting multi-IP configuration fix..." # ============ Initialization: Automatically locate project root directory ============ # Initialize_Project # Load environment variables if [ ! -f ".env" ]; then Red_Error "โŒ .env file not found, please ensure execution from project root directory" fi source .env if [ -z "${DBUSER}" ] || [ -z "${DBNAME}" ] || [ -z "${DBPASS}" ]; then Red_Error "โŒ .env configuration error, database settings are empty" fi echo "๐Ÿ“ Project directory: ${PWD_d}" # ============ 1. Find the latest backup file ============ echo "๐Ÿ” Searching for the latest docker-compose.yml backup file..." LATEST_BACKUP=$(find . -name 'docker-compose.yml.bak.*' -type f | sort -r | head -n 1) if [ -z "${LATEST_BACKUP}" ]; then echo "โš ๏ธ No docker-compose.yml backup file found" echo "๐Ÿ“‹ List of available backup files:" find . -name 'docker-compose.yml.bak.*' -type f 2>/dev/null || echo " No backup files" read -p "Proceed with database cleanup operation? (y/n): " continue_cleanup if [[ "$continue_cleanup" != "y" && "$continue_cleanup" != "Y" ]]; then echo "โŒ Operation cancelled" exit 1 fi else echo "โœ… Found latest backup: ${LATEST_BACKUP}" echo "๐Ÿ”„ Restoring docker-compose.yml..." # Create a backup of current file (in case rollback fails) cp docker-compose.yml "docker-compose.yml.before-fix.$(date +%Y%m%d-%H%M%S)" 2>/dev/null || true # Restore from backup if cp "${LATEST_BACKUP}" docker-compose.yml; then echo "โœ… docker-compose.yml has been restored" else Red_Error "โŒ Failed to restore docker-compose.yml" fi fi # ============ 2. Clean up multi-IP configurations in database ============ echo "๐Ÿ—„๏ธ Starting database configuration cleanup..." # Check database connection (using container method) echo "๐Ÿ”— Testing database connection..." if ! docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -c "SELECT 1;" >/dev/null 2>&1; then Red_Error "โŒ Database connection failed, please check if service is running properly" fi echo "โœ… Database connection is normal" # Delete records with atype="dedicated_ip" from bm_domain_smtp_transport table echo "๐Ÿ—‘๏ธ Deleting dedicated IP records from bm_domain_smtp_transport table..." # First, query the current number of records CURRENT_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "0") echo "๐Ÿ“Š Current number of dedicated IP sender rules: ${CURRENT_SMTP_COUNT}" if [[ "${CURRENT_SMTP_COUNT}" -gt 0 ]]; then # Execute deletion if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \ -c "DELETE FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" >/dev/null 2>&1; then echo "โœ… Deleted ${CURRENT_SMTP_COUNT} dedicated IP sender rules" else echo "โŒ Failed to delete dedicated IP sender rules" exit 1 fi else echo "โœ… No dedicated IP sender rules found to delete" fi # Update status of all records in bm_multi_ip_domain table to 'pending' echo "๐Ÿ”„ Resetting multi-IP domain status to 'pending'..." # First, query the current number of records needing update CURRENT_DOMAIN_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status != 'pending';" 2>/dev/null || echo "0") echo "๐Ÿ“Š Current number of domains needing status reset: ${CURRENT_DOMAIN_COUNT}" if [[ "${CURRENT_DOMAIN_COUNT}" -gt 0 ]]; then # Execute update operation if docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} \ -c "UPDATE bm_multi_ip_domain SET status = 'pending';" >/dev/null 2>&1; then echo "โœ… Reset ${CURRENT_DOMAIN_COUNT} multi-IP domain statuses to 'pending'" else echo "โŒ Failed to reset multi-IP domain statuses" exit 1 fi else echo "โœ… All multi-IP domain statuses are already 'pending'" fi # ============ 3. Verify cleanup results ============ echo "๐Ÿ” Verifying cleanup results..." # Verify dedicated IP sender rules have been cleaned REMAINING_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "unknown") # Verify multi-IP domain statuses have been reset REMAINING_APPLIED_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status = 'applied';" 2>/dev/null || echo "unknown") echo "๐Ÿ“Š Verification results:" echo " Remaining dedicated IP sender rules: ${REMAINING_SMTP_COUNT}" echo " Remaining applied status domains: ${REMAINING_APPLIED_COUNT}" # ============ 4. Restart services ============ echo "๐Ÿ”„ Restarting BillionMail services..." echo "๐Ÿ›‘ Stopping services..." if ! ${DOCKER_COMPOSE} down; then echo "โš ๏ธ Warning during service stop, continuing execution..." fi echo "๐Ÿš€ Starting services..." if ! ${DOCKER_COMPOSE} up -d; then Red_Error "โŒ Failed to start services, please check docker-compose.yml configuration" fi # Wait for services to start echo "โณ Waiting for services to start..." sleep 5 # Check service status echo "๐Ÿ“Š Checking service status..." ${DOCKER_COMPOSE} ps # ============ 5. Final verification ============ echo "๐Ÿ” Final verification of database status..." sleep 2 # Final verification of dedicated IP sender rules FINAL_SMTP_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_domain_smtp_transport WHERE atype = 'dedicated_ip';" 2>/dev/null || echo "unknown") # Final verification of multi-IP domain statuses FINAL_APPLIED_COUNT=$(docker exec -i -e PGPASSWORD=${DBPASS} ${PGSQL_CONTAINER_NAME} psql -U ${DBUSER} -d ${DBNAME} -t -A \ -c "SELECT COUNT(*) FROM bm_multi_ip_domain WHERE status = 'applied';" 2>/dev/null || echo "unknown") # ============ 6. Execution Summary ============ echo "" echo "๐ŸŽ‰ =============== Fix Summary ===============" echo "โœ… Multi-IP configuration fix completed!" if [ -n "${LATEST_BACKUP}" ]; then echo "๐Ÿ“ Restored backup: ${LATEST_BACKUP} โ†’ docker-compose.yml" fi echo "๐Ÿ—‘๏ธ Dedicated IP sender rule cleanup result: ${CURRENT_SMTP_COUNT} โ†’ ${FINAL_SMTP_COUNT}" echo "๐Ÿ”„ applied status domain reset result: ${CURRENT_DOMAIN_COUNT} โ†’ ${FINAL_APPLIED_COUNT}" echo "๐Ÿš€ Services restarted" echo "๐Ÿ• Completion time: $(date '+%Y-%m-%d %H:%M:%S')" echo "=========================================" # Provide detailed verification information if [[ "${FINAL_SMTP_COUNT}" == "0" && "${FINAL_APPLIED_COUNT}" == "0" ]]; then echo "" echo "๐ŸŽ‰ Congratulations! Database cleanup completed successfully:" echo " โœ… All dedicated IP sender rules have been deleted" echo " โœ… All multi-IP domain statuses have been reset to pending" else echo "" echo "โš ๏ธ Note: Some data may not have been fully cleaned:" if [[ "${FINAL_SMTP_COUNT}" != "0" ]]; then echo " โŒ Still ${FINAL_SMTP_COUNT} dedicated IP sender rules remaining" fi if [[ "${FINAL_APPLIED_COUNT}" != "0" ]]; then echo " โŒ Still ${FINAL_APPLIED_COUNT} domains with applied status" fi echo " ๐Ÿ’ก Suggest manually checking the database or re-running the fix command" fi echo "" echo "๐Ÿ’ก Tips:" echo " 1. To reconfigure multi-IP, please re-run: bm.sh multi_ip" echo " 2. Suggest checking email sending functionality" echo " 3. If issues occur, check container logs: bm.sh l-c core" } SHOW_HELP() { echo "Help Information:" echo " default - Show BillionMail login default info: $0 default" echo " update - Update BillionMail: $0 update" echo " change-port - Modify BillionMail access management port: $0 change-port" echo " change-tz - Modify BillionMail time zone: $0 change-tz" echo " change-user - Modify BillionMail Administrator user: $0 change-user" echo " change-password - Modify BillionMail Administrator password: $0 change-password" echo " change-safe-path - Modify BillionMail security entrance path: $0 change-safe-path" echo " change-apply-ssl-port - Modify BillionMail apply ssl port: $0 change-apply-ssl-port" echo " start - Start BillionMail: $0 start" echo " stop - Stop BillionMail: $0 stop" echo " restart - Restart BillionMail: $0 restart" echo " status - Show BillionMail containers running status : $0 status" echo " down - Stop and remove containers, networks: $0 down" echo " rebuild - Rebuild all BillionMail containers: $0 rebuild" echo " top - Show all BillionMail processes: $0 top" echo " ps - Show all BillionMail containers: $0 ps" echo " service-top - Show processes of a specific BillionMail service: $0 s-t postfix" echo " log-file - View logs of a specific service: $0 l-f postfix" echo " log-container - View logs of a specific container: $0 l-c postfix" echo " restart-service - Restart a specific service and its container: $0 r-s postfix" echo " clear - Clear BillionMail old images: $0 clear" echo " cancel-ip-limit - Cancel IP access limit : $0 c-i-l" # echo " add-domain - Add domain. Example: $0 add-domain example.com" # echo " del-domain - Delete domain. Example: $0 del-domain example.com" # echo " add-email - Add email. Example: $0 add-email user@example.com" # echo " del-email - Delete email. Example: $0 del-email user@example.com" echo " show-domain - Show domain data." echo " show-email - Show email data." # echo " show-record - Show domain DNS record." exit } case "$1" in h | -h | help | --help | -help) SHOW_HELP ;; default | info) Default_info ;; update) Update_BillionMail ;; add-domain) Init_Domain "$@" Add_Domain ;; del-domain) Init_Domain "$@" Del_Domain ;; add-email) Init_Email "$@" Add_Email ;; del-email) Init_Email "$@" Del_Email ;; show-domain) Select_Domain_data ;; show-email) Select_Email_data ;; show-record|show_dns_record) Init_Domain "$@" Domain_record ;; MODIFY_HTTP_PORT|modify_http_port|change-apply-ssl-port) MODIFY_HTTP_SSL_PORT ;; MODIFY_HTTPS_PORT|modify_https_port|change-port) MODIFY_HTTPS_PORT ;; MODIFY_TZ|modify_tz|change-tz) MODIFY_TZ ;; MODIFY_ADMIN_USERNAME|modify_user|change-user) MODIFY_ADMIN_USERNAME ;; MODIFY_ADMIN_PASSWORD|modify_password|change-password) MODIFY_ADMIN_PASSWORD ;; MODIFY_SAFE_ENTRANCE|modify_safe_path|change-safe-path) MODIFY_SAFE_ENTRANCE ;; REBUILD_PROJECT|rebuild_project|rebuild) REBUILD_PROJECT ;; RESTART_PROJECT|restart_project|restart) RESTART_PROJECT ;; START_PROJECT|start_project|start) START_PROJECT ;; STOP_PROJECT|stop_project|stop) STOP_PROJECT ;; GET_CONTAINER_STATUS|get_container_status|status) GET_CONTAINER_STATUS ;; DOWN_PROJECT|down_project|down) DOWN_PROJECT ;; RESTART_SERVICE|restart_service|restart-service|r-s) RESTART_SERVICE "$@" ;; GET_CONTAINER_LOG|log_container|log-container|l-c) GET_CONTAINER_LOG "$@" ;; GET_FILE_LOG|log_file|log-file|l-f) GET_FILE_LOG "$@" ;; GET_SERVICE_TOP|service_top|service-top|s-t) GET_SERVICE_TOP "$@" ;; GET_SERVICE_TOP_ALL|service_top_all|service-top-all|top) GET_SERVICE_TOP_ALL ;; GET_SERVICE_PS|service-ps|ps) GET_SERVICE_PS ;; RESTART_SERVICE_ADMIN|restart_service_admin|restart-service-admin|restart-admin) RESTART_SERVICE_ADMIN ;; clear) CLEAR_OLD_IMAGE ;; cancel-ip-limit|c-i-l) CANCEL_IP_WHITELIST_LIMIT ;; Domain_multi_send_IP|apply_multi_ip|multi_ip) APPLY_MULTI_IP ;; fix_multi_ip|fix-multi-ip) FIX_MULTI_IP ;; *) echo "=============== BillionMail CLI ==================" echo "1) Restart BillionMail 2) View login info" echo "" echo "3) View running status 4) Stop BillionMail" echo "" echo "5) Start BillionMail 6) Restart manage only (mail unaffected)" echo "" echo "7) Change manage password 8) Change manage username" echo "" echo "9) Change secure entry 10) Change manage access port" echo "" echo "11) View all processes 12) Update BillionMail" echo "" echo "13) Cancel IP access limit " echo "" echo "0) Exit For more commands: help" echo "==================================================" read -p "Enter number to execute command [0-12]: " input case ${input} in 1) RESTART_PROJECT ;; 2) Default_info ;; 3) GET_CONTAINER_STATUS ;; 4) STOP_PROJECT ;; 5) START_PROJECT ;; 6) RESTART_SERVICE_ADMIN ;; 7) MODIFY_ADMIN_PASSWORD ;; 8) MODIFY_ADMIN_USERNAME ;; 9) MODIFY_SAFE_ENTRANCE ;; 10) MODIFY_HTTPS_PORT ;; 11) GET_SERVICE_TOP_ALL ;; 12) Update_BillionMail ;; 13) CANCEL_IP_WHITELIST_LIMIT ;; help) SHOW_HELP ;; 0 | * ) echo "Cancelled!" exit ;; esac ;; esac