#!/bin/bash
#
# Compatability for Firefox and KDE Plasma Activities
#
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: © 2021 Matija Šuklje <matija@suklje.name>, 2022 Cristian Le <git@lecris.me>, 2022 Jarosław Czarniak <jaroslaw@czarniak.org>
#
# Required: kdialog
# Recomended: gio
#


## Edit to where the profiles are to be stored and used
FF_FOLDER="${HOME}/.mozilla/firefox"
KDIALOG="kdialog --icon firefox"
W=$(xdpyinfo | awk '/dimensions/{print $2}'|cut -f1 -d'x')
H=$(xdpyinfo | awk '/dimensions/{print $2}'|cut -f2 -d'x')
set +f	# enable wildcard expansion if disabled

if [ "$1" == "-x" ];then
	set -x
fi

function f_GenHelp()
{
	HELP="
	New profile\n
	-----------\n
	This option will create a fresh new Firefox Profile without copying anything. This is the fastest method, but you will end up with Firefox default settings, so you might need to manually modify settings yourself.\n
	\n
	Minimal copy\n
	------------\n
	This is a good trade-off between convenience and resource usage. Only a few important files will be copied over from your default Firefox Profile:\n
	- Bookmarks, Downloads and Browsing History\n
	- Passwords\n
	- Site-specific preferences\n
	- Search engines\n
	- Personal dictionary\n
	- Autocomplete history\n
	- Cookies\n
	- Security certificate settings\n
	- Download actions\n
	- Toolbar customization\n
	- User preferences\n
	\n
	More on this subject: https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data\n
	\n
	Template profile copy\n
	---------------------\n
	On its first run this script created an empty profile called 'Template profile' in ${FF_FOLDER} directory. If you choose the 'Template profile copy' option everything you set up there will be copied to the new activity profile.\n
	In order to customize your 'template profile' from Firefox itself, start it with the following command:\n
	firefox --profile ${FF_FOLDER}/????????.template-profile\n
	Alternatively, just copy the files you care about from your default (or other) Firefox Profile into '${FF_FOLDER}/????????.template-profile'.\n
	\n
	This is a great option if you know exactly which preferences etc. want to copy that you perhaps cannot sync over Firefox Sync.\n
	\n
	Full copy\n
	---------\n
	This option will copy all data found in the default Firefox Profile found under '${FF_FOLDER}/????????.default-release' (or alternatively under '${FF_FOLDER}/????????.default'). Please mind it _might_ take a long time and take a lot of disk space.\n
	\n
	WARNING: Please mind that Mozilla developers advise against making full profile copy.\n
	\n
	Migrate from pre-0.3 profile\n
	----------------------------\n
	Choose this option if you have used the 'activityfirefox' script in its early days (any release before 0.3).\n
	This option will try to identify the Firefox Profile associated with the KDE Plasma Activity you ran the script in and rename the Profile to fit the new schema.\n
	\n
	\n
	THIS SCRIPT WILL EXIT NOW."

	echo -ne ${HELP} > ${FF_FOLDER}/activityfirefox.help
}

function f_DeleteUnusedProfile()
{
	cd "${FF_FOLDER}" || exit
	for PROFILE_DIR in $(set +f;ls -d ????????.*_????????-????-????-????-????????????); do
		PROFILE_ID=$(cut -f2 -d'_' <<< "${PROFILE_DIR}")
		PROFILE_NAME=$(cut -f1 -d'_' <<< "${PROFILE_DIR}"| cut -f2 -d'.')
		ACTIVITY_STATE=$(qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityState "${PROFILE_ID}")
		if [ "${ACTIVITY_STATE}" = "0" ];then
			${KDIALOG} --title "Missing Activity" --warningyesno "Activity ${PROFILE_NAME} doesn't exists anymore.\nRemove unused FF profile directory?\n(${PROFILE_DIR})"
			if [ $? -eq 0 ]; then	# yes
				if [ $GIO_EXISTS -eq 1 ]; then
					# move profile dir to trash
					gio trash "${PROFILE_DIR}" || true
				else
					# more direct approach. move profile dir to trash manually
					mv -f "${FF_FOLDER}/${PROFILE_DIR}" "${HOME}/.local/share/Trash/files"
				fi
				# get profile section name with matching "Name" value
				SECTION_NAME=$(awk '/\[Profile[0-9]*\]/ {label=$0} /Name='${PROFILE_NAME}'_'${PROFILE_ID}'/ {print label}' "${FF_FOLDER}/profiles.ini"|tr -cd '[:alnum:]')
				# remove section from profiles file
				if [ "${SECTION_NAME}" != "" ]; then
					gawk -i inplace '/^\[Profile/{if($0~/\['${SECTION_NAME}'\]/){found=1}else{found=""}} !found' "${FF_FOLDER}/profiles.ini"
				fi
			fi
		fi
	done
}

function f_CheckAndPrepare()
{
	## Template profile path can be used for profile copying.
	TPL_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.template-profile")
	TPL_PROF_PATH="${FF_FOLDER}/${TPL_PROF_DIRNAME}"
	if [ ! -d ${TPL_PROF_PATH} ]; then
		$FF_BIN -CreateProfile "template-profile"
	fi
	TPL_PROF_SIZE="$(du -hs ${TPL_PROF_PATH} | cut -f1)B"
}

