#!/bin/bash

# © 2007 ROBO Design
# Mihai Şucan
# http://www.robodesign.ro
#
# Last update: 2007-10-06


# Do not use any trailing slash
DIR_OP_DEV="${HOME}/opera-dev/linux"
DIR_OP_PACK="${HOME}/opera-dev/downloads/linux"
DIR_OP_STABLE="${HOME}/bin/opera"
MY_PWD="$PWD"
MY_SELF="${0##*/}"
MY_BUILD=20071006

# upgrade settings
UPGRADE_BT=0 # bit torrent metadata
UPGRADE_CACHE=0 # copy cache4, cacheOp, opcache, and images (favicons)
UPGRADE_COOKIES=1
UPGRADE_MAIL=0 # upgrade the email folder, which includes feeds
UPGRADE_PLUGINS=0 # copy the plugins folder, pluginpath.ini, and javapath.txt
UPGRADE_WAND=1 # all your stored passwords

# DISABLED for safety: this always fixex file paths in all INI files, such that your stable Opera installation will not break (hope so)
# UPGRADE_FIX_INI=1

# For more options see the Upgrade_Opera() function. add/remove any files/folders you want copied.


# Check folders
if [[ ! -d "$DIR_OP_DEV" ]]
then
	echo "Error: Cannot find DIR_OP_DEV: $DIR_OP_DEV";
	exit 1
fi

if [[ ! -d "$DIR_OP_PACK" ]]
then
	echo "Error: Cannot find DIR_OP_PACK: $DIR_OP_PACK";
	exit 1
fi

if [[ ! -d "$DIR_OP_STABLE" ]]
then
	echo "Error: Cannot find DIR_OP_STABLE: $DIR_OP_STABLE";
	exit 1
fi

if [[ "$DIR_OP_STABLE" = "$DIR_OP_DEV" ]]
then
	echo "Error: DIR_OP_STABLE cannot be the same as DIR_OP_DEV!"
	exit 1
fi

if [[ "$DIR_OP_STABLE" = "$DIR_OP_PACK" ]]
then
	echo "Error: DIR_OP_STABLE cannot be the same as DIR_OP_PACK!"
	exit 1
fi

if [[ "$DIR_OP_DEV" = "$DIR_OP_PACK" ]]
then
	echo "Error: DIR_OP_DEV cannot be the same as DIR_OP_PACK!"
	exit 1
fi


# This does minimal checks to determine if a folder contains an Opera installation
Check_Opera_Dir()
{
	local DIR_OP="$1"

	if [[ -z "$DIR_OP" || ! -d "$DIR_OP" ]]
	then
		echo "Error: The folder was not found: $DIR_OP"
		exit 1
	fi

	if [[ -d "${DIR_OP}/profile" && -d "${DIR_OP}/usersettings" ]]
	then
		echo "Error: The Opera folder $DIR_OP contains both folders: 'usersettings' and 'profile'. Something is weird."
		exit 1
	fi

	if [[ ! -f "${DIR_OP}/opera" || ! -f "${DIR_OP}/LICENSE" || ! -f "${DIR_OP}/install.sh" ]]
	then
		echo "Error: Opera could not be detected inside the folder: $DIR_OP"
		exit 1
	fi
}

Check_Opera_Dir "$DIR_OP_STABLE"

