#!/bin/bash # Don't remove dumb re-check! # Prevent 'tput' errors when running from Cron [[ -z $TERM || $TERM == "unknown" || $TERM == "dumb" ]] && export TERM=dumb readonly app_version="1.17.8" readonly svr_version="1.8" readonly os_ubuntu_supported=(bionic focal jammy) # https://ubuntu.com/about/release-cycle readonly php_supported=(7.4 8.0 8.1 8.2 8.3) # https://www.php.net/supported-versions.php readonly php_default="8.2" readonly mariadb_supported=(10.6 10.11) # https://mariadb.com/kb/en/mariadb-server-release-dates/ readonly mariadb_default="10.11" readonly mysql_supported=(8.0) readonly mysql_default="8.0" readonly datadog_agent_ver="7" readonly tools_port_default="22222" # echo colors readonly red=$(tput setaf 1) readonly gre=$(tput setaf 2) readonly blu=$(tput setaf 6) readonly end=$(tput sgr0) readonly bol=$(tput bold) readonly dim=$(tput dim) readonly hid=$(tput setaf 6)$(tput setab 6) readonly hidend=$(tput sgr0)$(tput el) # *********************************************** # Configuration Management Functions ********** # *********************************************** conf_read() { if [[ ! -f /opt/webinoly/webinoly.conf ]]; then # Double check! echo "${red}[ERROR] Webinoly Configuration file not found!${end}" exit 1 fi echo $(grep -w -m 1 "^${1}:.*" /opt/webinoly/webinoly.conf | cut -f 2- -d ':') } conf_delete() { if [[ ! -f /opt/webinoly/webinoly.conf ]]; then # Double check! echo "${red}[ERROR] Webinoly Configuration file not found!${end}" exit 1 fi if [[ $2 == "-commented" ]]; then # Remove it, then add it again to prevent multiple # for each server-reset sudo sed -i "/^${1}:/s/^/#/" /opt/webinoly/webinoly.conf else sed -i "/^${1}:/d" /opt/webinoly/webinoly.conf fi } conf_write() { if [[ ! -f /opt/webinoly/webinoly.conf ]]; then sudo touch /opt/webinoly/webinoly.conf sudo cat /opt/webinoly/templates/general/conf >> /opt/webinoly/webinoly.conf fi # If exists modify just the line, if not then add it. if [[ -n $(sudo grep -E "^[# \t]?${1}\:.*$" /opt/webinoly/webinoly.conf) ]]; then sudo sed -i -E "/^[# \t]?${1}\:.*$/c $1:$2" /opt/webinoly/webinoly.conf else echo "$1:$2" >> /opt/webinoly/webinoly.conf fi } # *********************************************** # Useful variables **************************** # *********************************************** # STOP and exit if not root or sudo. if [[ $(whoami) != "root" ]]; then echo "${red}Please run this script as root or using sudo.${end}" exit 1 fi # Check for BASH Shell # This is a very "shity" method, but checking if file exists is very reliable # If modified: this same script is in installer, general lib and verify if [[ $(conf_read shell-check) != "false" && -n $(echo $(tty) | grep -Eo "pts/[0-9]+") && -n $(logname) ]]; then pre_pid=$(ps -au | grep -E "pts/[0-9]+[ ]+S[s]?[ ]+" | sed '/sudo/d' | tail -n 1) [[ -n $pre_pid ]] && shell_pid=$(echo $pre_pid | awk '{print $2}') [[ -n $shell_pid && -f /proc/$shell_pid/cmdline ]] && shell_current=$(tr -d '\000' < /proc/$shell_pid/cmdline) [[ -n $shell_pid && -f /proc/$shell_pid/status ]] && shell_status=$(grep -Eo '^Name:.*bash.*' /proc/$shell_pid/status) # Double check!!! if [[ -n $shell_current && $shell_current != *"bash"* && -z $shell_status ]]; then echo "${red}[WARNING] Seems like you are using an interactive shell different than BASH! ${dim}($(echo $pre_pid | awk '{print $1}'):${shell_current}) ${end}" fi fi # MySQL folder if [[ $(conf_read db-engine) == "mysql" ]]; then readonly MYSQL_CONF_PATH="/etc/mysql/mysql.conf.d" readonly MYSQL_CONF_PREF="xx" else readonly MYSQL_CONF_PATH="/etc/mysql/mariadb.conf.d" readonly MYSQL_CONF_PREF="90" fi # Current LoggedIn User! if [[ -n $(logname) && -d $(eval echo ~$(logname)) ]]; then # You better double check... readonly CURRENT_HOME=$(eval echo ~$(logname)) readonly CURRENT_USER=$(logname) else readonly CURRENT_HOME="/root" readonly CURRENT_USER="root" fi # Admin Tools Path if [[ -n $(conf_read tools-port) && -n $(conf_read tools-site) && -f /etc/nginx/sites-available/$(conf_read tools-site) ]]; then readonly ADMIN_TOOLS_SITE="$(conf_read tools-site):$(conf_read tools-port)" else if [[ -n $(conf_read tools-port) ]]; then readonly ADMIN_TOOLS_SITE="default:$(conf_read tools-port)" else # Fresh installation, dynvar is empty! readonly ADMIN_TOOLS_SITE="default:${tools_port_default}" fi fi # *********************************************** # General Functions *************************** # *********************************************** check_ubuntu_release() { local check="false" for val in "${os_ubuntu_supported[@]}" do [[ $val == $(lsb_release -c | cut -d':' -f 2 | xargs) ]] && check="true" done echo $check } check_php_version() { local check="false" for val in "${php_supported[@]}" do [[ $val == $1 ]] && check="true" done echo $check } check_mysql_version() { local check="false" # It works for both MySQL and MariaDB! if [[ $(conf_read db-engine) == "mysql" ]]; then for val in "${mysql_supported[@]}" do [[ $val == $1 ]] && check="true" done else for val in "${mariadb_supported[@]}" do [[ $val == $1 ]] && check="true" done fi echo $check } check_osname() { if ! [[ $(lsb_release -i | cut -d':' -f 2 | xargs) == "Ubuntu" && $(check_ubuntu_release) == "true" ]]; then echo "${red}" >&2 echo "****************************************************************************" >&2 echo "**** This OS is not supported by Webinoly and could not work properly ****" >&2 echo "****************************************************************************" >&2 echo "${end}" >&2 else echo $(lsb_release -c | cut -d':' -f 2 | xargs) fi } check_for_nginx() { if [[ $(conf_read nginx) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ NGINX Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -nginx fi if [[ $(conf_read nginx) != "true" ]]; then echo "${red}[ERROR] NGINX is required and not found! ${end}" exit 1 fi } check_for_nginx_tool_ssl() { check_for_nginx if [[ $(conf_read nginx-tool-ssl) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ Let's Encrypt Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -letsencrypt fi if [[ $(conf_read nginx-tool-ssl) != "true" ]]; then echo "${red}[ERROR] Let's Encrypt is required and not found! ${end}" exit 1 fi } check_for_nginx_tool_bkp() { #check_for_nginx # Backups doesn't need nginx, can be installed alone! if [[ $(conf_read nginx-tool-bkp) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ BackUp packages Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -backups fi if [[ $(conf_read nginx-tool-bkp) != "true" ]]; then echo "${red}[ERROR] BackUp packages are required and not found! ${end}" exit 1 fi } check_for_php() { if [[ $(conf_read php) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ PHP Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -php fi if [[ $(conf_read php) != "true" ]]; then echo "${red}[ERROR] PHP is required and not found! ${end}" exit 1 fi } check_for_php_tool_postfix() { check_for_php if [[ $(conf_read php-tool-postfix) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ Postfix Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -postfix fi if [[ $(conf_read php-tool-postfix) != "true" ]]; then echo "${red}[ERROR] Postfix is required and not found! ${end}" exit 1 fi } check_for_php_tool_redis() { check_for_php if [[ $(conf_read php-tool-redis) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ Redis Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -redis fi if [[ $(conf_read php-tool-redis) != "true" ]]; then echo "${red}[ERROR] Redis is required and not found! ${end}" exit 1 fi } check_for_mysql() { if [[ $(conf_read mysql) != "true" && $1 == "-ask" ]]; then echo "${red}" echo "+ MySQL/MariaDB Not Found!" echo "${blu}Do you want to install it now? [y/N]? ${end}" while read -r -n 1 -s answer; do answer=${answer:-n} echo "" [[ $answer = [YyNn] ]] && break done [[ $answer == [Yy] ]] && sudo stack -mysql fi if [[ $(conf_read mysql) != "true" ]]; then echo "${red}[ERROR] MySQL/MariaDB is required and not found! ${end}" exit 1 fi } check_for_mysql_client() { if [[ $(conf_read mysql-client) != "true" ]]; then echo "${gre}${dim}MySQL/MariaDB Client is not installed and we need it to stablish a connection with your external server.${end}" >&2 echo "${dim}Wait while we install MySQL/MariaDB Client...${end}" >&2 sudo stack -mysql=client > /dev/null 2>&1 & wait $! echo "${gre}MySQL/MariaDB Client has been successfully installed!${end}" >&2 fi } check_mysql_connection() { # Examples for admin connection: # Localhost: $(check_mysql_connection localhost) # Unix socket: $(check_mysql_connection localhost /var/run/mysqld/mysqld.sock) # Custom port: $(check_mysql_connection localhost 3307) # External DB: $(check_mysql_connection $dburl $dbport $dburoot $dbproot) # External DB if login group exist: $(check_mysql_connection $dburl $dbport $dburoot -login-file) # External DB if login group exist and is master-admin: $(check_mysql_connection $dburl $dbport any -login-file -master-admin) # External DB check and save it as master-admin: $(check_mysql_connection $dburl $dbport $dburoot $dbproot -master-admin) # Examples for especific User connection: # Localhost: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass) # Unix socket: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass /var/run/mysqld/mysqld.sock) # Custom port: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass 3307) # External DB: $(check_mysql_connection $extdb_url $extdb_port $wp_dbuser $wp_dbpass) # External DB if login group exist: $(check_mysql_connection $extdb_url $extdb_port $wp_dbuser -login-file) # External DB if login group exist and is master-admin: $(check_mysql_connection $extdb_url $extdb_port any -login-file -master-admin) # External DB check and save it as master-admin: $(check_mysql_connection $extdb_url $extdb_port $wp_dbuser $wp_dbpass -master-admin) # Examples for especific DBname/User connection: # Note: Error message is not displayed! # Localhost: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass $wp_dbname) # Unix socket: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass $wp_dbname /var/run/mysqld/mysqld.sock) # Custom port: $(check_mysql_connection localhost $wp_dbuser $wp_dbpass $wp_dbname 3307) # External DB: $(check_mysql_connection $extdb_url $extdb_port $wp_dbuser $wp_dbpass $wp_dbname) # External DB if login group exist: $(check_mysql_connection $extdb_url $extdb_port $wp_dbuser -login-file $wp_dbname) # Master-admin not needed here for specific dbs. # Note: You can always use the dynvar 'quiet' set to 'true' to not display messages. # In this especific case ONLY, there is a third value: false, true and truebutnotmaster (External DB connection successfull but not enough privileges for master-admin) local query="quit" local error_display="true" if [[ -n $1 && ${1,,} != "localhost" && $(is_url $1) =~ ^(http|https|true|http\+ip|https\+ip|ip)$ && -n $2 && -n $3 && -n $4 ]]; then check_for_mysql_client local suffix_group_name="${1}:${2}_${3}" local user_param="-u${3}" if [[ -n $5 && $5 == "-master-admin" ]]; then # 'default' is a reserved word, real usernames should never use it, it's possible, but not practical! local suffix_group_name="${1}:${2}_default" elif [[ -n $5 ]]; then local query="use $5" local error_display="false" fi # Create or update the login file if [[ $4 != "-login-file" ]]; then mysql_login_cnf sudo sed -i "/\[client_${suffix_group_name}\]/,/# ClientEnd/{/.*/d}" $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf echo "[client_${suffix_group_name}] host = $1 port = $2 user = $3 password = $4 # ClientEnd" >> $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf else [[ $5 == "-master-admin" ]] && unset user_param # take the user from the login group! local error_display="false" fi # Just for the record: We don't use the 'mysql_conf_editor' just because it can not be unattended :( # --defaults-group-suffix should always be at the beginning, otherwise it fails. sudo mysql --defaults-group-suffix=_${suffix_group_name} --connect-timeout=10 -h "$1" -P "$2" $user_param -e "$query" 2>/dev/null if [[ $? != "0" ]]; then local out="false" [[ -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf ]] && sudo sed -i "/\[client_${suffix_group_name}\]/,/# ClientEnd/{/.*/d}" $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf else # Display a warning message when not enough privileges! if [[ $5 == "-master-admin" ]]; then local priv=$(sudo mysql --defaults-group-suffix=_${suffix_group_name} --connect-timeout=10 -h "$1" -P "$2" -e "SHOW GRANTS FOR CURRENT_USER();") if ! [[ -n $(echo $priv | grep -Fo "WITH GRANT OPTION") && ( -n $(echo $priv | grep -Fo "GRANT ALL PRIVILEGES") || ( -n $(echo $priv | grep -Fo "CREATE USER") && -n $(echo $priv | grep -Fo "ALTER"))) ]]; then if [[ $(conf_read quiet) != "true" ]]; then echo "${red}${dim}[WARNING] Seems like '$(echo $priv | grep -oP -m 1 'Grants for \K\w+')' is not a Master user!${end}" >&2 else local master_priv="false" fi fi fi fi elif [[ ${1,,} == "localhost" && $(conf_read mysql) == "true" ]]; then if [[ -n $2 && -n $3 ]]; then if [[ -S $4 ]]; then local mysql_params="-S${4}" elif [[ $4 =~ ^[0-9]+$ && $4 -ge 0 && $4 -le 65535 ]]; then local mysql_params=(-P${4} --protocol=TCP) elif [[ -n $4 ]]; then local query="use $4" [[ -S $5 ]] && local mysql_params="-S${5}" [[ $5 =~ ^[0-9]+$ && $5 -ge 0 && $5 -le 65535 ]] && local mysql_params=(-P${5} --protocol=TCP) fi # We need the protocol socket fixed to prevent warnings when custom port is used. sudo mysql --connect-timeout=10 --user=$2 -p$3 -e "$query" "${mysql_params[@]}" 2>/dev/null [[ $? != "0" ]] && local out="false" local error_display="false" else # In case of Unix socket or localhost with custom port [[ -S $2 ]] && local mysql_params="-S${2}" [[ $2 =~ ^[0-9]+$ && $2 -ge 0 && $2 -le 65535 ]] && local mysql_params=(-P${2} --protocol=TCP) sudo mysql --connect-timeout=10 --user=admin -e "$query" "${mysql_params[@]}" 2>/dev/null [[ $? != "0" ]] && local out="false" fi else local out="false" fi if [[ $out == "false" && $error_display != "false" && $(conf_read quiet) != "true" ]]; then echo "${red}===================================================" >&2 echo " [Error] Database conection failed! (${1})" >&2 echo "===================================================${end}" >&2 echo "" >&2 echo "false" elif [[ $out == "false" ]]; then echo "false" else [[ $master_priv == "false" ]] && echo "truebutnotmaster" || echo "true" fi } check_external_db_saved() { if [[ -n $(conf_read external-dbh) && -n $(conf_read external-dbu) && -n $(conf_read external-dbp) && -n $(conf_read external-dbx) ]]; then external_db="[$(conf_read external-dbu),$(conf_read external-dbp),$(conf_read external-dbh):$(conf_read external-dbx)]" [[ $(conf_read quiet) != "true" ]] && echo "${blu}${dim}External DB credentials found in your saved configuration!${end}" >&2 fi } external_db_parse() { if [[ -n $external_db ]]; then # Prevent errors if [[ ${#external_db} -lt 2 ]]; then echo "${red}[ERROR] Invalid data for External Database!${end}" return fi local dbdata=${external_db:1:-1} local user=$(echo "${dbdata}" | cut -d',' -f 1 -s) local pass=$(echo "${dbdata}" | cut -d',' -f 2 -s) local host=$(echo "${dbdata}" | cut -d',' -f 3 -s) [[ -z $host && -n $wp_dbhost ]] && local host=$wp_dbhost # This is very shitty! local url=$(echo "$host" | cut -f 1 -d ':') local port=$(echo "$host" | cut -f 2 -d ':' -s) if [[ $(echo "${external_db}" | cut -c-1) != "[" || $(echo "${external_db}" | rev | cut -c-1) != "]" ]]; then echo "${red}[ERROR] Invalid syntax for External Database!${end}" return elif [[ -z $user || -z $pass ]]; then echo "${red}[ERROR] Invalid data for External Database!${end}" return fi if [[ $(check_mysql_connection $url $port $user $pass -master-admin) != "true" ]]; then echo "${red}[ERROR] Cannot connect with your External Database!${end}" return else # Make it global only after verification extdb_user=$user extdb_pass=$pass extdb_host=$host extdb_url=$url extdb_port=$port fi else echo "${red}[ERROR] External DB parameter not found!${end}" return fi } wp_config_path() { # If this file exist is because is WP parked # We can not use is_parked here because it will cause an infinite loop (is_parked uses this function) # if parked: check if parked before use this path value if [[ -f /etc/nginx/sites-available/$1 ]]; then local parpath="$(grep -G "root .*;" /etc/nginx/sites-available/$1 | sed -r 's/^.*root (.*)htdocs;$/\1/')wp-config.php" if [[ -n $2 && $2 != "false" && -f /var/www/$1/htdocs$2/wp-config.php ]]; then echo "/var/www/$1/htdocs$2/wp-config.php" # WP take this file first (if exist) and then look one folder below. # Webinoly check one folder below first and give priority because is our default file, no matter if you manually add a different one. # If user want to use their own file is ok, but they should remove the Webinoly default WP conf file. elif [[ ( -z $2 || $2 == "false" ) && -f /var/www/$1/wp-config.php ]]; then echo "/var/www/$1/wp-config.php" elif [[ ( -z $2 || $2 == "false" ) && -f /var/www/$1/htdocs/wp-config.php ]]; then echo "/var/www/$1/htdocs/wp-config.php" elif [[ ( -z $2 || $2 == "false" ) && -f $parpath ]]; then echo "$parpath" else return fi fi } wp_config_read() { # Example: wp_config_read example.com WP_DEBUG # Example: wp_config_read example.com WP_DEBUG /subfolder local path=$(wp_config_path $1 $3) # Last sed is to remove ^M (carriege return character) I don't know why is introduced. [[ -n $1 && -n $2 && -n $path ]] && echo $(grep -iE "^define\([ ]*[\"'\'']$2[\"'\''][ ]*,.*\);.*$" $path | cut -f 2 -d "," -s | sed 's/[ ;")'\'']//g' | sed -e "s/\r//g" ) || return } wp_config_delete() { # Example: wp_config_delete example.com WP_DEBUG # Example: wp_config_delete example.com WP_DEBUG /subfolder local path=$(wp_config_path $1 $3) [[ -n $1 && -n $2 && -n $path ]] && local str=$(grep -iE "^define\([ ]*[\"'\'']$2[\"'\''][ ]*,.*\);.*$" $path) [[ -n $str ]] && sudo sed -i "/$str/d" $path } wp_config_write() { # Example: wp_config_write example.com WP_DEBUG $value # Example: wp_config_write example.com WP_DEBUG $value /subfolder # Example: wp_config_write example.com WP_DEBUG \'test\' <- escaped quotes needed local path=$(wp_config_path $1 $4) # If variable is already defined, update it, if not exist then add a new var at the bottom. if [[ -n $path && -n $(wp_config_read $1 $2 $4) ]]; then sudo sed -i "/$(grep -iE "^define[ ]?\([ ]?[\"'\'']$2[\"'\''].*$" $path)/c \define('$2', $3);" $path elif [[ -n $path ]]; then sudo sed -i "/ stop editing! /i \define('$2', $3);" $path fi } wp_conf_retrieve() { # wp_conf_retrieve example.com # wp_conf_retrieve example.com false false /subfolder # $1 is domain # $2 set to false if you want to skip external_db questions. ONLY NEEDED (true) when you need master-admin privileges for your DB queries! # $3 set to false if you want to disable WP Domain Mapping check (wp_pref will be for the main site when disabled, wp_ instead of wp_3_). # $4 is subfolder # Prevent unwanted values when called multiple times unset wp_config unset wp_dbname unset wp_dbuser unset wp_dbhost unset wp_dbpass unset wp_dbpref unset wp_dbhost_host unset wp_dbhost_port unset wp_dbhost_socket unset wp_dbpref_main unset wp_blogid unset mysql_params unset mysql_param wp_config=$(wp_config_path $1 $4) # Just in case: We should always check is_wp before calling this function # The best error message is the one that never shows up! :) if [[ -z $wp_config || ! -f $wp_config ]]; then echo "${red}[ERROR] WordPress configuration file not found!${end}" >&2 return fi wp_dbname=$( wp_config_read $1 DB_NAME $4 ) wp_dbuser=$( wp_config_read $1 DB_USER $4 ) wp_dbhost=$( wp_config_read $1 DB_HOST $4 ) wp_dbpass=$( wp_config_read $1 DB_PASSWORD $4 ) wp_dbpref=$( grep -F "table_prefix" $wp_config | cut -f 2 -d "'" -s) # wp_dbhost - Always contains the complete string # wp_dbhost_host - Only the host part # wp_dbhost_port - Only the port part (default: 3306) # wp_dbhost_socket - Only the socket path if exist (default: empty) # Example: localhost:3307 (host: localhost, port:3307) # Example: localhost:/var/run/mysqld/mysqld.sock (host: localhost, socket:/var/run/mysqld/mysqld.sock) # Example: mysql.example.com:3306 (host: mysql.example.com, port:3307) wp_dbhost_host=$(echo "$wp_dbhost" | cut -f 1 -d ':') local host_pars=$(echo "$wp_dbhost" | cut -f 2 -d ':' -s) if [[ $host_pars =~ ^[0-9]+$ && $host_pars -ge 0 && $host_pars -le 65535 ]]; then wp_dbhost_port=$host_pars mysql_params=(-P${wp_dbhost_port} --protocol=TCP) # Array because it fails to split the args in MySQL http://mywiki.wooledge.org/BashFAQ/050 mysql_param="$wp_dbhost_port" elif [[ -S $host_pars ]]; then wp_dbhost_socket=$host_pars mysql_params="-S${wp_dbhost_socket}" mysql_param="$wp_dbhost_socket" else wp_dbhost_host=$wp_dbhost fi wp_dbpref_main=${wp_dbpref} # In case of domain mapping this variable always remains with the main site info [[ ${wp_dbhost,,} == "localhost" ]] && wp_dbhost=${wp_dbhost,,} #[[ -z $wp_dbhost_port ]] && wp_dbhost_port="3306" # Only used when External DB and Master-Admin privileges are required! # Example: Cloning site because its needed to create new dbs and users. # Example: Delete site because its required to drop users. if [[ $2 != "false" && $wp_dbhost_host != "localhost" && -z $wp_dbhost_socket && $(is_url $wp_dbhost) =~ ^(http|https|true|http\+ip|https\+ip|ip)$ ]]; then # Don't needed if already exist a login-file with master-admin privileges! # -external-db have priority, mainly to rewrite or update old/wrong credentials. if [[ -n $external_db || $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port any -login-file -master-admin) != "true" ]]; then if [[ -z $external_db && -n $(conf_read external-dbh) && -n $(conf_read external-dbx) && $wp_dbhost == "$(conf_read external-dbh):$(conf_read external-dbx)" ]]; then check_external_db_saved elif [[ -z $external_db && -n $(conf_read external-dbh) ]]; then echo "${dim}[INFO] External Database saved credentials found, but we cannot use it because not the same host!${end}" >&2 fi if [[ -n $external_db ]]; then external_db_parse if [[ $wp_dbhost != $extdb_host ]]; then unset external_db unset extdb_user unset extdb_pass unset extdb_host unset extdb_url unset extdb_port echo "${red}${dim}[ERROR] External DB credentials cannot be used! (host mismatch)${end}" fi fi if [[ -z $external_db ]]; then local done="0" while [[ $done -lt "3" ]] do echo "" >&2 echo "${gre}External DB${blu} '${wp_dbhost}' ${gre}found in:${blu}${dim} ${1}${4} ${end}" >&2 read -p "${blu}External DB Master Username: ${end}" extdb_user read -p "${blu}External DB password: ${hid}" extdb_pass echo "${hidend}" if [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $extdb_user $extdb_pass -master-admin) == "true" ]]; then extdb_host=$wp_dbhost extdb_url=$wp_dbhost_host extdb_port=$wp_dbhost_port external_db="[${extdb_user},${extdb_pass},${extdb_host}]" break fi local done=$(($done+1)) done fi else # We always need these variables for non-WP sites! unset external_db unset extdb_user unset extdb_pass extdb_host=$wp_dbhost extdb_url=$wp_dbhost_host extdb_port=$wp_dbhost_port echo "${blu}${dim}External DB Credentials found!${end}" >&2 fi fi # In case of parked sites with WP domain mapping #if [[ $3 != "false" && $(is_parked $1) == "true" ]]; then # Modified recently, not sure the impact! if [[ $3 != "false" ]]; then # If domain doesn't exist, check if it's a subsite of a subdomain Multisite network. if [[ ! -f /etc/nginx/sites-available/$1 && -f /etc/nginx/sites-available/$(echo $1 | cut -d "." -f 2- -s) ]]; then local maindom=$(echo $1 | cut -d "." -f 2- -s) else local maindom=$1 fi # Parked site don't have support for subfolders, but we send it here as double check. # If we send only the domain and the original request contains the subfolder, we can get a wrong value in return. if [[ $(is_wp_multisite $maindom $4) =~ ^(subdomain|subdirectory)$ ]]; then local dbq="USE $wp_dbname; SELECT blog_id FROM ${wp_dbpref}blogs WHERE domain='$1' OR domain='www.$1';" if [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then local bid=$(sudo mysql --connect-timeout=10 --user=admin -e "$dbq" "${mysql_params[@]}") elif [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $wp_dbuser $wp_dbpass) == "true" ]]; then local bid=$(sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" -e "$dbq") fi wp_blogid=$(echo $bid | cut -f 2 -d " " -s) # Number 1 is main site, so we don't need to change the WP prefix. if [[ $wp_blogid =~ ^[0-9]+$ && $wp_blogid -gt 1 ]]; then wp_dbpref="${wp_dbpref}${wp_blogid}_" echo "${blu}${dim}Site${end}${dim} ${1}${4} ${blu}is a subsite (${wp_dbpref}) in a WP Multisite Network!${end}" >&2 elif [[ $wp_blogid == 1 ]]; then echo "${blu}${dim}Site${end}${dim} ${1}${4} ${blu}is the main site in a WP Multisite Network!${end}" >&2 else [[ $wp_blogid != 1 ]] && wp_blogid="" fi fi fi } # Remove Installation Files app_purge() { sudo rm $HOME/webinoly.tar sudo rm -rf /opt/webinoly/usr } api-events_update() { conf_write status-api $1 if [[ -f /opt/webinoly/lib/api-events ]]; then source /opt/webinoly/lib/api-events api-events_catch_status $1 fi } remove_nginx_default_server() { if [[ -n $1 && -f /etc/nginx/sites-available/$1 ]]; then sudo sed -i "s/listen 80 default_server;/listen 80;/" /etc/nginx/sites-available/$1 sudo sed -i "s/listen \[::\]:80 default_server;/listen [::]:80;/" /etc/nginx/sites-available/$1 sudo sed -i "s/listen 443 ssl http2 default_server;/listen 443 ssl http2;/" /etc/nginx/sites-available/$1 sudo sed -i "s/listen \[::\]:443 ssl http2 default_server;/listen [::]:443 ssl http2;/" /etc/nginx/sites-available/$1 sudo sed -i '/WebinolyStartBlackhole/,/WebinolyEndBlackhole/{/.*/d}' /etc/nginx/sites-available/$1 fi } check_for_parameters() { # Global variables: domain, domain_name, domain_port, tld, subdomain, main_domain, sub_domain, empty_param # Note: domain and domain_name are the same except when port is present (example.com:22), then port is removed from domain_name (example.com) # Check for domain parameter if is first parameter and have no hyphen at the begining. if [[ -n $1 && $(echo $1 | cut -c-1) != "-" ]]; then domain=$1 domain_name=$1 shift # Check for port and remove it! domain_port=$(echo $domain | cut -d':' -f 2- -s) if [[ $domain_port =~ ^[0-9]+$ && $domain_port -ge 0 && $domain_port -le 65535 ]]; then domain_name=$(echo $domain | cut -d':' -f 1 -s) else unset domain_port fi local count=1 while true; do tld=$(echo $domain_name | cut -d'.' -f ${count}- -s) if grep -Fxq "$tld" /opt/webinoly/lib/public_suffix_list.dat || [ -z $tld ]; then break fi count=$[$count+1] done [[ $count -gt 2 ]] && subdomain="true" || subdomain="false" if [[ $subdomain == "true" && -n $tld ]]; then main_domain=$(echo $domain_name | cut -d'.' -f $[$count-1]- -s) sub_domain=$(echo $domain_name | cut -d'.' -f -$[$count-2] -s) fi fi for arg in $@; do local check=$(echo "${arg}" | cut -c-1) local arg=${arg:1} local par=$(echo "${arg}" | cut -d'=' -f 1 -s) local val=$(echo "${arg}" | cut -d'=' -f 2- -s) [[ -z $par ]] && par=$arg [[ -z $val ]] && val=true # Convert to lowercase and support for hyphen in arguments. par=${par,,} par=$(echo $par | sed "s/-/_/g") # Only valid variables names and check for begin with hyphen. if [[ $check == "-" && $par =~ ^[a-zA-Z_][a-zA-Z_0-9]*$ ]]; then [[ -n $par ]] && eval $par=\$val else echo "${red}[ERROR] Invalid parameters! ${end}" exit 1 fi done [[ -z $@ ]] && empty_param="true" } is_ip() { # Check for valid IPv4 and IPv6 values, including CIDR. if [[ -n $1 && $1 =~ ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$ || $1 =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/([0-9]|[1-9][0-9]|1[0-2][0-8]))?$ ]]; then echo "true" else echo "false" fi } is_domain() { # Only numerals 0-9, basic Latin letters, both lowercase and uppercase, hyphen. if ! [[ $1 =~ ^[\.0-9A-Za-z\-]+$ ]]; then echo "false" # Check Lenght elif [[ ${#1} -gt 67 ]]; then echo "false" # Can not start or end with a hyphen elif [[ $(echo "${1}" | cut -c-1) == "-" || $(echo "${1}" | rev | cut -c-1) == "-" ]]; then echo "false" # Can not contain two points together and can not start or end with a point elif [[ $1 == *..* || $(echo "${1}" | cut -c-1) == "." || $(echo "${1}" | rev | cut -c-1) == "." ]]; then echo "false" # Check if IP address elif [[ $(is_ip $1) == "true" ]]; then echo "false" else echo "true" fi } is_url_path() { # Should start with / and after that all should be valid characters. # https://stackoverflow.com/questions/4669692/valid-characters-for-directory-part-of-a-url-for-short-links if [[ -n $1 && $1 =~ ^\/([\]A-Za-z0-9_\/\.:\!\*\'\[\(\)\;@\&\=\+\$\,\?#\~\%\-]+)?$ ]]; then echo "true" else echo "false" fi } is_url() { # Output: http,https,true,http+ip,https+ip,ip,http+unix,https+unix,unix,false - Example: $(is_url $domain) # Global variables when -split is set: url_type, url_scheme, url_host, url_path, url_port - Example: is_url $domain -split # Examples: # example.com -> true # http://example.com -> http # https://example.com -> https # 1.1.1.1 -> ip # http://1.1.1.1 -> http+ip # https://1.1.1.1 -> https+ip # unix:/tmp/backend.socket:/uri/ -> unix # http://unix:/tmp/backend.socket:/uri/ -> http+unix # https://unix:/tmp/backend.socket:/uri/ -> https+unix # Unix sockets are mainly used in upstream and proxy_pass # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass # Important, because they can have a previous value when runs multiple times. unset url_scheme unset url_host unset url_path unset url_port unset url_type # Here we are assuming URL with scheme local scheme=$(echo "${1,,}" | cut -d':' -f 1 -s) local host=$(echo "${1,,}" | cut -d'/' -f 3 -s) [[ $(echo $host | cut -d':' -f 2 -s) =~ ^[0-9]+$ ]] && local host=$(echo $host | cut -d':' -f 1) # We need this 2dn step to prevent http://example.com:/tmp (empty port) local path=$(echo "${1,,}" | cut -d'/' -f 4- -s) local port=$(echo "${1,,}" | cut -d'/' -f 3 -s | cut -d':' -f 2 -s) local out="false" # In case of URL with no-scheme local hosted=$(echo "${1,,}" | cut -d'/' -f 1) [[ $(echo $hosted | cut -d':' -f 2 -s) =~ ^[0-9]+$ ]] && hosted=$(echo $hosted | cut -d':' -f 1) if [[ $1 =~ ^((http|https)+\:\/\/)?unix:\/[^\:]+(:\/.*)?$ ]]; then unset port if [[ $scheme == "unix" ]]; then local host="$(echo "${1,,}" | cut -d':' -f 1-2 -s)" local path=$(echo "${1,,}" | cut -d':' -f 3- -s) local out="unix" unset scheme else local host="$(echo "${1,,}" | cut -d':' -f 2-3 -s)" local host=${host:2} local path=$(echo "${1,,}" | cut -d':' -f 4- -s) local out="${scheme}+unix" fi elif [[ $scheme =~ ^(http|https)$ ]]; then if [[ $(is_domain $host) != "true" && $(is_ip $host) != "true" ]]; then local out="false" elif [[ -n $path && $(is_url_path /$path) != "true" ]]; then local out="false" elif [[ -n $port ]] && ! [[ $port =~ ^[0-9]+$ && $port -ge 0 && $port -le 65535 ]]; then local out="false" elif [[ $(is_ip $host) == "true" ]]; then local out="${scheme}+ip" else local out="${scheme}" fi elif [[ $(is_domain $hosted) == "true" || $(is_ip $hosted) == "true" ]]; then local scheme="" local host=$hosted local path=$(echo "${1,,}" | cut -d'/' -f 2- -s) local port=$(echo "${1,,}" | cut -d'/' -f 1 | cut -d':' -f 2 -s) if [[ -n $path && $(is_url_path /$path) != "true" ]]; then local out="false" elif [[ -n $port ]] && ! [[ $port =~ ^[0-9]+$ && $port -ge 0 && $port -le 65535 ]]; then local out="false" elif [[ $(is_ip $hosted) == "true" ]]; then local out="ip" else local out="true" fi else local out="false" fi # If path doesn't begin with /, then add it! # If there is no path, but the url ends with /, then / is a valid path, so show it as path! [[ ( -n $path && $(echo "$path" | cut -c-1) != "/" ) || ( -z $path && $(echo "${1}" | rev | cut -c-1) == "/" ) ]] && local path="/$path" [[ -n $path && $out =~ ^(unix|http\+unix|https\+unix)$ ]] && local path=":${path}" # Final validation, just because we need to be really sure! (double check!!) if [[ -n $scheme && -n $host && -n $port ]]; then local url_final="${scheme}://${host}:${port}${path}" elif [[ -z $scheme && -n $host && -n $port ]]; then local url_final="${host}:${port}${path}" elif [[ -n $scheme && -n $host && -z $port ]]; then local url_final="${scheme}://${host}${path}" elif [[ -z $scheme && -n $host && -z $port ]]; then local url_final="${host}${path}" fi [[ $url_final != ${1,,} ]] && out="false" # SHORT CIRCUIT!!! # Final output! if [[ $2 == "-split" && $out != "false" ]]; then [[ -n $scheme ]] && url_scheme=$scheme [[ -n $path ]] && url_path=$path [[ -n $port ]] && url_port=$port url_host=$host # We are very sure these two vars are never empty! ;) url_type=$out # empty/unset when false elif [[ $2 != "-split" ]]; then echo $out fi } is_ssl() { [[ -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "ssl_certificate_key") ]] && echo "true" || echo "false" } is_ssl_le() { [[ -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "ssl_certificate_key /etc/letsencrypt/live/") && -z $(grep -F "WebinolySSLCustomCert" /etc/nginx/sites-available/$1) ]] && echo "true" || echo "false" } is_ssl_staging() { [[ -f /etc/letsencrypt/renewal/$1.conf && -n $(grep -E "^server = " /etc/letsencrypt/renewal/$1.conf | cut -d'=' -f 2 -s | grep -F "acme-staging-") ]] && echo "true" || echo "false" } is_ssl_wildcard() { [[ -f /etc/letsencrypt/renewal/$1.conf && -n $(sudo certbot certificates --cert-name $1 2>/dev/null | grep -E "Domains:.* \*.$1") ]] && echo "true" || echo "false" } is_html() { # $1 = domain, $2 = subfolder [[ -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "location $2/ { try_files ") ]] && echo "true" || echo "false" } is_php() { # $1 = domain, $2 = subfolder if [[ -f /etc/nginx/sites-available/$1 ]]; then local isphp=$(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -E " common/phpx?\.conf;") [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") if [[ -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -E " apps.d/$1$subn-phpcache.conf;") ]]; then echo "true" elif [[ -n $2 && $(is_wp $1 $2) == "false" ]]; then if [[ -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -E " apps\.d/$1$subn-phpx?\.conf;") ]]; then echo "true" else echo "false" fi elif [[ -n $isphp && $(is_wp $1) == "false" ]]; then echo "true" else echo "false" fi else echo "false" fi } is_proxy() { # $1 = domain, $2 = subfolder [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") [[ -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F " apps.d/$1$subn-proxy.conf;") ]] && echo "true" || echo "false" } is_dedicated_proxy() { # $1 = domain, $2 = subfolder [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") [[ -f /etc/nginx/sites-available/$1 && -z $(grep -F " common/locations.conf;" /etc/nginx/sites-available/$1) && -z $(grep -E "include common/headers-.*.conf;" /etc/nginx/sites-available/$1) && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F " apps.d/$1$subn-proxy.conf;") ]] && echo "true" || echo "false" } is_dedicated_proxy_domain() { # $1 = domain [[ -f /etc/nginx/sites-available/$1 && -z $(grep -F " common/locations.conf;" /etc/nginx/sites-available/$1) && -z $(grep -E "include common/headers-.*.conf;" /etc/nginx/sites-available/$1) && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -E " apps.d/${1}.*-proxy.conf;") ]] && echo "true" || echo "false" } is_forward() { if [[ -f /etc/nginx/sites-available/$1 ]]; then local index=$(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "index ") local return=$(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "return 301 ") [[ ! -d /var/www/$1 && -z $index && -n $return ]] && echo "true" || echo "false" else echo "false" fi } is_parked() { # Last weird proxy check is for parked with proxy main site [[ -f /etc/nginx/sites-available/$1 && ! -d /var/www/$1 && $(is_proxy $1) == "false" && $(is_forward $1) == "false" && ( $(is_html $1) == "true" || $(is_php $1) == "true" || $(is_wp $1) == "true" || $(is_proxy $(grep -E "include /var/www/.*/\*-nginx.conf;" /etc/nginx/sites-available/$1 | cut -d'/' -f 4 -s)) == "true" ) ]] && echo "true" || echo "false" } is_wp() { # $1 = domain, $2 = WP subfolder [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") if [[ -z $2 && -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F " common/wpcommon") ]]; then [[ -f $(wp_config_path $1) ]] && echo "true" || echo "false" elif [[ -n $2 && -f /etc/nginx/sites-available/$1 && -f /etc/nginx/apps.d/$1$subn-wpcommon.conf ]]; then [[ -f $(wp_config_path $1 $2) ]] && echo "true" || echo "false" else echo "false" fi } is_cache() { # $1 = domain, $2 = WP subfolder [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") if [[ -z $2 && -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F " common/wpfc.conf;") ]]; then echo "wp" elif [[ -n $2 && -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/# WebinolyCustom/,$p' /etc/nginx/sites-available/$1 | grep -F "$1$subn-wpfc.conf;") ]]; then echo "wp" elif [[ -z $2 && -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -E " apps.d/$1-(wp|php)cache.conf;") ]]; then echo "custom" elif [[ -n $2 && -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/# WebinolyCustom/,$p' /etc/nginx/sites-available/$1 | grep -E "$1$subn-(wp|php)cache.conf;") ]]; then echo "custom" elif [[ -f /etc/nginx/apps.d/$1$subn-proxy.conf && -n $(grep -F "# WebinolyProxyCacheStart" /etc/nginx/apps.d/$1$subn-proxy.conf) && -z $(grep -F "proxy_cache off;" /etc/nginx/apps.d/$1$subn-proxy.conf) ]]; then echo "proxy" else echo "false" fi } is_wp_multisite() { if [[ $(is_wp $1 $2) == "true" ]]; then wp_conf_retrieve $1 false false $2 # 3th parameter should always be 'false' to prevent an infinite loop! if [[ -n $wp_dbhost_host && -n $wp_dbname && -n $wp_dbpref ]]; then local dbsetup="SELECT * FROM information_schema.tables WHERE table_schema = '$wp_dbname' AND table_name = '${wp_dbpref}sitemeta' LIMIT 1;" local dbsetuc="USE $wp_dbname; SELECT meta_value FROM ${wp_dbpref}sitemeta where meta_key='subdomain_install';" if [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then wpmu=$(sudo mysql --connect-timeout=10 --user=admin -e "$dbsetup" "${mysql_params[@]}") [[ -n $wpmu ]] && mutype=$(sudo mysql --connect-timeout=10 --user=admin -e "$dbsetuc" "${mysql_params[@]}") elif [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $wp_dbuser $wp_dbpass) == "true" ]]; then wpmu=$(sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" -e "$dbsetup") [[ -n $wpmu ]] && mutype=$(sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" -e "$dbsetuc") fi if [[ $(echo $mutype | cut -f 2 -d " " -s) == "1" ]]; then echo "subdomain" elif [[ -n $wpmu ]]; then echo "subdirectory" else echo "false" fi else echo "false" fi else echo "false" fi } is_wp_installed() { # This function check if WP db exists. # When you create a WP site, DB is created only after the initial WP installation wizard is completed. if [[ $(is_wp $1 $2) == "true" ]]; then wp_conf_retrieve $1 false false $2 # It makes no sense checking for mapped domains, that's why is set to false. # is_wp_installed will return true even if domain is not mapped, only a domain parked pointing to a WP site. # if we want to check for mapped domains: wp_conf_retrieve $1 true false $2 > /dev/null (silenced echoed messages because affects this function) # but that makes no-sense because even if it's not mapped, it will return the main site data # Until now, we don't need a "is_domain_mapped" function, maybe we can change "is_wp_installed" to only be true for main domain sites, not parked (if it's not mapped). This can change in the future!!! if [[ -n $wp_dbhost_host && -n $wp_dbname && -n $wp_dbpref ]]; then local dbsetup="SELECT * FROM information_schema.tables WHERE table_schema = '$wp_dbname' AND table_name = '${wp_dbpref}options' LIMIT 1;" if [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then [[ -n $(sudo mysql --connect-timeout=10 --user=admin -e "$dbsetup" "${mysql_params[@]}") ]] && echo "true" || echo "false" elif [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $wp_dbuser $wp_dbpass) == "true" ]]; then [[ -n $(sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" -e "$dbsetup") ]] && echo "true" || echo "false" else echo "false" fi else echo "false" fi else echo "false" fi } is_wp_debug() { [[ $( wp_config_read $1 WP_DEBUG $2 ) == "true" ]] && echo "true" || echo "false" } is_wp_auth() { [[ -n $2 ]] && local subn=$(echo $2 | sed "s/\//_/g") if [[ -z $2 && -f /etc/nginx/sites-available/$1 && -n $( grep -F "wpcommon.conf;" /etc/nginx/sites-available/$1 ) ]]; then echo "true" elif [[ -n $2 && -f /etc/nginx/sites-available/$1 && -n $( grep -F "wpcommon.conf;" /etc/nginx/apps.d/$domain$subn-php.conf ) ]]; then echo "true" else echo "false" fi } is_force_redirect() { if [[ -f /etc/nginx/sites-available/$1 && -n $( grep -F "WebinolyWWWredirectStart" /etc/nginx/sites-available/$1 ) ]]; then [[ -n $(sed -n -e '/WebinolyWWWredirectStart/,/WebinolyWWWredirectEnd/p' /etc/nginx/sites-available/$1 | grep -F "server_name www.${1};") ]] && echo "root" || echo "www" else echo "off" fi } is_subfolder() { # $1 = domain, $2 = subfolder if [[ -n $1 && -n $2 ]]; then if [[ $(is_wp $1 $2) == "true" ]]; then echo "wp" elif [[ $(is_php $1 $2) == "true" ]]; then echo "php" elif [[ $(is_proxy $1 $2) == "true" ]]; then echo "proxy" elif [[ $(is_html $1 $2) == "true" ]]; then echo "html" elif [[ -d /var/www/${1}/htdocs${2} && -z $(find /var/www/${1}/htdocs${2} -maxdepth 1 -type f | head -n 1) ]]; then echo "empty" elif [[ -d /var/www/${1}/htdocs${2} ]]; then echo "true" else echo "false" fi else echo "false" fi } is_empty_root_site() { # $1 = domain if [[ -n $1 ]]; then if [[ -f /etc/nginx/sites-available/$1 && -z $(grep -F " common/locations.conf;" /etc/nginx/sites-available/$1) && -z $(grep -E "include common/headers-.*.conf;" /etc/nginx/sites-available/$1) ]]; then echo "full" elif [[ $(is_wp $1) == "true" || $(is_php $1) == "true" || $(is_proxy $1) == "true" || $(is_html $1) == "true" || $(is_parked $1) == "true" || $(is_forward $1) == "true" ]]; then echo "false" elif [[ -f /etc/nginx/sites-available/$1 ]]; then echo "true" else echo "false" fi else echo "false" fi } is_dd_log() { if [[ $1 == "nginx" ]]; then local ddpath="/etc/datadog-agent/conf.d/nginx.d/conf.yaml" elif [[ $1 == "fpm" ]]; then local ddpath="/etc/datadog-agent/conf.d/php_fpm.d/conf.yaml" elif [[ $1 == "mysql" ]]; then local ddpath="/etc/datadog-agent/conf.d/mysql.d/conf.yaml" elif [[ $1 == "redis" ]]; then local ddpath="/etc/datadog-agent/conf.d/redisdb.d/conf.yaml" elif [[ $1 == "global" ]]; then [[ -f /etc/datadog-agent/datadog.yaml && -n $(grep -F "WebinolyLogsStart" /etc/datadog-agent/datadog.yaml) ]] && local global="true" else local ddpath="" fi [[ -n $global || ( -n $ddpath && -f $ddpath && -n $(grep -F "WebinolyDatadogLogsStart" $ddpath)) ]] && echo "true" || echo "false" } is_log() { [[ -f /etc/nginx/sites-available/$1 && -n $(sed -n -e '/WebinolyNginxServerStart/,$p' /etc/nginx/sites-available/$1 | grep -F "nginx/$1.access.log ") ]] && echo "true" || echo "false" } escaped_string() { # Escaped characters: Put a backslash before $.*/[\]^()+ echo $(echo $1 | sed "s#/#\\\/#g" | sed "s/\./\\\./g" | sed "s/\\$/\\\\$/g" | sed "s/\*/\\\*/g" | sed "s/\\\/\\\\/g" | sed "s/\[/\\\[/g" | sed "s/\]/\\\]/g" | sed "s/\^/\\\^/g" | sed -E "s/\(/\\\(/g" | sed -E "s/\)/\\\)/g" | sed "s/\+/\\\+/g") } site_type() { if [[ -z $1 ]]; then echo "false" elif [[ $(is_parked $1) == "true" ]]; then echo "Parked" elif [[ $(is_wp $1) == "true" ]]; then echo "WordPress" elif [[ $(is_php $1) == "true" ]]; then echo "PHP" elif [[ $(is_html $1) == "true" ]]; then echo "HTML" elif [[ $(is_dedicated_proxy_domain $1) == "true" ]]; then echo "Dedicated Reverse Proxy" elif [[ $(is_proxy $1) == "true" ]]; then echo "Reverse Proxy" elif [[ $(is_forward $1) == "true" ]]; then echo "Forward" elif [[ $(is_empty_root_site $1) == "full" ]]; then echo "Empty Blank" elif [[ $(is_empty_root_site $1) == "true" ]]; then echo "Subfolders" else echo "Unknown" fi } email_update() { echo "${blu}" if [[ -z $1 && ( -z $email || $email == "true" ) ]]; then read -p "Email address: ${end}" mail else [[ -z $1 ]] && local mail=$email || local mail=$1 fi if [[ $mail =~ ^[a-z0-9_\+-]+(\.[a-z0-9_\+-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,4})$ ]]; then conf_write mail $mail [[ ! -f /root/.forward ]] && sudo touch /root/.forward || sudo truncate -s 0 /root/.forward sudo echo "$mail" >> /root/.forward if [[ -s /var/spool/cron/crontabs/root && -n $( sudo grep -F "MAILTO=" /var/spool/cron/crontabs/root ) ]]; then sudo sed -i "/MAILTO=/c \MAILTO=${mail}" /var/spool/cron/crontabs/root echo "${gre}${dim}Cronjob (root) MAILTO has been updated!${end}" fi if [[ -d /etc/letsencrypt/renewal ]]; then sudo certbot update_account --email $mail --no-eff-email echo "${gre}${dim}Let's Encrypt account email address has been updated!${end}" fi echo "${gre}Email address has been successfuly validated, updated and saved!" else echo "${red}Please enter a valid email address!" fi echo "${end}" } edit_wp_db_url_multisite() { #Subfolder is not allowed here because is not needed, parked and force-www are not allowed in subfolders. #Example: edit_wp_db_url_multisite olddomain.com newdomain.com 2 # $3 - WP BlogID to force (optional) if [[ -n $1 && -n $2 && $(is_wp_multisite $1) =~ ^(subdomain|subdirectory)$ ]]; then wp_conf_retrieve $1 false true $subfolder # Force WP blogID if [[ -n $3 && $3 =~ ^[0-9]+$ ]]; then local wp_dbpref="${wp_dbpref}${3}_" local wp_blogid=$3 local dbsetup="SELECT * FROM information_schema.tables WHERE table_schema = '$wp_dbname' AND table_name = '${wp_dbpref}options' LIMIT 1;" if [[ $wp_dbhost_host == "localhost" && -n $(sudo mysql --connect-timeout=10 --user=admin -e "$dbsetup" "${mysql_params[@]}") ]]; then echo "${blu}${dim}WordPress blog ID (${wp_dbpref}) found and validated in a WP Multisite Network!${end}" >&2 elif [[ $wp_dbhost_host != "localhost" && -n $(sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" -e "$dbsetup") ]]; then echo "${blu}${dim}WordPress blog ID (${wp_dbpref}) found and validated in a WP Multisite Network!${end}" >&2 else echo "${red}${dim}[ERROR] WordPress blog ID (${wp_dbpref}) not found!${end}" >&2 wp_dbpref="" wp_blogid="" fi fi if [[ -n $wp_dbhost_host && -n $wp_dbname && -n $wp_dbpref_main && -n $wp_blogid ]]; then if [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then sudo mysql --connect-timeout=10 --user=admin "${mysql_params[@]}" <<_EOF_ USE $wp_dbname; UPDATE ${wp_dbpref_main}blogs SET domain='$2' WHERE blog_id='${wp_blogid}'; UPDATE ${wp_dbpref_main}blogs SET path='/' WHERE blog_id='${wp_blogid}'; _EOF_ elif [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $wp_dbuser $wp_dbpass) == "true" ]]; then sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" <<_EOF_ USE $wp_dbname; UPDATE ${wp_dbpref_main}blogs SET domain='$2' WHERE blog_id='${wp_blogid}'; UPDATE ${wp_dbpref_main}blogs SET path='/' WHERE blog_id='${wp_blogid}'; _EOF_ else echo "${red}${dim}[ERROR] WordPress Multisite database cannot be updated!${end}" >&2 return 1 fi else echo "${red}${dim}[ERROR] WordPress Multisite database cannot be updated!${end}" >&2 return 1 fi fi } edit_wp_db_url() { #Example: edit_wp_db_url example.com "http://${domain}${subfolder}" #Example: edit_wp_db_url example.com "http://${domain}${subfolder}" /subfolder #IMPORTANT NOTE: Always be sure to take "www" Force-Redirect into consideration before updating WP Url in database. if [[ -n $1 && -n $2 && $(is_wp_installed $1 $3) == "true" ]]; then wp_conf_retrieve $1 false true $3 if [[ -n $wp_dbhost_host && -n $wp_dbname && -n $wp_dbpref ]]; then if [[ $wp_dbhost_host == "localhost" && $(check_mysql_connection localhost $mysql_param) == "true" ]]; then sudo mysql --connect-timeout=10 --user=admin "${mysql_params[@]}" <<_EOF_ USE $wp_dbname; UPDATE ${wp_dbpref}options SET option_value='$2' WHERE option_name='home'; UPDATE ${wp_dbpref}options SET option_value='$2' WHERE option_name='siteurl'; _EOF_ echo "${gre}${dim}WordPress site${blu} ${1}${3} ${gre}database URL updated! ${blu}(${2})${end}" elif [[ $(check_mysql_connection $wp_dbhost_host $wp_dbhost_port $wp_dbuser $wp_dbpass) == "true" ]]; then sudo mysql --defaults-group-suffix=_${wp_dbhost_host}:${wp_dbhost_port}_${wp_dbuser} --connect-timeout=10 -h "$wp_dbhost_host" -P "$wp_dbhost_port" -u"$wp_dbuser" <<_EOF_ USE $wp_dbname; UPDATE ${wp_dbpref}options SET option_value='$2' WHERE option_name='home'; UPDATE ${wp_dbpref}options SET option_value='$2' WHERE option_name='siteurl'; _EOF_ echo "${gre}${dim}WordPress site${blu} ${1}${3} ${gre}database URL updated! ${blu}(${2})${end}" else echo "${red}${dim}[ERROR] WordPress database cannot be updated!${end}" >&2 fi else echo "${red}${dim}[ERROR] WordPress database cannot be updated!${end}" >&2 fi fi } wp_db_update() { # Same as edit_wp_db_url, but it also check and includes WP in subfolders. # if main site is WP. [[ $(is_wp $1) == "true" ]] && edit_wp_db_url $1 $2 # Check if site contains WP in subfolders. for site in "/etc/nginx/apps.d/${1}_"*-wpcommon.conf do local subwp="/$(echo $site | cut -f 2- -d "_" -s | cut -f -1 -d "-" -s | sed "s/_/\//g")" [[ -n $subwp && -f /var/www/${1}/htdocs$subwp/wp-config.php ]] && edit_wp_db_url $1 ${2}${subwp} $subwp done } db_role_check() { if ! [[ $1 =~ ^(basic|limited|extra|complete|full|all|grant)$ ]]; then echo "${red}[ERROR] Please, enter a valid database role!${end}" [[ -n $db_role ]] && db_role="" [[ $2 == "unattended" ]] && echo "${dim}We will use the default db role!${end}" || exit 1 elif [[ $1 == "basic" ]]; then echo "${dim}[WARNING] You have set basic privileges for your databases. Some sites, like WordPress, could NOT work properly!${end}" fi } db_user_role() { # Can't be used on not global users: FILE,REPLICATION CLIENT,REPLICATION SLAVE,PROCESS,SHOW DATABASES,CREATE USER,RELOAD,GRANT,SUPER,SHUTDOWN # FILE Privilege not supported by external DB's: http://cloudofnines.blogspot.com/2014/09/in-rds-instances-file-privilege-for.html # AWS RDS Reference: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.MasterAccounts.html # ALL PRIVILEGES not supported for external DB's. [[ -z $db_role ]] && db_role=$(conf_read dbrole) if [[ $db_role == "all" ]]; then local priv="ALL PRIVILEGES" elif [[ $db_role == "basic" ]]; then local priv="SELECT,INSERT,UPDATE,DELETE" elif [[ $db_role == "limited" ]]; then local priv="SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER" elif [[ $db_role == "extra" ]]; then local priv="SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,LOCK TABLES" elif [[ $db_role == "complete" ]]; then local priv="SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,EXECUTE,CREATE VIEW,SHOW VIEW,CREATE ROUTINE,ALTER ROUTINE,EVENT,TRIGGER" elif [[ $db_role == "grant" ]]; then local priv="SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,EXECUTE,CREATE VIEW,SHOW VIEW,CREATE ROUTINE,ALTER ROUTINE,EVENT,TRIGGER,REFERENCES,LOCK TABLES,GRANT OPTION" else # full (DEFAULT) local priv="SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER,CREATE TEMPORARY TABLES,EXECUTE,CREATE VIEW,SHOW VIEW,CREATE ROUTINE,ALTER ROUTINE,EVENT,TRIGGER,REFERENCES,LOCK TABLES" fi echo $priv } dbword_check() { local win="$1" local RANDOM_NAME="Webinoly_$(pwgen -s -1)" # Trim start/end spaces and quotes win=$(echo ${win//\'}) win=$(echo ${win//\"} | xargs) # Check Lenght # MySQL user names are up to 32 characters long. # MariaDB - Usernames can be up to 80 characters long before 10.6 and starting from 10.6 it can be 128 characters long. # DB name is 64 for both! [[ ( ${#win} -gt 64 && $2 != "user" ) ]] && win=$RANDOM_NAME [[ ( ${#win} -gt 32 && $2 == "user" ) && $(conf_read db-engine) == "mysql" ]] && win=$RANDOM_NAME [[ ( ${#win} -gt 80 && $2 == "user" ) && $(conf_read db-engine) != "mysql" ]] && win=$RANDOM_NAME # Reserved words - https://mariadb.com/kb/en/library/reserved-words/ https://dev.mysql.com/doc/mysqld-version-reference/en/keywords-8-0.html # https://mariadb.com/kb/en/library/identifier-names/ # We have both MySQL and MariaDB reserved words. # We have information_schema.keywords, but we prefer do this check manually because mariadb doesn't have a way to know which keywords are reserved. if [[ ${win^^} =~ ^(ACCESSIBLE|ADD|ALL|ALTER|ANALYZE|AND|AS|ASC|ASENSITIVE|BEFORE|BETWEEN|BIGINT|BINARY|BLOB|BOTH|BY|CALL|CASCADE|CASE|CHANGE|CHAR|CHARACTER|CHECK|COLLATE|COLUMN|CONDITION|CONSTRAINT|CONTINUE|CONVERT|CREATE|CROSS|CUBE|CUME_DIST|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DATABASES|DAY_HOUR|DAY_MICROSECOND|DAY_MINUTE|DAY_SECOND|DEC|DECIMAL|DECLARE|DEFAULT|DELAYED|DELETE|DENSE_RANK|DESC|DESCRIBE|DETERMINISTIC|DISTINCT|DISTINCTROW|DIV|DOUBLE|DROP|DUAL|EACH|ELSE|ELSEIF|EMPTY|ENCLOSED|ESCAPED|EXCEPT|EXISTS|EXIT|EXPLAIN|FALSE|FETCH|FIRST_VALUE|FLOAT|FLOAT4|FLOAT8|FOR|FORCE|FOREIGN|FROM|FULLTEXT|FUNCTION|GENERATED|GET|GRANT|GROUP|GROUPING|GROUPS|HAVING|HIGH_PRIORITY|HOUR_MICROSECOND|HOUR_MINUTE|HOUR_SECOND|IF|IGNORE|IN|INDEX|INFILE|INNER|INOUT|INSENSITIVE|INSERT|INT|INT1|INT2|INT3|INT4|INT8|INTEGER|INTERSECT|INTERVAL|INTO|IO_AFTER_GTIDS|IO_BEFORE_GTIDS|IS|ITERATE|JOIN|JSON_TABLE|KEY|KEYS|KILL|LAG|LAST_VALUE|LATERAL|LEAD|LEADING|LEAVE|LEFT|LIKE|LIMIT|LINEAR|LINES|LOAD|LOCALTIME|LOCALTIMESTAMP|LOCK|LONG|LONGBLOB|LONGTEXT|LOOP|LOW_PRIORITY|MASTER_BIND|MASTER_SSL_VERIFY_SERVER_CERT|MATCH|MAXVALUE|MEDIUMBLOB|MEDIUMINT|MEDIUMTEXT|MIDDLEINT|MINUTE_MICROSECOND|MINUTE_SECOND|MOD|MODIFIES|NATURAL|NOT|NO_WRITE_TO_BINLOG|NTH_VALUE|NTILE|NULL|NUMERIC|OF|ON|OPTIMIZE|OPTIMIZER_COSTS|OPTION|OPTIONALLY|OR|ORDER|OUT|OUTER|OUTFILE|OVER|PARTITION|PERCENT_RANK|PRECISION|PRIMARY|PROCEDURE|PURGE|RANGE|RANK|READ|READS|READ_WRITE|REAL|RECURSIVE|REFERENCES|REGEXP|RELEASE|RENAME|REPEAT|REPLACE|REQUIRE|RESIGNAL|RESTRICT|RETURN|REVOKE|RIGHT|RLIKE|ROW|ROWS|ROW_NUMBER|SCHEMA|SCHEMAS|SECOND_MICROSECOND|SELECT|SENSITIVE|SEPARATOR|SET|SHOW|SIGNAL|SMALLINT|SPATIAL|SPECIFIC|SQL|SQLEXCEPTION|SQLSTATE|SQLWARNING|SQL_BIG_RESULT|SQL_CALC_FOUND_ROWS|SQL_SMALL_RESULT|SSL|STARTING|STORED|STRAIGHT_JOIN|SYSTEM|TABLE|TERMINATED|THEN|TINYBLOB|TINYINT|TINYTEXT|TO|TRAILING|TRIGGER|TRUE|UNDO|UNION|UNIQUE|UNLOCK|UNSIGNED|UPDATE|USAGE|USE|USING|UTC_DATE|UTC_TIME|UTC_TIMESTAMP|VALUES|VARBINARY|VARCHAR|VARCHARACTER|VARYING|VIRTUAL|WHEN|WHERE|WHILE|WINDOW|WITH|WRITE|XOR|YEAR_MONTH|ZEROFILL|CURRENT_ROLE|DELETE_DOMAIN_ID|DO_DOMAIN_IDS|GENERAL|IGNORE_DOMAIN_IDS|IGNORE_SERVER_IDS|MASTER_HEARTBEAT_PERIOD|OFFSET|PAGE_CHECKSUM|PARSE_VCOL_EXPR|POSITION|REF_SYSTEM_ID|RETURNING|SLOW|STATS_AUTO_RECALC|STATS_PERSISTENT|STATS_SAMPLE_PAGES)$ ]]; then win=$RANDOM_NAME fi # Only numerals 0-9, basic Latin letters, both lowercase and uppercase, dollar sign, underscore. [[ $win =~ ^[0-9A-Za-z\$_]+$ ]] || win=$RANDOM_NAME # Dollar sign at the beggining not allowed. [[ $(echo "${win}" | cut -c-1) == "$" ]] && win=$RANDOM_NAME # Can not contain only numbers [[ $win =~ ^[0-9]+$ ]] && win=$RANDOM_NAME # Floating point number confusing [[ ${win:0:1} =~ ^[0-9]+$ && ${win:1:1} == "e" ]] && win=$RANDOM_NAME echo $win } cnf_delete() { #Example: cnf_delete error_log [[ -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf ]] && sudo sed -i "/^$1 /d" $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf } cnf_write() { #Example: cnf_write error_log /var/log/mysql/error.log cnf_delete $1 mysql_default_cnf [[ -n $2 ]] && local value="= $2" echo "$1 $value" >> $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf } cnf_read() { #Example: cnf_read error_log [[ -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf ]] && echo $( grep -P "^$1 = " $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf | cut -f 2 -d "=" -s | sed 's/ //g' ) } mysql_default_cnf() { # Creates the default Webinoly Configuration File (.cnf) for mysql if not exists. if [[ ! -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf ]]; then sudo touch $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf sudo chmod 644 $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf sudo chown -R root:root $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf echo "# Webinoly MySQL/MariaDB Configuration File ###################################################################### # Webinoly (This configuration file is only for internal use) # # Please, DO NOT MODIFY this file, it can cause unexpected behavior. # ###################################################################### [mysqld] log_error = /var/log/mysql/error.log" >> $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly.cnf fi } mysql_login_cnf() { # MySQL login data if [[ ! -f $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf ]]; then sudo touch $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf sudo chmod 644 $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf sudo chown -R root:root $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf echo "# Webinoly MySQL/MariaDB Login Configuration File ###################################################################### # Webinoly (This configuration file is only for internal use) # # Please, DO NOT MODIFY this file, it can cause unexpected behavior. # ###################################################################### " >> $MYSQL_CONF_PATH/${MYSQL_CONF_PREF}-webinoly-login.cnf fi } check_var() { # fastcgi_read_timeout,max_execution_time,request_terminate_timeout if [[ $1 == "php-max-time" ]]; then [[ -n $(conf_read php-max-time) && $(conf_read php-max-time) =~ ^[0-9]+$ && $(conf_read php-max-time) -gt 0 ]] && local out=$(conf_read php-max-time) || local out=60 # PHP Memory limit elif [[ $1 == "php-max-mem" ]]; then [[ -n $(conf_read php-max-mem) && $(conf_read php-max-mem) =~ ^[0-9]+$ && $(conf_read php-max-mem) -gt 0 ]] && local out=$(conf_read php-max-mem) || local out=192 # PHP simultaneous file uploads elif [[ $1 == "php-max-files" ]]; then [[ -n $(conf_read php-max-files) && $(conf_read php-max-files) =~ ^[0-9]+$ && $(conf_read php-max-files) -gt 0 ]] && local out=$(conf_read php-max-files) || local out=20 # PHP Process Manager elif [[ $1 == "php-pm" ]]; then if [[ $(conf_read php-pm) =~ ^(static|ondemand|dynamic)$ ]]; then local out=$(conf_read php-pm) else [[ $ram -gt 1 ]] && local out="static" || local out="dynamic" fi # PHP Workers elif [[ $1 == "php-max-child" ]]; then if [[ -n $(conf_read php-max-child) && $(conf_read php-max-child) =~ ^[0-9]+$ && $(conf_read php-max-child) -ge 3 ]]; then local out=$(conf_read php-max-child) else [[ $((3*$cores)) =~ ^[0-9]+ && $((3*$cores)) -ge 6 ]] && local out=$((3*$cores)) || local out=6 fi # PHP max input vars elif [[ $1 == "php-max-input-vars" ]]; then [[ -n $(conf_read php-max-input-vars) && $(conf_read php-max-input-vars) =~ ^[0-9]+$ && $(conf_read php-max-input-vars) -gt 0 ]] && local out=$(conf_read php-max-input-vars) || local out=5000 # PHP opcache validate timestamps elif [[ $1 == "php-opcache-timestamps" ]]; then local out="false" [[ $(conf_read php-opcache-timestamps) == "true" ]] && local out="1" [[ $(conf_read php-opcache-timestamps) == "false" ]] && local out="0" # PHP opcache revalidate frequency elif [[ $1 == "php-opcache-reval" ]]; then [[ -n $(conf_read php-opcache-reval) && $(conf_read php-opcache-reval) =~ ^[0-9]+$ && $(conf_read php-opcache-reval) -ge 0 ]] && local out=$(conf_read php-opcache-reval) || local out="false" # Redis Memory elif [[ $1 == "redis-max-mem" ]]; then [[ -n $(conf_read redis-max-mem) && $(conf_read redis-max-mem) =~ ^[0-9]+$ && $(conf_read redis-max-mem) -le 100 ]] && local out=$(conf_read redis-max-mem) || local out=10 # client_max_body_size,upload_max_filesize,post_max_size elif [[ $1 == "max-mb-uploads" ]]; then [[ -n $(conf_read max-mb-uploads) && $(conf_read max-mb-uploads) =~ ^[0-9]+$ && $(conf_read max-mb-uploads) -gt 0 ]] && local out=$(conf_read max-mb-uploads) || local out=100 # FastCGI Cache size elif [[ $1 == "run-folder-size" ]]; then [[ -n $(conf_read run-folder-size) && $(conf_read run-folder-size) =~ ^[0-9]+$ && $(conf_read run-folder-size) -gt 10 && $(conf_read run-folder-size) -le "70" ]] && local out=$(conf_read run-folder-size) || local out=25 # FastCGI Cache: Query Strings elif [[ $1 == "wpcache-query-strings" ]]; then if [[ $(conf_read wpcache-query-strings) =~ ^(all|never)$ ]]; then local out=$(conf_read wpcache-query-strings) else local out="false" fi # Locations: Deny Extensions elif [[ $1 == "locations-deny-extensions" ]]; then if [[ -z $(conf_read locations-deny-extensions) ]]; then local out="7z|asc|asp|aspx|ba|bak|bash|bat|bin|bz2|c|cfg|cgi|class|com|conf|cpp|crt|cs|dat|db|dbf|deb|der|dll|dmg|dmp|dump|ear|exe|git|gz|h|hg|hqx|img|ini|iso|jar|jsp|log|mdb|msi|msm|msp|old|orig|original|out|pem|php#|php_bak|php~|pkg|pl|ppk|py|rar|rdf|rpm|run|save|sh|sql|srv|svn|swo|swp|sys|tar|taz|tcl|tgz|tk|tmp|tpl|tz|vb|war|wsf|z|zip" else local out=$(conf_read locations-deny-extensions) fi # Locations: Deny Files elif [[ $1 == "locations-deny-files" ]]; then if [[ -z $(conf_read locations-deny-files) ]]; then local out="changelog|example|installation|legalnotice|license|readme|wp-config" else local out=$(conf_read locations-deny-files) fi # FastCGI Cache: Exclude URL elif [[ $1 == "wpcache-exclude-url" ]]; then if [[ -z $(conf_read wpcache-exclude-url) ]]; then local out="/wp-admin/|/xmlrpc.php|wp-.*.php|index.php|/feed/|.*sitemap.*\.xml|/feed/|/account/|/add_to_cart/|/cart/|/my-account/|/checkout/|/logout/" else local out=$(conf_read wpcache-exclude-url) fi # FastCGI Cache: Exclude Cookie elif [[ $1 == "wpcache-exclude-cookie" ]]; then if [[ -z $(conf_read wpcache-exclude-cookie) ]]; then local out="comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|[a-z0-9]+_items_in_cart|[a-z0-9]+_cart_hash" else local out=$(conf_read wpcache-exclude-cookie) fi # Nginx Error Log Level elif [[ $1 == "nginx-error-log-level" ]]; then [[ -n $(conf_read nginx-error-log-level) && $(conf_read nginx-error-log-level) =~ ^(info|notice|warn|error|crit|alert|emerg)$ ]] && local out=$(conf_read nginx-error-log-level) || local out="false" # Nginx Log Format elif [[ $1 == "nginx-log-format" ]]; then local out="we_log" # Basic is fallback! [[ $(conf_read nginx-log-format) == "extended" ]] && local out="we_log_ext" [[ $(conf_read nginx-log-format) == "custom" ]] && local out="we_log_custom" else local out="false" fi echo $out } config_fastcgi_cache() { # This function is used alone by webinoly command. # For a particular site you can use it as follows: #Example: config_fastcgi_cache -site #Example: config_fastcgi_cache -site [1h,1d,1m] local pref="fastcgi" if [[ $1 == "-site" && -f /etc/nginx/sites-available/$domain ]]; then if [[ $(is_wp $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-wpcache.conf ]]; then local timefile="/etc/nginx/apps.d/$domain$subname-wpcache.conf" elif [[ $(is_php $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-phpcache.conf ]]; then local timefile="/etc/nginx/apps.d/$domain$subname-phpcache.conf" elif [[ $(is_proxy $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-proxy.conf ]]; then if [[ -n $(grep -F "# WebinolyProxyCacheStart" /etc/nginx/apps.d/$domain$subname-proxy.conf) ]]; then local timefile="/etc/nginx/apps.d/$domain$subname-proxy.conf" local pref="proxy" else echo "${red}[ERROR] Custom Proxy Cache not found! ${dim}(Must be enabled at least once before you can add these custom values)${end}" exit 1 fi else echo "${red}[ERROR] Unexpected internal error! (Wrong sitetype for custom cache)${end}" exit 1 fi local mainfile="/etc/nginx/conf.d/webinoly.conf" local cache_valid=$2 else local timefile="/etc/nginx/conf.d/fastcgi.conf" local mainfile="/etc/nginx/conf.d/fastcgi.conf" fi if [[ $(conf_read nginx) == "true" ]]; then hitline=$( grep -F "${pref}_cache_valid 200" $timefile ) hitval=$(echo "${hitline//;}" | rev | cut -d' ' -f 1 | rev) inaline=$( grep -F "${pref}_cache_path" $mainfile ) inactive=$(echo "${inaline//;}" | rev | cut -d' ' -f 1 | rev) inaval=$(echo "${inactive}" | cut -d'=' -f 2) maxsize=$(echo "${inaline}" | rev | cut -d' ' -f 2 | rev) othline=$( grep -F "${pref}_cache_valid 301 302 303 307 308 404 410 451" $timefile ) othval=$(echo "${othline//;}" | rev | cut -d' ' -f 1 | rev) elif [[ -n $(conf_read fastcgi-conf) ]]; then hitval=$( echo $(conf_read fastcgi-conf) | cut -d',' -f 1 -s ) inaval=$( echo $(conf_read fastcgi-conf) | cut -d',' -f 2 -s ) othval=$( echo $(conf_read fastcgi-conf) | cut -d',' -f 3 -s ) else hitval="Not yet defined" inaval="Not yet defined" othval="Not yet defined" fi if [[ $cache_valid == true ]]; then echo "${gre}" echo "**********************************************************************" echo "************* Set Nginx Cache new time values **************" echo "***** Example: 30d = 30days | 3h = 3hours | 5m = 5minutes ******" echo "**********************************************************************" echo "${blu}" echo "Nginx Cache Valid for Pages (HttpCode: 200) actual value is: $hitval" read -p " Set new value: " hit hit=${hit:-$hitval} echo "" echo "Purge Cache for inactive pages actual value is: $inaval" read -p " Set new value: " ina ina=${ina:-$inaval} echo "" echo "Nginx Cache Valid for Errors and Redirections (301 302 303 307 308 404 410 451) actual value is: $othval" read -p " Set new value: " oth oth=${oth:-$othval} echo "" elif [[ $(echo "${cache_valid}" | cut -c-1) == "[" && $(echo "${cache_valid}" | rev | cut -c-1) == "]" ]]; then custombegin=$(echo "${cache_valid}" | cut -c-1) customlast=$(echo "${cache_valid}" | rev | cut -c-1) # No need for check var lenght to prevent errors, the previous condition is enough in this case. cachedata=${cache_valid:1:-1} hit=$(echo "${cachedata}" | cut -d',' -f 1 ) ina=$(echo "${cachedata}" | cut -d',' -f 2 ) oth=$(echo "${cachedata}" | cut -d',' -f 3 ) else echo "${red}[ERROR] Please enter a valid value!${end}" exit 1 fi if [[ "$hit" =~ ^[0-9]+[smhdwMy]$ && "$ina" =~ ^[0-9]+[smhdwMy]$ && "$oth" =~ ^[0-9]+[smhdwMy]$ ]]; then if [[ $(conf_read nginx) == "true" ]]; then [[ $1 == "-site" ]] && local path="/run/nginx-cache/$(echo $domain | sed 's/[^0-9A-Za-z]/_/g')$subname" || local path="/run/nginx-cache" local cachepath=$(grep -F "${pref}_cache_path $path " $mainfile) local newcachepath=$(echo $cachepath | sed "s/ inactive=.*[a-z]/ inactive=$ina/") if [[ -n $cachepath && -n $newcachepath ]]; then sudo sed -i "s#$cachepath#$newcachepath#" $mainfile else echo "${red}[ERROR] Unexpected error! (Cache configuration corrupted)${end}" exit 1 fi sudo sed -i "/${pref}_cache_valid 200/c ${pref}_cache_valid 200 ${hit};" $timefile sudo sed -i "/${pref}_cache_valid 301 302 /c ${pref}_cache_valid 301 302 303 307 308 404 410 451 ${oth};" $timefile sudo sed -i "/^${pref}_/s/^/ /" $timefile fi [[ $1 == "-site" ]] || conf_write fastcgi-conf ${hit},${ina},${oth} echo "${gre}Nginx Cache values has been successfully updated!${end}" else echo "${red}[ERROR] Invalid values!${end}" exit 1 fi } custom_cache_global() { # This function is used alone by webinoly command. # For a particular site you can use it as follows: # Example: custom_cache_global -site if [[ $1 == "-site" && -f /etc/nginx/sites-available/$domain ]]; then if [[ ( $(is_wp $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-wpcache.conf ) || ( $(is_php $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-phpcache.conf ) || ( $(is_proxy $domain $subfolder) == "true" && -f /etc/nginx/apps.d/$domain$subname-proxy.conf ) ]]; then local confile="/etc/nginx/apps.d/${domain}${subname}-site_custom_cache.conf" local mark="_$(echo $domain | sed "s/[^0-9A-Za-z]/_/g")${subname}" else echo "${red}[ERROR] Custom Cache not found! ${dim}(Must be enabled at least once before you can add these custom rules)${end}" exit 1 fi else local confile="/etc/nginx/apps.d/global_custom_cache.conf" fi if [[ -n $list ]]; then [[ -n $raw || $list == "raw" ]] && echo "" || echo "${gre}" if [[ -f $confile ]]; then [[ -n $query_string_never_cache ]] && local id="NeverCacheQueryString" [[ -n $query_string_cache ]] && local id="CacheQueryString" [[ -n $skip_cookie_cache ]] && local id="CacheSkipCookie" [[ -n $skip_cache ]] && local id="CacheSkipURL" local isem=$(sudo sed -n "/# Value: /{h;d;}; H; /# $id/{x;p;}" $confile) if [[ -n $raw || $list == "raw" ]]; then sudo sed -n "/# Value: /{h;d;}; H; /# $id/{x;p;}" $confile | sed -n '/# Value:/p' | sed 's/# Value: //g' else sudo sed -n "/# Value: /{h;d;}; H; /# $id/{x;p;}" $confile | sed -n '/# Value:/p' | sed 's/# Value: /+ /g' fi fi [[ -z $isem && -z $raw && $list != "raw" ]] && echo "${blu}[Empty] No Cache Rules were found!${end}" [[ -n $raw || $list == "raw" ]] && echo "" || echo "${end}" elif [[ -n $delete ]]; then echo "" [[ $skip_cache == "true" ]] && read -p "${blu}Cache Skip URL to delete: ${end}" skip_cache [[ $skip_cookie_cache == "true" ]] && read -p "${blu}Cache Skip Cookie to delete: ${end}" skip_cookie_cache [[ $query_string_cache == "true" ]] && read -p "${blu}Cache Query String to delete: ${end}" query_string_cache [[ $query_string_never_cache == "true" ]] && read -p "${blu}Never Cache this Query String to delete: ${end}" query_string_never_cache if [[ -z $query_string_cache && -z $query_string_never_cache && -z $skip_cache && -z $skip_cookie_cache ]]; then echo "${red}[ERROR] Please, enter a valid value!${end}" exit 1 fi if [[ -f $confile ]]; then [[ -n $query_string_never_cache ]] && local value=$query_string_never_cache [[ -n $query_string_cache ]] && local value=$query_string_cache [[ -n $skip_cookie_cache ]] && local value=$skip_cookie_cache [[ -n $skip_cache ]] && local value=$skip_cache sudo sed -Ei "/^# Value: $(escaped_string $value)( .*)?$/,/^# CacheRuleEnd/{/.*/d}" $confile [[ -f $confile && ( ! -s $confile || -z $(cat -v $confile | grep -m 1 '[^[:space:]]')) ]] && sudo rm $confile # Better because also check for files containing only empty-spaces! fi echo "${gre}Cache rule successfully removed!${end}" else echo "" [[ $skip_cache == "true" ]] && read -p "${blu}Cache Skip URL: ${end}" skip_cache [[ $skip_cookie_cache == "true" ]] && read -p "${blu}Cache Skip Cookie: ${end}" skip_cookie_cache [[ $query_string_cache == "true" ]] && read -p "${blu}Cache Query String: ${end}" query_string_cache [[ $query_string_never_cache == "true" ]] && read -p "${blu}Never Cache this Query String: ${end}" query_string_never_cache echo "" if [[ -z $query_string_cache && -z $query_string_never_cache && -z $skip_cache && -z $skip_cookie_cache ]]; then echo "${red}[ERROR] Please, enter a valid value!${end}" exit 1 elif [[ -n $regex && ( -n $query_string_cache || -n $query_string_never_cache ) ]]; then echo "${red}[ERROR] Regex not allowed for Query-String custom rules!${end}" exit 1 elif [[ $skip_cache == "/" && -z $regex ]]; then echo "${red}[ERROR] Root folder not allowed, use regex or disable site cache!${end}" exit 1 elif [[ -n $regex ]] && ! [[ $regex =~ ^(sensitive|insensitive)$ ]]; then echo "${red}[ERROR] Invalid regex value!${end}" exit 1 elif [[ -z $regex && -n $skip_cache && $(is_url_path $skip_cache) != "true" ]]; then echo "${red}[ERROR] Invalid URL!${end}" exit 1 elif [[ -z $regex && -n $skip_cookie_cache ]] && ! [[ $skip_cookie_cache =~ ^([\]A-Za-z0-9_\/\.:\!\*\'\[\(\)\;@\&\=\+\$\,\?#\~\%\-]+)?$ ]]; then echo "${red}[ERROR] Invalid Cookie String!${end}" exit 1 elif [[ -z $regex && -n $query_string_cache ]] && ! [[ $query_string_cache =~ ^([\]A-Za-z0-9_\/\.:\!\*\'\[\(\)\;@\&\=\+\$\,\?#\~\%\-]+)?$ ]]; then echo "${red}[ERROR] Invalid Query String!${end}" exit 1 elif [[ -z $regex && -n $query_string_never_cache ]] && ! [[ $query_string_never_cache =~ ^([\]A-Za-z0-9_\/\.:\!\*\'\[\(\)\;@\&\=\+\$\,\?#\~\%\-]+)?$ ]]; then echo "${red}[ERROR] Invalid Query String!${end}" exit 1 fi if [[ ! -f $confile ]]; then sudo touch $confile sudo chmod 644 $confile sudo chown -R root:root $confile fi [[ -n $query_string_never_cache ]] && local value=$query_string_never_cache [[ -n $query_string_cache ]] && local value=$query_string_cache [[ -n $skip_cookie_cache ]] && local value=$skip_cookie_cache [[ -n $skip_cache ]] && local value=$skip_cache local exist=$( grep -E "^# Value: $(escaped_string $value)( .*)?$" $confile ) if [[ -z $exist ]]; then if [[ $regex == "sensitive" ]]; then sign="~" code="$value (Regex)" elif [[ $regex == "insensitive" ]]; then sign="~*" code="$value (Regex)" else sign="=" code="$value" fi if [[ -n $query_string_never_cache ]]; then echo "# Value: $code # NeverCacheQueryString if (\$arg_${value}) { set \$skip_cache${mark} 1; } # CacheRuleEnd" >> $confile elif [[ -n $skip_cookie_cache ]]; then echo "# Value: $code # CacheSkipCookie if (\$http_cookie $sign $value) { set \$skip_cache${mark} 1; } # CacheRuleEnd" >> $confile elif [[ -n $query_string_cache ]]; then # Sed can not write when file is empty [[ ! -s $confile ]] && echo ' ' >> $confile sudo sed -i "1i # Value: $code\n# CacheQueryString\nif (\$arg_${value}) {\n set \$skip_cache${mark} 0;\n}\n# CacheRuleEnd" $confile elif [[ -n $skip_cache ]]; then [[ ! -s $confile ]] && echo ' ' >> $confile sudo sed -i "1i # Value: $code\n# CacheSkipURL\nif (\$request_uri $sign $value) {\n set \$skip_cache${mark} 1;\n}\n# CacheRuleEnd" $confile fi # Last verification if ! sudo nginx -t > /dev/null 2>&1; then if [[ $1 == "-site" && -n $subfolder ]]; then [[ -n $query_string_never_cache ]] && sudo site $domain -subfolder=$subfolder -cache=custom -query-string-never-cache=$query_string_never_cache -delete > /dev/null 2>&1 [[ -n $query_string_cache ]] && sudo site $domain -subfolder=$subfolder -cache=custom -query-string-cache=$query_string_cache -delete > /dev/null 2>&1 [[ -n $skip_cookie_cache ]] && sudo site $domain -subfolder=$subfolder -cache=custom -skip-cookie-cache=$skip_cookie_cache -delete > /dev/null 2>&1 [[ -n $skip_cache ]] && sudo site $domain -subfolder=$subfolder -cache=custom -skip-cache=$skip_cache -delete > /dev/null 2>&1 elif [[ $1 == "-site" ]]; then [[ -n $query_string_never_cache ]] && sudo site $domain -cache=custom -query-string-never-cache=$query_string_never_cache -delete > /dev/null 2>&1 [[ -n $query_string_cache ]] && sudo site $domain -cache=custom -query-string-cache=$query_string_cache -delete > /dev/null 2>&1 [[ -n $skip_cookie_cache ]] && sudo site $domain -cache=custom -skip-cookie-cache=$skip_cookie_cache -delete > /dev/null 2>&1 [[ -n $skip_cache ]] && sudo site $domain -cache=custom -skip-cache=$skip_cache -delete > /dev/null 2>&1 else [[ -n $query_string_never_cache ]] && sudo webinoly -query-string-never-cache=$query_string_never_cache -delete > /dev/null 2>&1 [[ -n $query_string_cache ]] && sudo webinoly -query-string-cache=$query_string_cache -delete > /dev/null 2>&1 [[ -n $skip_cookie_cache ]] && sudo webinoly -skip-cookie-cache=$skip_cookie_cache -delete > /dev/null 2>&1 [[ -n $skip_cache ]] && sudo webinoly -skip-cache=$skip_cache -delete > /dev/null 2>&1 fi echo "${red}[ERROR] Seems like you are using some invalid Nginx values or characters!${end}" exit 1 fi echo "${gre}Cache rule successfully added!${end}" else echo "${gre}Cache rule already exists!${end}" fi fi } smtp_backup() { if [[ $(conf_read smtp) == "true" ]]; then sudo touch /var/www/webinoly_backup_smtp echo "$(sudo sed -n 1p /etc/mailname) $(sudo sed -n 1p /etc/postfix/sasl_passwd)" > /var/www/webinoly_backup_smtp fi } smtp_backup_recovery() { if [[ -s /var/www/webinoly_backup_smtp ]]; then local smtpdata=$(sudo sed -n 1p /var/www/webinoly_backup_smtp) local main=$(echo $smtpdata | cut -d' ' -f 1 -s) local host=$(echo $smtpdata | cut -d' ' -f 2 -s | cut -d':' -f 1 -s) local user=$(echo $smtpdata | cut -d' ' -f 3 -s | cut -d':' -f 1 -s) local pass=$(echo $smtpdata | cut -d' ' -f 3 -s | cut -d':' -f 2 -s) sudo rm -rf /var/www/webinoly_backup_smtp if [[ -n $host && -n $user && -n $pass && -n $main ]]; then sudo webinoly -smtp=[$host,$user,$pass,$main] else echo "${red}[ERROR] SMTP Configuration recovery failed! ${end}" fi fi } help_message() { echo "" echo "${blu}${bol}Thanks for using Webinoly!${end}" echo "${blu}We have put a lot of time and effort into creating the most awesome, detailed and extensive documentation just for you." echo "${dim}Link: https://webinoly.com/documentation/ ${end}" echo "" } ads_donate() { # Donations message displayed once a day! # Note: Don't run when stdout is redirected to /dev/null: https://unix.stackexchange.com/questions/484228/how-to-check-if-stdin-is-dev-null-from-the-shell if [[ -z $(conf_read cron-ads) && -n $EPOCHSECONDS ]]; then conf_write cron-ads $EPOCHSECONDS elif ! [[ $(conf_read cron-ads) =~ ^[0-9]+$ ]] || [[ $(conf_read cron-ads) -gt $EPOCHSECONDS ]]; then # Autofix! (Just in case!) conf_write cron-ads $EPOCHSECONDS elif [[ $TERM != "dumb" && -n $EPOCHSECONDS && -n $(conf_read cron-ads) && $(($EPOCHSECONDS-$(conf_read cron-ads))) -gt 86400 ]] && ! [[ /dev/stdout -ef /dev/null ]]; then echo "${blu}" echo "****************************************************************************" echo "******************** ${bol}Are you enjoying Webinoly?${end}${blu} ********************" echo "*** ${dim}With just \$1 you can make a difference to keep this project alive!${end}${blu} ***" echo "*********** ${bol}Donate Now!${end}${blu}${dim} https://github.com/sponsors/QROkes${end}${blu} ***********" echo "****************************************************************************" echo "${end}" conf_write cron-ads $EPOCHSECONDS fi }