function f_FSyncPrepare()
{

	# new instance reqs a new token otherwise it will be visible as the old one. re-login is required.
	if [ -f {NEW_PROF_PATH}/signedInUser.json ]; then
		mv ${NEW_PROF_PATH}/signedInUser.json $(ls -d ${NEW_PROF_PATH}/signedInUser.json).old
	fi
	# set new FF sync instance name
	if [ -f ${NEW_PROF_PATH}/prefs.js ]; then
		sed -i "s/user_pref(\"identity.fxaccounts.account.device.name\",.*);/user_pref(\"identity.fxaccounts.account.device.name\", \"${FF_SYNC_NAME}\");/" ${NEW_PROF_PATH}/prefs.js
	fi
}

function f_CheckDeps()
{
	FF_BIN=$(which firefox)
	## Not the most secure way to check for firefox existence but it does its job
	if [ $? -ne 0 ]; then
		echo ">>> Could not locate firefox executable"
		${KDIALOG} --error "Could not locate firefox executable"
		exit 1
	fi

	#check if gio cmd exists on a system
	GIO_EXISTS=0
	command -v gio > /dev/null 2>&1
	if (( $? == 0 )) ; then
		GIO_EXISTS=1
	fi

	#check if kdialog exists on a system
	command -v kdialog > /dev/null 2>&1
	if (( $? != 0 )) ; then
		echo ">>> Error! Requirement kdialog missing. Exiting."
		notify-send -u critical  -i "firefox" 'Error !!' 'Requirement kdialog missing. Exiting.'
		exit 1
	fi
}

function f_GetDirName()
{
	if [ -d $1 ]; then 
		basename $(ls -d $1)
	else
		echo "DirectoryNotFound"
	fi
}


f_GenHelp
f_CheckDeps
f_CheckAndPrepare
f_DeleteUnusedProfile

ACTIVITY_UUID=$(qdbus org.kde.ActivityManager /ActivityManager/Activities CurrentActivity)
## Remove all (but '-') non alphanumeric characters from the activity name
ACTIVITY_NAME=$(qdbus org.kde.ActivityManager /ActivityManager/Activities ActivityName "${ACTIVITY_UUID}")
ACTIVITY_NAME_CURATED=$(gawk -v RS='( |[^[:alnum:]-])+' '{printf "%s", sep $0; sep=""}'<<<"${ACTIVITY_NAME}")

FF_SYNC_NAME="${ACTIVITY_NAME} @ ${HOSTNAME}"
ACTIVITY_PROFILENAME="${ACTIVITY_NAME_CURATED}_${ACTIVITY_UUID}"
echo ">>> ${ACTIVITY_NAME_CURATED}"

## Default profile
DEF_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.default-release")
DEF_PROF_PATH="${FF_FOLDER}/${DEF_PROF_DIRNAME}"

if [ ! -d ${DEF_PROF_PATH} ]; then
	DEF_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.default")
	DEF_PROF_PATH="${FF_FOLDER}/${DEF_PROF_DIRNAME}"
	if [ ! -d ${DEF_PROF_PATH} ]; then
		echo ">>> Could not locate the default profile"
		${KDIALOG} --error "Could not locate the default profile in ${FF_FOLDER} directory"
		exit 1
	fi
fi
DEF_PROF_SIZE="$(du -hs ${DEF_PROF_PATH} | cut -f1)B"

## Desired profile path of the current activity, ???????? indicate randomly generated firefox names
NEW_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.${ACTIVITY_PROFILENAME}")	# ex: ky10cbmo.test_9936a864-799e-4393-a93c-12c3b980272c 
NEW_PROF_PATH="${FF_FOLDER}/${NEW_PROF_DIRNAME}"									# ex: /home/XYZ/.mozilla/firefox/ky10cbmo.test_9936a864-799e-4393-a93c-12c3b980272c

if [ -d ${NEW_PROF_PATH} ]; then													## A profile already exist
	exec ${FF_BIN} --profile ${NEW_PROF_PATH} "$@" &								# Start with the profile with the given path. 
	echo ">>> Started an existing profile: ${ACTIVITY_PROFILENAME}"
