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