File activityfirefox of Package activityfox

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

## Needed for aliases to be expanded in non-interative Bash (and therefore work in script)
shopt -s expand_aliases

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

## Enable verbose debug mode when using flag `-x`
if [ "$1" == "-x" ];then
	set -x
fi

## Default settings for dialog
function f_Dialog()
{
kdialog --icon firefox "$@"
}

## Default settings for passive popups
function f_Popup()
{
notify-send --icon firefox --app-name 'Activity-aware Firefox' "$@"
}

## Use the correct plasma-activities CLI command
if command -v plasma-activities-cli6 &> /dev/null
	then
		alias plasma-activities-cli="plasma-activities-cli6"

elif command -v plasma-activities &> /dev/null
	then
		alias plasma-activities-cli="plasma-activities"

elif command -v kactivities-cli &> /dev/null
	then
		alias plasma-activities-cli="kactivities-cli"

elif ! command -v plasma-activities-cli &> /dev/null
	then
		echo ">>> Error! Requirement plasma-activities-cli missing. Exiting."
		f_Dialog --error 'Error !!' 'Requirement plasma-activities-cli missing. Exiting.'
		exit 1
fi


function f_GenHelp()
{
HELP="
New profile
-----------
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.

Minimal copy
------------
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:
- Bookmarks, Downloads and Browsing History
- Passwords
- Site-specific preferences
- Search engines
- Personal dictionary
- Autocomplete history
- Cookies
- Security certificate settings
- Download actions
- Toolbar customization
- User preferences

More on this subject: https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data

Template profile copy
---------------------
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.
In order to customize your 'template profile' from Firefox itself, start it with the following command:
firefox --profile ${FF_FOLDER}/????????.template-profile
Alternatively, just copy the files you care about from your default (or other) Firefox Profile into '${FF_FOLDER}/????????.template-profile'.

This is a great option if you know exactly which preferences etc. want to copy that you perhaps cannot sync over Firefox Sync.

Full copy
---------
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.

WARNING: Please mind that Mozilla developers advise against making full profile copy.

Migrate from pre-0.3 profile
----------------------------
Choose this option if you have used the 'activityfirefox' script in its early days (any release before 0.3).
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.


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 --directory ????????.*_????????-????-????-????-????????????); do
		PROFILE_ID=$(cut --fields 2 --delimiter '_' <<< "${PROFILE_DIR}")
		PROFILE_NAME=$(cut --fields 1 --delimiter '_' <<< "${PROFILE_DIR}" | cut --fields 2 --delimiter '.')
		ACTIVITY_STATE=$(plasma-activities-cli --activity-property name "${PROFILE_ID}")
		if [ "${ACTIVITY_STATE}" = "" ];then
			f_Dialog --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 --force "${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 --complement --delete '[:alnum:]')
				# remove section from profiles file
				if [ "${SECTION_NAME}" != "" ]; then
					gawk --include 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 --human --summarize ${TPL_PROF_PATH} | cut --fields 1)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 --directory ${NEW_PROF_PATH}/signedInUser.json)".old
	fi
	# set new FF sync instance name
	if [ -f ${NEW_PROF_PATH}/prefs.js ]; then
		sed --in-place "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"
		f_Dialog --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."
		f_Dialog --error 'Error !!' 'Requirement kdialog missing. Exiting.'
		exit 1
	fi
}

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


f_GenHelp
f_CheckDeps
f_CheckAndPrepare
f_DeleteUnusedProfile

ACTIVITY_UUID=$(plasma-activities-cli --current-activity | cut --fields 2 --delimiter ' ')
## Remove all (but '-') non alphanumeric characters from the activity name
ACTIVITY_NAME=$(plasma-activities-cli --activity-property name "${ACTIVITY_UUID}")
ACTIVITY_NAME_CURATED=$(gawk --assign 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"
		f_Dialog --error "Could not locate the default profile in ${FF_FOLDER} directory"
		exit 1
	fi
fi
DEF_PROF_SIZE="$(du --human --summarize ${DEF_PROF_PATH} | cut --fields 1)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=$(f_Dialog --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}"
		f_Popup "Created a new profile:
		${ACTIVITY_PROFILENAME}"
	fi

	# copy options and help from the default profile
	if [ "${COPY_TYPE}" = "full" ]; then
		# f_Popup --category "transfer" "Copying data to the new profile …" ## too chatty perhaps
		cp --archive ${DEF_PROF_PATH}/* ${NEW_PROF_PATH}/
		f_FSyncPrepare
		echo ">>> Copied all from default profile"
		f_Popup --category "transfer.complete" "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"
			f_Popup --urgency critical --category "transfer.error" "Could not locate an old (pre-0.3) profile in ${FF_FOLDER} directory!"
			exit 1
		fi
		# f_Popup --category "transfer" "Migrating data from the old (pre-0.3) to the new profile …" ## too chatty perhaps
		NEW_PROF_DIRNAME="$(cut --fields 1 --delimiter '.' <<< ${OLD_PROF_DIRNAME}).${ACTIVITY_NAME}_${ACTIVITY_UUID}"
		NEW_PROF_PATH="${FF_FOLDER}/${NEW_PROF_DIRNAME}"

		mv ${OLD_PROF_PATH} ${NEW_PROF_PATH}
		sed --in-place 's/'${OLD_PROF_DIRNAME}'/'${NEW_PROF_DIRNAME}'/' "${FF_FOLDER}"/profiles.ini
		sed --in-place 's/'=${ACTIVITY_UUID}'/'=${NEW_PROF_DIRNAME}'/' "${FF_FOLDER}"/profiles.ini
		f_FSyncPrepare
		echo ">>> Migrated all from this Activity’s old profile"
		f_Popup --category "transfer.complete" "Migrated all from this Activity’s old profile."
	# copy options from the template profile
	elif [ "${COPY_TYPE}" = "templ" ]; then
		# f_Popup --category "transfer" "Copying data from the template to the new profile …" ## too chatty perhaps
		cp --archive ${TPL_PROF_PATH}/* ${NEW_PROF_PATH}/
		f_FSyncPrepare
		echo ">>> Copied all from template profile"
		f_Popup --category "transfer.complete" "Copied all from the 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"
		f_Popup --category "transfer.complete" "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
		f_Dialog --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}"
	f_Popup --expire-time 10000 "Please re-login to Firefox Sync, if you use it."
fi

exit 0
openSUSE Build Service is sponsored by