else
	# noop option is used only as a visual separator
	WINW=320
	WINH=320
	GEO="${WINW}x${WINH}+$((${W}/2-${WINW}/2))+$((${H}/2-${WINH}/2))"
	COPY_TYPE=$(${KDIALOG} --geometry "${GEO}" --menu "Select new profile type:" new "New profile (0MB)" minimal "Minimal copy (< 3MB)" templ "Template profile copy (${TPL_PROF_SIZE})" full "Full copy (${DEF_PROF_SIZE})" old "Migrate from pre-0.3 profile" noop "__________________________________" help "[Help me decide]")

	# cancel. do nothing and exit.
	if [ $? -eq 1 ]; then
		exit 1
	fi

	# create a new profile only if valid copy option was choosen
	if [ "${COPY_TYPE}" != "help" ] && [ "${COPY_TYPE}" != "noop" ] && [ "${COPY_TYPE}" != "old" ]; then
		#https://wiki.mozilla.org/Firefox/CommandLineOptions#-CreateProfile_profile_name
		$FF_BIN -CreateProfile ${ACTIVITY_PROFILENAME}
		NEW_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.${ACTIVITY_PROFILENAME}")						# Find dir again after it being created
		NEW_PROF_PATH="${FF_FOLDER}/${NEW_PROF_DIRNAME}"
		echo ">>> Created a new profile: ${ACTIVITY_PROFILENAME}"
		${KDIALOG} --msgbox "Created a new profile:\n${ACTIVITY_PROFILENAME}"
	fi

	# copy options and help from the default profile
	if [ "${COPY_TYPE}" = "full" ]; then
		${KDIALOG} --title "Copy in progress" --passivepopup "Copying data to the new profile" 30
		cp -a ${DEF_PROF_PATH}/* ${NEW_PROF_PATH}/
		f_FSyncPrepare
		echo ">>> Copied all from default profile"
		${KDIALOG} --msgbox "Copied all from a default profile."
	# rename old (0.1 or 0.2) profile to the new naming schema
	elif [ "${COPY_TYPE}" = "old" ]; then
		OLD_PROF_DIRNAME=$(f_GetDirName "${FF_FOLDER}/????????.${ACTIVITY_UUID}")	# ex: ky10cbmo.9936a864-799e-4393-a93c-12c3b980272c 
		OLD_PROF_PATH="${FF_FOLDER}/${OLD_PROF_DIRNAME}"							# ex: /home/XYZ/.mozilla/firefox/ky10cbmo.9936a864-799e-4393-a93c-12c3b980272c 

		if [ ! -d ${OLD_PROF_PATH} ]; then
			echo ">>> Could not locate an old (pre-0.3) profile"
			${KDIALOG} --error "Could not locate an old (pre-0.3) profile in ${FF_FOLDER} directory"
			exit 1
		fi
		${KDIALOG} --title "Migration in progress" --passivepopup "Moving data to the new profile" 30
		NEW_PROF_DIRNAME="$(cut -f1 -d'.' <<< ${OLD_PROF_DIRNAME}).${ACTIVITY_NAME}_${ACTIVITY_UUID}"
		NEW_PROF_PATH="${FF_FOLDER}/${NEW_PROF_DIRNAME}"

		mv ${OLD_PROF_PATH} ${NEW_PROF_PATH}
		sed -i 's/'${OLD_PROF_DIRNAME}'/'${NEW_PROF_DIRNAME}'/' "${FF_FOLDER}"/profiles.ini
		sed -i 's/'=${ACTIVITY_UUID}'/'=${NEW_PROF_DIRNAME}'/' "${FF_FOLDER}"/profiles.ini
		f_FSyncPrepare
		echo ">>> Migrated all from this Activity’s old profile"
		${KDIALOG} --msgbox "Migrated all from this Activity’s old profile."
	# copy options from the template profile
	elif [ "${COPY_TYPE}" = "templ" ]; then
		${KDIALOG} --title "Copy in progress" --passivepopup "Copying data to the new profile" 15
		cp -a ${TPL_PROF_PATH}/* ${NEW_PROF_PATH}/
		f_FSyncPrepare
		echo ">>> Copied all from template profile"
		${KDIALOG} --msgbox "Copied all from template profile."
	# copy limited options from the default profile
	elif [ "${COPY_TYPE}" = "minimal" ]; then
		cp ${DEF_PROF_PATH}/places.sqlite ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/favicons.sqlite ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/key4.db ${NEW_PROF_PATH}/			 2>/dev/null
		cp ${DEF_PROF_PATH}/logins.json ${NEW_PROF_PATH}/		 2>/dev/null
		cp ${DEF_PROF_PATH}/permissions.sqlite ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/search.json.mozlz4 ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/formhistory.sqlite ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/cookies.sqlite ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/cert9.db ${NEW_PROF_PATH}/		 2>/dev/null
		cp ${DEF_PROF_PATH}/handlers.json ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/xulstore.json ${NEW_PROF_PATH}/	 2>/dev/null
		cp ${DEF_PROF_PATH}/prefs.js ${NEW_PROF_PATH}/		 2>/dev/null
		cp ${DEF_PROF_PATH}/containers.json ${NEW_PROF_PATH}/	 2>/dev/null
		echo ">>> Minimal data set copied"
		${KDIALOG} --msgbox "Minimal data set copied."
	# create empty new profile
	elif [ "${COPY_TYPE}" = "new" ]; then
		echo ">>> No data need to be copy"
	# open up help text
	elif [ "${COPY_TYPE}" = "help" ]; then
		kdialog --title "Help" --textbox ${FF_FOLDER}/activityfirefox.help 1024 1024
		exit 0
	fi

	exec ${FF_BIN} --profile ${NEW_PROF_PATH} "$@" &								# Start with the profile with the given path. 
	echo ">>> Started the new profile: ${ACTIVITY_PROFILENAME}"
	${KDIALOG} --msgbox "Please re-login to FF Sync."
fi

exit 0
