HEX
Server: Apache
System: Linux box2146.bluehost.com 4.19.286-203.ELK.el7.x86_64 #1 SMP Wed Jun 14 04:33:55 CDT 2023 x86_64
User: thetwis6 (2126)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: //tmp/update-and-clean-bh.1763074004.sh
#!/usr/bin/env bash

####################################################### MANUAL #########################################################
#                                                                                                                      #
# This script was created to execute on the root af any WordPress installation, remove stray copies of BH and MOJO     #
# plugin, and install, update and activate official BH plugin.                                                         #
#                                                                                                                      #
# Recommended usage:                                                                                                   #
# > ./update-and-clean-bh.sh -r -vv                                                                                    #
# In this configuration the script will remove any copy of the BH plugin under unexpected paths, and install (or       #
# update) the official BH plugin.                                                                                      #
# Once installed, the BH plugin will be activated only if there was a previous active copy of the same plugin          #
# Note that, if there isn't any active copy of BH plugin, it is impossible for the script to update the plugin         #
#                                                                                                                      #
# Various parameters can be used to tweak behaviour of the script:                                                     #
# -d          -> Dry run; the script will execute as usual, but won't apply any change to current installation.        #
# -v|-vv|-vvv -> Verbosity level; the higher, the more info you will get out of the script.                            #
# -f          -> Force install; if provided, BH plugin will be installed, updated and activated, no matter what        #
#                current status is.                                                                                    #
# -r          -> Remove others; if provided, out-of-spec installations of BH plugin, and all installations of Mojo     #
#                plugin, will be permanently removed from the site.                                                    #
# -n          -> When set, even if a valid copy of BH is installed, it will delete it and install BH from scratch.     #
# -h          -> Prints the manual.                                                                                    #
########################################################################################################################

######################################################## GLOBALS #######################################################

## Whether last execution of search function found some matches
matchesFound=0;

## Whether any of the matches is active
hasActiveMatch=0;

## If previous flag is 1, contains rowIndex for first active match; default is -1 to distinguish it from valid indexes
activeMatch=-1;

## List of all installation plugins, retrieved only one time
allPlugins=();

## Matching plugins, populated with results of searchPlugin function
matchingPlugins=();

## Whether has BH plugin
hasBHPlugin=0;

## Index of the BH plugin, if found; default is -1 to distinguish it from valid indexes
BHPlugin=-1;

## Whether has BH-Alike plugins
hasOtherBHPlugins=0;

## Index of other BH plugin installations, if any.
otherBHPlugins=();

## Whether has at least one active copy of BH
hasActiveBH=0;

## If previous flag is 1, contains rowIndex for first active BH plugin; default is -1 to distinguish it from valid indexes
activeBH=-1;

## Whether BH plugin is installed and needs updates.
isBHUpToDate=0;

## Whether has Mojo-Alike plugins
hasMojoPlugins=0;

## Index of other Mojo plugin installations, if any.
mojoPlugins=();

## Whether has at least one active copy of Mojo; default is -1 to distinguish it from valid indexes
hasActiveMojo=0;

## If previous flag is 1, contains rowIndex for first active Mojo plugin; default is -1 to distinguish it from valid indexes
activeMojo=-1;

###################################################### UTILITIES #######################################################

## Search a specific plugin by field (title|file|name).
function searchPluginByField() {
	local FIELD=$1;
	local SEARCH=$2;

	# check if field is supported
	case "$FIELD" in
		"name")
			log "## Searching for matches of \"${SEARCH}\" in plugin names"
			columnIndex=0
		;;
		"file")
			log "## Searching for matches of \"${SEARCH}\" in plugin files"
			columnIndex=1
		;;
		"title")
			log "## Searching for matches of \"${SEARCH}\" in plugin titles"
			columnIndex=2
		;;
		*)
			log 'ERROR: Unsupported search field';
			exit 1;
		;;
	esac

	# retrieve all plugins
	getAllPlugins

	for rowIndex in "${!allPlugins[@]}"; do
		row=${allPlugins[rowIndex]}
		IFS='|' read -ra columns <<< "$row"
		log "### Processing: ${columns[0]} -> $FIELD = ${columns[$columnIndex]}"

		if [[ ${columns[$columnIndex]} =~ $SEARCH ]]; then
			matchingPlugins[$rowIndex]="${rowIndex}"
			matchesFound=1
		fi
	done
}