# Determine the folder of the latest Opera build
Find_Opera_Latest()
{
	local LOCAL_PWD="$PWD"
	cd "$DIR_OP_DEV"

	local OP_DIRS=(opera*/)
	local n=${#OP_DIRS[@]}

	DIR_OP_LATEST=${OP_DIRS[$n-1]}
	DIR_OP_LATEST="${DIR_OP_DEV}/${DIR_OP_LATEST}"

	if [[ ! -d "$DIR_OP_LATEST" ]]
	then
		echo "Error: The latest Opera build folder could not be found in DIR_OP_DEV: $DIR_OP_DEV"
		exit 1
	fi

	cd "$DIR_OP_LATEST"
	DIR_OP_LATEST="$PWD"
	cd "$LOCAL_PWD"

	if [[ "$DIR_OP_LATEST" = "$DIR_OP_STABLE" ]]
	then
		echo "Error: The latest Opera build folder is the same as DIR_OP_STABLE: $DIR_OP_DEV"
		echo "The script cannot continue. This is probably an error in the configuration."
		exit 1
	fi

	Check_Opera_Dir "$DIR_OP_LATEST"
}

# Remove any profile folders in the given Opera folder
Clean_Opera()
{
	local LOCAL_PWD="$PWD"

	Check_Opera_Dir "$1"

	if [[ "$DIR_OP_STABLE" = "$1" ]]
	then
		echo "Error: You are trying to clean the profile of the stable Opera installation. This is not allowed!"
		exit 1
	fi

	cd "$1"

	for cdir in 'usersettings' 'profile'
	do
		if [[ ! -d "$cdir" ]]
		then
			continue
		fi

		# TODO
		# if [[ -f "${cdir}/lock" ]]
		# then
		# 	echo "Error: This Opera instance is locked: ${1}/${cdir}/lock"
		# 	echo "Most likely this Opera instance is already running. I cannot remove the profile!"
		# 	exit 1
		# fi

		echo;
		echo "Are you sure you want to remove the folder '${cdir}' from '${1}' ? Type [y]es or anything else for no."
		read -r
		echo;

		if [[ "$REPLY" != 'y' && "$REPLY" != 'yes' ]]
		then
			echo "Operation aborted!"
			exit 1
		fi

		echo "Deleting $cdir"
		rm -r "$cdir"
	done

	cd "$LOCAL_PWD"
}

# This extracts an Opera build and optionally runs it
Unpack_Opera()
{
	local OP_ARCH="$2"

	if [[ -z "$OP_ARCH" ]]
	then
		echo "Error: No Opera archive specified!"
		exit 1
	fi

	if [[ ! -f "$OP_ARCH" ]]
	then
		echo "Error: The archive was not found: $OP_ARCH"
		exit 1
	fi

	echo "Extracting $OP_ARCH"

	if [[ "$OP_ARCH" =~ tar\.bz2$ ]]
	then
		tar -C "$DIR_OP_DEV" -xjf "$OP_ARCH"
	elif [[ "$OP_ARCH" =~ tar\.gz$ ]]
	then
		tar -C "$DIR_OP_DEV" -xzf "$OP_ARCH"
	else
		echo "Error: File type unrecognized: $OP_ARCH"
		exit 1
	fi

	mv "$OP_ARCH" "${DIR_OP_PACK}/"

	echo "Archive moved to $DIR_OP_PACK"

	local DIR_OP="${OP_ARCH##*/}"
	DIR_OP="${DIR_OP_DEV}/${DIR_OP%.tar.*}"

	if [[ ! -d "$DIR_OP" ]]
	then
		echo "Warning: The folder of the Opera package could not be determined."
	else
		echo "The file was unpacked to: $DIR_OP"
		Check_Opera_Dir "$DIR_OP"
	fi

	# optionally run Opera from the unpacked folder
	if [[ "$1" = "unpack-run" ]]
	then
		echo "Starting Opera"
		cd "$DIR_OP"
		./opera
	fi
}

# This function "upgrades" an experimental build.
# What it does: the script takes all the profile files from your stable Opera installation and copies them to the experimental build. This is very useful when you want to test experimental builds with "heavy" profiles. The script will modify the INI files changing all the occurances of the stable Opera folder path, replacing them with the path of your experimental build.
# WARNING: This script relies on predefined folder paths. Make sure your stable Opera installation profile is in the default location (in 'usersettings' or 'profile'). If your INI files point to external folders for skins, userjs, plugins, mail, menus, toolbars, or styles, then those files will not be copied. The script does not check your INI files for external paths. This will make your experimental build share files with your stable build - most likely will cause damage.
Upgrade_Opera()
{
	local DIR_OP="$1"

	# Find the absolute path to the given folder

	if [[ -z "$DIR_OP" ]]
	then
		DIR_OP="$MY_PWD"
	else
		if [[ ! -d "$DIR_OP" ]]
		then
			echo "Error: Folder not found: $DIR_OP"
			exit 1
		fi

		local LOCAL_PWD="$PWD"
		cd "$DIR_OP"
		DIR_OP="$PWD"
		cd "$LOCAL_PWD"
	fi

	if [[ "$DIR_OP" = "$DIR_OP_STABLE" ]]
	then
		echo "Error: You are trying to upgrade the stable build! You cannot do this."
		exit 1
	fi

	Check_Opera_Dir "$DIR_OP"

	if [[ -d "${DIR_OP_STABLE}/profile" ]]
	then
		local DIR_PROFILE="${DIR_OP_STABLE}/profile"
	elif [[ -d "${DIR_OP_STABLE}/usersettings" ]]
	then
		local DIR_PROFILE="${DIR_OP_STABLE}/usersettings"
	else
		echo "Error: No profile folder found in DIR_OP_STABLE: $DIR_OP_STABLE"
		exit 1
	fi

	# Determine the Opera version of the experimental build, such that the new profile folder can be created ('usersettings' or 'profile')
	# hackish ... but it works

	local OP_VER="${DIR_OP##*/}"
	if [[ -z "$OP_VER" ]]
	then
		OP_VER="$DIR_OP"
	fi

	IFS='-'
	OP_VER=($OP_VER)
	unset IFS

	if [[ "${OP_VER[0]}" != 'opera' ]]
	then
		echo "Error: The specified target Opera folder name is not of the form opera-N.N-whatever. The script cannot continue."
		exit 1
	fi

	Clean_Opera "$DIR_OP" # remove any existing profile

	local -i OP_VER_MAJ="${OP_VER[1]%%.*}"
	local -i OP_VER_MIN="${OP_VER[1]#*.}"
	OP_VER_MIN="${OP_VER_MIN:0:1}"

	if [[ ( "$OP_VER_MAJ" -gt 9 ) || ("$OP_VER_MAJ" -eq 9 && "$OP_VER_MIN" -ge 5 ) ]]
	then
		OP_VER='kestrel'
	else
		OP_VER='merlin'
	fi

	if [[ "$OP_VER" = 'kestrel' ]]
	then
		local DIR_NEW='profile'
	else
		local DIR_NEW='usersettings'
	fi

	DIR_NEW="${DIR_OP}/${DIR_NEW}"

	# Create the new profile folder and copy the files

	mkdir "$DIR_NEW"

	TO_COPY='keyboard:locale:menu:mouse:sessions:skin:styles:thumbnails:toolbar:widgets:userjs:browser.js:contacts.adr:download.dat:global.dat:notes.adr:opcacrt6.dat:opcert6.dat:opera.dir:opera6.adr:opera6.ini:oprand.dat:opssl6.dat:opthumb.dat:override.ini:override_downloaded.ini:search.ini:speeddial.ini:spellcheck.ini:urlfilter.ini:vlink4.dat:widgets.adr'

	if [[ "$UPGRADE_CACHE" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:cache4:cacheOp:opcache:images"
	fi

	if [[ "$UPGRADE_MAIL" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:mail"
	fi

	if [[ "$UPGRADE_PLUGINS" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:plugins:pluginpath.ini:javapath.txt"
	fi

	if [[ "$UPGRADE_BT" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:bt_metadata"
	fi

	if [[ "$UPGRADE_COOKIES" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:cookies4.dat"
	fi

	if [[ "$UPGRADE_WAND" -eq 1 ]]
	then
		TO_COPY="${TO_COPY}:wand.dat"
	fi

	echo "Starting to upgrade Opera: $DIR_OP"
	echo "Copying files..."
	IFS=':'
	TO_COPY=($TO_COPY)
	for elem in "${TO_COPY[@]}"
	do
		if [[ ! -d "${DIR_PROFILE}/${elem}" && ! -f "${DIR_PROFILE}/${elem}" ]]
		then
			continue
		fi

		echo "Copying $elem"
		cp -r "${DIR_PROFILE}/${elem}" "${DIR_NEW}/${elem}"
	done
	unset IFS

	echo "Files copied."

	# DISABLED
	# if [[ "$UPGRADE_FIX_INI" -eq 1 ]]
	# then
		Fix_INI "$DIR_OP_STABLE" "$DIR_OP"

		if [[ "$UPGRADE_MAIL" -eq 1 && -d "${DIR_NEW}/mail" ]]
		then
			Fix_INI "$DIR_OP_STABLE" "$DIR_OP" "*.ini *.txt" "mail"
		fi
	# fi

	echo "Finished upgrading Opera. Make sure you check the result before trying to run Opera."
}

# This function fixes file paths in Opera INI files
Fix_INI()
{
	local LOCAL_PWD="$PWD"
	local DIR_OP1="$1" # source Opera installation path
	local DIR_OP2="$2" # destination Opera installation path
	local FIND_FILES="$3" # which files to change
	local SUBDIR="$4" # which subdir of the two profiles to process

	Check_Opera_Dir "$DIR_OP1"
	Check_Opera_Dir "$DIR_OP2"

	if [[ "$DIR_OP2" = "$DIR_OP_STABLE" ]]
	then
		echo "Error: The destination folder for the INI fixes is the same as DIR_OP_STABLE: ${DIR_OP_STABLE}. This is not allowed. The script will not alter in any way the stable Opera installation."
		exit 1
	fi

	if [[ "$DIR_OP1" = "$DIR_OP2" ]]
	then
		echo "Error: The source and destination folders for the INI fixes are the same: ${DIR_OP1}. This is not allowed."
		exit 1
	fi

	# Determine the profile folders

	if [[ -d "${DIR_OP1}/profile" ]]
	then
		local DIR_PROFILE1="${DIR_OP1}/profile"
	elif [[ -d "${DIR_OP1}/usersettings" ]]
	then
		local DIR_PROFILE1="${DIR_OP1}/usersettings"
	else
		echo "Error: I could not find the profile folder in: $DIR_OP1"
		exit 1
	fi

	if [[ -d "${DIR_OP2}/profile" ]]
	then
		local DIR_PROFILE2="${DIR_OP2}/profile"
	elif [[ -d "${DIR_OP2}/usersettings" ]]
	then
		local DIR_PROFILE2="${DIR_OP2}/usersettings"
	else
		echo "Error: I could not find the profile folder in: $DIR_OP2"
		exit 1
	fi

	if [[ ! -z "$SUBDIR" ]]
	then
		local SUBDIR1="${DIR_PROFILE1}/${SUBDIR}"
		local SUBDIR2="${DIR_PROFILE2}/${SUBDIR}"
	else
		local SUBDIR1="$DIR_PROFILE1"
		local SUBDIR2="$DIR_PROFILE2"
	fi

	if [[ ! -d "$SUBDIR2" ]]
	then
		echo "Error: The specified SUBDIR could not be found: $SUBDIR2"
		exit 1
	fi

	if [[ -z "$FIND_FILES" ]]
	then
		FIND_FILES="*.ini"
	fi

	# Start fixing the files...

	cd "$SUBDIR2"

	for FILE_IN in $FIND_FILES
	do
		# Skip some files with no important paths
		case "$FILE_IN" in
			( 'search.ini' | 'speeddial.ini' | 'spellcheck.ini' | 'urlfilter.ini' | 'override_downloaded.ini' | signature* )
			continue ;;
		esac

		if [[ ! -f "$FILE_IN" ]]
		then
			continue
		fi

		echo "Fixing $FILE_IN"

		FILE_OUT=".op2.${FILE_IN}"

		IFS=
		while read -r
		do
			local LINE="${REPLY//$SUBDIR1/$SUBDIR2}"
			if [[ "$REPLY" = "$LINE" ]]
			then
				LINE="${REPLY//$DIR_PROFILE1/$DIR_PROFILE2}"
				if [[ "$REPLY" = "$LINE" ]]
				then
					LINE="${REPLY//$DIR_OP1/$DIR_OP2}"
				fi
			fi

			# Special handling for the 'Mail Root Directory' option in the opera6.ini
			# For safety this will NEVER be the same as it was in the stable opera6.ini - this is to make sure the experimental Opera does NOT access the same Opera files
			if [[ "$FILE_IN" = 'opera6.ini' && "$LINE" =~ ^Mail\ Root\ Directory\= ]]
			then
				# Skip this line if not upgrading the mail. This way, the experimental build will not have any email
				if [[ ! "$UPGRADE_MAIL" -eq 1 ]]
				then
					continue
				fi

				# If the line was not changed in the previous steps, then the mail folder is, most likely, external
				# The script specifically does not handle this case: it will just skip the line (for safety: no email upgrades)
				if [[ "$REPLY" = "$LINE" ]]
				then
					echo "Warning: Your stable Opera installation is configured to use an external mail folder (outside the profile). This script will not copy the mail folder to the experimental build. Your experimental build will not have any emails."
					continue
				fi
			fi

			echo "$LINE"
		done <"$FILE_IN" >"$FILE_OUT"
		unset IFS

		rm "$FILE_IN"
		mv "$FILE_OUT" "$FILE_IN"
	done

	cd "$LOCAL_PWD"
}

case "${1:-''}" in

	( 'help' | '-h' | '--help' | 'man' | 'doc' )
		echo "Opera2 ($MY_BUILD) script by ROBO Design. http://www.robodesign.ro"
		echo;
		echo "Usage:"
		echo;
		echo "$MY_SELF clean"
		echo;
		echo "This will remove the profile of your latest Opera build. You can configure where experimental builds are located using the DIR_OP_DEV variable."
		echo;
		echo "$MY_SELF clean-run"
		echo;
		echo "The same as above, but Opera will be started afterwards."
		echo;
		echo "$MY_SELF unpack opera-*.tar.bz2/gz"
		echo;
		echo "This will unpack the Opera build you want designated by the second argument. The build must be provided as a tar.gz or tar.bz2 file - as the official ones are. The package will moved to the configured DIR_OP_PACK, and the content of the archive will be extracted to your DIR_OP_DEV."
		echo;
		echo "$MY_SELF unpack-run opera-*.tar.bz2/gz"
		echo;
		echo "The same as above, but the script will also start Opera from the extracted folder."
		echo;
		echo "$MY_SELF upgrade opera-*"
		echo;
		echo "This will \"upgrade\" the Opera build you want designated by the second argument - a folder name starting with \"opera-\" followed by the major and minor version (this is the default naming of Opera builds). If the second argument is not specified, the current working directory will be used. The script will COPY files from your stable profile folder to the profile of the build you want. The script will also make some changes to several copied files (will replace all paths pointing to the stable Opera installation folder to the new build)."
		echo "WARNING: Doing this will DELETE the profile of the Opera build specified as the second argument. The script does NOT try to detect if the destination folder is REALLY an experimental build. Do NOT run this on your Opera stable installation folder. Also, you should check the script source ... to be sure it will not break your stable installation. :)"
		echo "Always check the result of running this script before starting Opera."
		echo;
		echo "$MY_SELF any thing else (or no arguments)"
		echo;
		echo "This will start the latest Opera build from the configured DIR_OP_DEV with the provided arguments. You can configure other applications to automatically open links with this script - the latest Opera build."
		;;

	( 'clean' | 'clean-run' )
		Find_Opera_Latest
		Clean_Opera "$DIR_OP_LATEST"

		if [[ "$1" = 'clean-run' ]]
		then
			echo "Starting Opera with a clean profile from: $DIR_OP_LATEST"

			cd "$DIR_OP_LATEST"
			./opera
		fi

		;;

	( 'unpack' | 'unpack-run' )
		Unpack_Opera "$1" "$2"
		;;

	( 'upgrade' )
		Upgrade_Opera "$2"
		;;

	( * )
		Find_Opera_Latest

		echo "Starting Opera: $DIR_OP_LATEST"

		cd "$DIR_OP_LATEST"
		./opera $@
		;;
esac