# removes official BH plugin from result set, after searching for BH-Alike plugins
function removeBHPluginFromMatches() {
	if [[ $hasBHPlugin -eq 0 ]]; then return; fi

	local tempMatches=()
	for match in "${matchingPlugins[@]}"; do
		if [[ $match != $BHPlugin ]]; then
			tempMatches+=("$match")
		fi
	done

	if [[ ${#tempMatches[@]} -eq 0 ]]; then
		matchesFound=0
	fi

	matchingPlugins=("${tempMatches[@]}")
}

## Checks if it has at least an active copy of Bluehost
function hasActiveMatch() {
	if [[ ${#matchingPlugins[@]} -eq 0 ]]; then return; fi

	for rowIndex in "${!matchingPlugins[@]}"; do
		row=${allPlugins[rowIndex]}
		IFS='|' read -ra columns <<< "$row"

		if [[ ${columns[3]} = "active" ]]; then
			hasActiveMatch=1
			activeMatch=$rowIndex
			break
		fi
	done
}

## Search for exact match of Bluehost plugin
function searchBluehostPlugin() {
	log "# SEARCHING FOR BH PLUGIN"
	log "" 2

	# search for matches
	resetMatchingPlugins
	searchPluginByField "file" "^bluehost-wordpress-plugin/bluehost-wordpress-plugin.php$"

	# print results
	printSearchResults

	hasBHPlugin=$matchesFound
    BHPlugin="${!matchingPlugins[@]}"
}

## Search for occurrences of plugins that resemble Bluehost plugin
function searchBluehostAlikePlugins() {
	log "# SEARCHING FOR BH-ALIKE PLUGIN"
	log "" 1

	# search for matches
	resetMatchingPlugins
	searchPluginByField "title" "The Bluehost Plugin"
	searchPluginByField "file" ".*/bluehost-wordpress-plugin.php"
	searchPluginByField "name" ".*bluehost-wordpress-plugin.*"

	# set flag if we have at least one active installation
	hasActiveMatch

	# remove official BH from the matches
	removeBHPluginFromMatches

	# print results
	printSearchResults

	hasActiveBH=$hasActiveMatch
	activeBH=$activeMatch
	hasOtherBHPlugins=$matchesFound
	otherBHPlugins=("${matchingPlugins[@]}")
}

## Search for MOJO plugin
function searchMojoAlikePlugins() {
	log "# SEARCHING FOR MOJO-ALIKE PLUGIN"
	log "" 2

	# search for matches
	resetMatchingPlugins
	searchPluginByField "title" "MOJO Marketplace"
	searchPluginByField "file" ".*/mojo-marketplace.php"
	searchPluginByField "name" ".*mojo-marketplace-wp-plugin.*"

	# set flag if we have at least one active installation
	hasActiveMatch

	# print results
	printSearchResults

	hasActiveMojo=$hasActiveMatch
	activeMojo=$activeMatch
	hasMojoPlugins=$matchesFound
	mojoPlugins=("${matchingPlugins[@]}")
}

## Reset matchingPlugins array and matchesFound flag
function resetMatchingPlugins() {
	matchingPlugins=()
	matchesFound=0
	hasActiveMatch=0
	activeMatch=-1
}

## Retrieve all existing plugins.
function getAllPlugins() {
	if [[ ${#allPlugins[@]} -gt 0 ]]; then return; fi

	while IFS="," read -r name file title status update
	do
		name="${name//\"/}"
		name="${name//\'/}"
		file="${file//\"/}"
		file="${file//\'/}"
		title="${title//\"/}"
		title="${title//\'/}"
		allPlugins+=("$name|$file|$title|$status|$update")
	done < <(tail -n +2 <(wp plugin list --fields=name,file,title,status,update --format=csv --skip-plugins --skip-themes --skip-packages))
}

## Install BH plugin
function installBHPlugin() {
	log "" 1
	log "# Installing BH plugin"
	if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi

	attempt=1
	maxAttempts=5
	waitBetweenRetries=2

	while [[ $attempt -le $maxAttempts ]];
	do
		log ""
		log "## Attempt $attempt out of $maxAttempts..."
		wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages
		verifyBHPluginInstallation

		if [[ $hasBHPlugin -eq 1 ]]; then
			log ""
			log "# Successfully installed BH plugin"
			break;
		fi

		(( attempt++ ))

		if [[ $attempt -le $maxAttempts ]]; then
			log "## Attempt failed; waiting for retry in $waitBetweenRetries seconds..."
			sleep "$waitBetweenRetries"
		else
			log ""
			log "# Failed to install BH plugin"
		fi
	done

	log "" 1
}

## Force install BH plugin
function forceInstallBHPlugin() {
	log "" 1
	log "# Force installing BH plugin (to update inactive copy)"
	if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi

	wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages --force

	log "" 1
}

## Activate BH plugin
function activateBHPlugin() {
	log "" 1
	log "# Activating BH plugin"
	if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi

	wp plugin activate bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages
	log "" 1
}

## Update BH plugin
function updateBHPlugin() {
	if [[ $hasBHPlugin -eq 0 ]]; then return; fi

	log "" 1
	log "# Updating BH plugin"
	if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi

	attempt=1
	maxAttempts=5
	waitBetweenRetries=2

	while [[ $attempt -le $maxAttempts ]];
	do
		log ""
		log "## Attempt $attempt out of $maxAttempts..."
		wp plugin update bluehost-wordpress-plugin --skip-themes --skip-packages --skip-plugins=$( wp plugin list --fields=name --skip-themes --skip-packages --skip-plugins --format=csv | grep -v bluehost-wordpress-plugin | tail -n+2 | tr -d ' ' | tr -d '"' | tr '\n' ',' )
		verifyBHPluginUpdateStatus

		if [[ $isBHUpToDate -eq 1 ]]; then
			log ""
			log "# Successfully updated BH plugin"
			break;
		fi

		(( attempt++ ))

		if [[ $attempt -le $maxAttempts ]]; then
			log "## Attempt failed; waiting for retry in $waitBetweenRetries seconds..."
			sleep "$waitBetweenRetries"
		else
			log "# Failed to update BH plugin; do one final attempt to force install"
			wp plugin install https://cdn.hiive.space/plugins/bluehost-wordpress-plugin.zip --skip-plugins --skip-themes --skip-packages --force

			verifyBHPluginUpdateStatus

			if [[ $isBHUpToDate -eq 1 ]]; then
				log ""
				log "# Successfully updated BH plugin"
			else
				log ""
				log "# Failed to update BH plugin"
			fi
		fi
	done

	log "" 1
}

## Verify whether BH installation was successful
function verifyBHPluginInstallation() {
	wp plugin is-installed bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages

	if [[ $? -eq 0 ]]; then
		hasBHPlugin=1
		log "## BH plugin is installed"
	else
		hasBHPlugin=0
		log "## BH plugin isn't installed"
	fi
}

## Verify whether BH activation was successful
function verifyBHPluginActivation() {
	wp plugin is-active bluehost-wordpress-plugin --skip-plugins --skip-themes --skip-packages

	if [[ $? -eq 0 ]]; then
		hasActiveBH=1
		log "## BH plugin is active"
	else
		hasActiveBH=0
		log "## BH plugin isn't active"
	fi
}

## Verify whether BH plugin is up to date
function verifyBHPluginUpdateStatus() {
	status=$(wp plugin list --fields=name,update --format=csv --skip-themes --skip-packages --skip-plugins=$( wp plugin list --fields=name --skip-themes --skip-packages --skip-plugins --format=csv | grep -v bluehost-wordpress-plugin | tail -n+2 | tr -d ' ' | tr -d '"' | tr '\n' ',' ) | grep bluehost-wordpress-plugin, | cut -d',' -f2)

	if [[ $status == 'none' ]]; then
		isBHUpToDate=1;
		log "## BH plugin is up to date"
	elif [[ $status == 'available' ]]; then
		isBHUpToDate=0;
		log "## BH plugin needs to be updated"
	else
		log "## Couldn't verify BH plugin update status"
	fi;
}

## Deactivate stray instances of BH plugin
function deactivateOtherBHPlugins() {
	if [[ $hasOtherBHPlugins -eq 0 ]]; then return; fi

	log "" 1
	log "# Deactivating other copies of BH plugin"

	deactivatePlugins "${otherBHPlugins[@]}"
}

## Removes stray instances of BH plugin
function removeOtherBHPlugins() {
	if [[ $hasOtherBHPlugins -eq 0 || $removeOther -eq 0 ]]; then return; fi

	log "" 1
	log "# Removing other copies of BH plugin"

	removePlugins "${otherBHPlugins[@]}"
}

## Removes instances of Mojo plugin
function removeMojoPlugins() {
	if [[ $hasMojoPlugins -eq 0 || $removeOther -eq 0 ]]; then return; fi

	log "" 1
	log "# Removing copies of Mojo plugin"

	removePlugins "${mojoPlugins[@]}"
}

## Activate the specified plugin
function activatePlugin() {
	pluginToActivate=$1

	if [[ $pluginToActivate -eq -1 ]]; then return; fi

	IFS='|' read -ra columns <<< "${allPlugins[pluginToActivate]}"

	log "" 1
	log "## Activating ${columns[0]}..."
	if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; return; fi

	wp plugin activate "${columns[0]}" --skip-plugins --skip-themes --skip-packages
}

## Deactivate plugins
function deactivatePlugins() {
	pluginsToDeactivate=("$@")

	if [[ ${#pluginsToDeactivate[@]} -eq 0 ]]; then return; fi

	for rowIndex in "${pluginsToDeactivate[@]}"; do
		IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"

		log "## Deactivating ${columns[0]}..."
		if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; continue; fi
		wp plugin deactivate "${columns[0]}" --skip-plugins --skip-themes --skip-packages
	done

	log "" 1
}

## Remove plugins from this installation
function removePlugins() {
	pluginsToRemove=("$@")

	if [[ ${#pluginsToRemove[@]} -eq 0 ]]; then return; fi

	for rowIndex in "${pluginsToRemove[@]}"; do
		IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"

		log "## Removing ${columns[0]}..."
		if [[ $dryRun -eq 1 ]]; then log "## DRY RUN - Skipping"; continue; fi
		wp plugin delete "${columns[0]}" --skip-plugins --skip-themes --skip-packages
	done

	log "" 1
}

## Log what's going on
function log() {
	local message=$1
	local logLevel=$2

	if [[ $logLevel = "" ]]; then
		while [[ "${message:0:1}" = "#" ]]; do
			((logLevel++))
			message="${message:1}"
		done
	fi

	if [[ $logLevel -le $verbosity ]]; then
		if [[ $message != "" ]]; then
			printf '%0.s#' $(seq 1 $logLevel)
		fi

		printf "$message"
		echo
	fi
}

## Pretty-print search results
function printSearchResults() {
	local columnSizes=()
	local rowSize=16

	if [[ $matchesFound -eq 1 && $verbosity -ge 2 ]]; then
		log "## Matches found:"

		for rowIndex in "${matchingPlugins[@]}"; do
			IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"

			for columnIndex in "${!columns[@]}"; do
				if [[ columnSizes[columnIndex] -lt ${#columns[columnIndex]} ]]; then
					columnSizes[columnIndex]="${#columns[columnIndex]}";
				fi
			done
		done

		for columnSize in ${columnSizes[@]}; do
		  let rowSize+=$columnSize
		done

		printf '%.0s-' $(seq 1 $rowSize)
		echo

		for rowIndex in "${matchingPlugins[@]}"; do
			IFS='|' read -ra columns <<< "${allPlugins[rowIndex]}"

			printf "| "
			for columnIndex in "${!columns[@]}"; do
				padding="${columnSizes[columnIndex]}"
                printf "%-${padding}s | " "${columns[columnIndex]}"
            done
            echo
            printf '%.0s-' $(seq 1 $rowSize)
            echo
		done
	else
		log "## No match found :("
	fi

	log "" 2
}

################################################### PRE-FLIGHT TESTS ###################################################

## 1. Verify we're in a WordPress installation directory.
if ! [[ -f ./wp-config.php ]]; then
	log 'ERROR: not in a WordPress installation';
	exit 1;
fi

## 2. Verify WP CLI is available
if ! command -v wp &> /dev/null; then
	log 'ERROR: WP CLI not available';
	exit 1;
fi

dryRun=0
verbosity=0
forceInstall=0
removeOther=0
newInstallation=0

while getopts "vfrdhn" opt; do
	case "$opt" in
		v)
			((verbosity++))
			;;
		d)
			dryRun=1
			;;
		f)
			forceInstall=1
			;;
		r)
			removeOther=1
			;;
		n)
			newInstallation=1
			;;
		h)
			echo "NAME"
			echo
			echo "Bluehost updating script"
			echo
			echo "DESCRIPTION"
			echo
			echo "Install, activate and updates official BH plugin"
			echo "Optionally removes out-of-spec installation of BH and Mojo"
			echo
			echo "SYNOPSIS"
			echo
			echo "update-bluehost [-v] [-f] [-r] [-d] [-h]"
			echo
			echo "PARAMETERS"
			echo
			echo "-d          -> Dry run; the script will execute as usual, but won't apply any change to current installation."
			echo "-v|-vv|-vvv -> Verbosity level; the higher, the more info you will get out of the script."
			echo "-f          -> Force install; if provided, BH plugin will be installed, updated and activated, no matter what current status is."
			echo "-r          -> Remove others; if provided, out-of-spec installations of BH plugin, and all installations of Mojo plugin, will be permanently removed from the site."
			echo "-n          -> When set, even if a valid copy of BH is installed, it will delete it and install BH from scratch."
			echo "-h          -> Prints this manual."
			exit 0
			;;
		*)
			log "USAGE: $0 [-v] [-f] [-r] [-d] [-n] [-h]"
			exit 1
			;;
	esac
done

if [[ $verbosity -eq 0 ]]; then verbosity=1; fi

##################################################### MAIN SCRIPT ######################################################

# Search for occurrences of Mojo plugin, including official one
searchMojoAlikePlugins

# Search for BlueHost plugin.
searchBluehostPlugin
verifyBHPluginUpdateStatus

# Search for other occurrences of BlueHost plugin
searchBluehostAlikePlugins

# Register some flags that will be used to evaluate the final result
(( hadBHPlugin = hasBHPlugin || hasOtherBHPlugins ))
wasBHActive=$hasActiveBH
wasBHUpToDate=$isBHUpToDate

# Make sure to remove stray copies of BH, and run on latest
if [[ $hasBHPlugin -eq 0 && $hasOtherBHPlugins -eq 0 ]]; then
	log "# No copy of the BH plugin found on this installation"

	if [[ $hasMojoPlugins -eq 1 || $forceInstall -eq 1 ]]; then
		log "## Proceeding to install a fresh copy"
		installBHPlugin

		if [[ $hasBHPlugin -eq 1 && ( $hasActiveMojo -eq 1 || $forceInstall -eq 1 ) ]]; then
			activateBHPlugin
		fi
	else
		exit 0
	fi
else
	log "# Found at least one copy of the BH plugin"

	deactivateOtherBHPlugins

	if [[ $hasBHPlugin -eq 1 && $newInstallation -eq 1 ]]; then
		log "# Removing BH plugin to install it from scratch"
		removePlugins "$BHPlugin"
	fi

	if [[ $hasBHPlugin -eq 0 || $newInstallation -eq 1 ]]; then
		installBHPlugin
	fi

	if [[ $hasBHPlugin -eq 1 ]]; then
		# if we're positive about BH installation, proceed with activation and removal of stray copies
		if [[ $hasActiveBH -eq 1 || $hasActiveMojo -eq 1 || $forceInstall -eq 1 ]]; then
			# activate and update BH
			activateBHPlugin
			updateBHPlugin

			verifyBHPluginActivation

			# if we're positive about BH activation, proceed with removal of stray copies
			if [[ $hasActiveBH -eq 1 ]]; then
				removeOtherBHPlugins
			else
				if [[ $activeBH -ge 0 ]]; then activatePlugin $activeBH; fi
			fi
		else
			# if we had an inactive copy of the plugin, force install it, to make sure it is updated.
			if [[ $BHPlugin -ge 0 ]]; then forceInstallBHPlugin; fi

			# then, just remove stray copies
			removeOtherBHPlugins
		fi
	else
		# if we couldn't install BH plugin, reactivate the one previously active
		if [[ $hasActiveBH -eq 1 && $activeBH -ge 0 ]]; then
			activatePlugin $activeBH
		fi
	fi
fi

# Migrate from MOJO to BH
log ""
if [[ $hasMojoPlugins -eq 1 ]]; then
	log "# Found at least one copy of MOJO plugin"

	if [[ $hasBHPlugin -eq 1 ]]; then
		if [[ $hasActiveMojo -eq 1 && $hasActiveBH -eq 0 ]]; then
			# activate and update BH
			activateBHPlugin
			updateBHPlugin

			verifyBHPluginActivation

			# if we're positive about BH activation, proceed with removal of mojo
			if [[ $hasActiveBH -eq 1 ]]; then
				removeMojoPlugins
			else
				if [[ $activeMojo -ge 0 ]]; then activatePlugin $activeMojo; fi
			fi
		else
			# if we don't have to activate BH, just remove Mojo
			removeMojoPlugins
		fi
	else
		log "# Couldn't replace MOJO for a copy of BH plugin"
	fi
else
	log "# No copy of MOJO plugin found on this installation"
fi

# Evaluate the final result
log ""
verifyBHPluginInstallation
verifyBHPluginActivation
verifyBHPluginUpdateStatus

# In order to consider process a success we need to either:
# - skip installation because we have no BH initially, and we're not forcing the installation
# - we have BH installed but deactivated, because this was the initial state and we're not forcing the installation
# - we have BH installed, activated and updated, because we originally had BH installed and active.
(( status =  ( ! hadBHPlugin && ! hasBHPlugin && ! forceInstall ) || ( hasBHPlugin && ( ( ! wasBHActive && ! forceInstall ) || ( hasActiveBH && isBHUpToDate ) ) ) ))

# In case of failure, we need to flag cases that are more critical than others
# - if plugin was active before and it isn't now
# - or if was installed before and script removed it
# we lost connection to the site, and need to manually review
# In this case we can accept any copy of BH plugin, so we double check for BH-Alike plugins.
searchBluehostAlikePlugins > /dev/null;
(( hasBHPlugin = hasBHPlugin || hasOtherBHPlugins ))
(( critical = ( wasBHActive && ! hasActiveBH ) || ( hadBHPlugin && ! hasBHPlugin ) ))

# print result line for easier debugging
log ""
if [[ $critical -eq 1 ]];
	then log "# OPERATIONS LOG: $(pwd) | $(whoami) | CRITICAL"
elif [[ $status -eq 1 ]];
	then log "# OPERATIONS LOG: $(pwd) | $(whoami) | SUCCESS"
	else log "# OPERATIONS LOG: $(pwd) | $(whoami) | FAILURE"
fi
log "## Initial status: BH Installed -> $hadBHPlugin; BH Active -> $wasBHActive; BH Up to date -> $wasBHUpToDate"
log "## Final status: BH Installed -> $hasBHPlugin; BH Active -> $hasActiveBH; BH Up to date -> $isBHUpToDate"
log ""