#!/usr/bin/env bash
# ------------------------------------------------------------------------
# Command line script to handle Firefox and Thunderbird extensions
# Manage installation, upgrade and removal of system wide and user extensions
#
# Depends on unzip and wget
#
# Manual available at http://bernaerts.dyndns.org/linux/74-ubuntu/271-ubuntu-firefox-thunderbird-addon-commandline
#
# 26/03/2013, V1.0 - Creation by N. Bernaerts
# 08/11/2015, V2.0 - Complete rewrite
# Add --install, --remove, --system and --user parameters
# 28/12/2016, V2.1 - Add Fedora compatibility thanks to Cedric Brandenbourger
# 07/02/2017 V2.2 - Remove zenity, as running zenity under root with wayland/mir is not working
# 17/05/2017 V2.3 - Rewrite, add path parameters, read UID and name in different ways
# 25/05/2017 V2.4 - Add --update and --list parameters
# 05/09/2017 V2.5 - Change handling of sudo commands for more compatibility
# 15/02/2018 V2.6 - Add download failure handling thanks to Rene Nyffenegger
# ------------------------------------------------------------------------
# --------------
# Functions
# --------------
function GetUID ()
{
local LOC_XPI="$1"
local LOC_UID=""
# get extension UID from install.rdf in original format (extension-uid)
LOC_UID=$(unzip -qq -p "${LOC_XPI}" install.rdf 2>/dev/null | grep "" | head -n 1 | cut -d'>' -f2 | cut -d'<' -f1)
# if extension UID not found, get it from install.rdf in new format (/dev/null | grep "em:id=" | head -n 1 | sed "s/^.*em:id=\"\([^\"]*\).*$/\1/")
# if extension UID not found, get it from key "id" in manifest.json ("id": "extension-uid")
[ "${LOC_UID}" = "" ] && LOC_UID=$(unzip -qq -p "${LOC_XPI}" manifest.json 2>/dev/null | grep "\"id\"" | head -n 1 | cut -d'"' -f4)
# if extension UID not found, get it from key "name" in manifest.json ("name": "extension name")
[ "${LOC_UID}" = "" ] && LOC_UID=$(unzip -qq -p "${LOC_XPI}" manifest.json 2>/dev/null | grep "\"name\"" | head -n 1 | cut -d'"' -f4 | tr ' ' '-')}
# display extension UID
echo "${LOC_UID}"
}
function GetName ()
{
local LOC_XPI="$1"
local LOC_NAME=""
# get extension name from install.rdf in original format (extension-uid)
LOC_NAME=$(unzip -qq -p "${LOC_XPI}" install.rdf 2>/dev/null | grep "" | head -n 1 | cut -d'>' -f2 | cut -d'<' -f1)
# if extension name not found, get it from install.rdf in new format (/dev/null | grep "em:name=" | head -n 1 | sed "s/^.*em:name//" | cut -d'"' -f2)
# if extension name not found, get it from key "name" in manifest.json ("name": "extension name")
[ "${LOC_NAME}" = "" ] && LOC_NAME=$(unzip -qq -p "${LOC_XPI}" manifest.json 2>/dev/null | grep "\"name\"" | head -n 1 | cut -d'"' -f4)
# display extension name
echo "${LOC_NAME}"
}
function GetVersion ()
{
local LOC_XPI="$1"
local LOC_VERSION=""
# get extension version from install.rdf in original format (version-number)
LOC_VERSION=$(unzip -qq -p "${LOC_XPI}" install.rdf 2>/dev/null | grep "" | head -n 1 | cut -d'>' -f2 | cut -d'<' -f1)
# if extension version not found, get it from install.rdf in new format (/dev/null | grep "em:version=" | head -n 1 | sed "s/^.*em:version//" | cut -d'"' -f2)
# if extension name not found, get it from key "name" in manifest.json ("name": "extension name")
[ "${LOC_VERSION}" = "" ] && LOC_VERSION=$(unzip -qq -p "${LOC_XPI}" manifest.json 2>/dev/null | grep "\"version\"" | head -n 1 | cut -d'"' -f4)
# display extension name
echo "${LOC_VERSION}"
}
# -------------------------------------------------------
# Check tools availability
# -------------------------------------------------------
command -v unzip >/dev/null 2>&1 || { echo "Please install unzip"; exit 1; }
command -v wget >/dev/null 2>&1 || { echo "Please install wget"; exit 1; }
# -------------------
# Default values
# -------------------
EXT_ACTION=""
EXT_FAMILY=""
EXT_TYPE=""
EXT_URL=""
EXT_PATH=""
# ---------------
# Parameters
# ---------------
# if no argument, display help
if [ $# -eq 0 ]
then
echo "Tool to install or remove mozilla firefox or thunderbird extensions"
echo "Extensions can be installed in user mode or system mode (needs sudo)"
echo "Parameters are :"
echo " --update Install or update extension to latest version"
echo " --install Install new extension only"
echo " --remove Remove installed extension"
echo " --list List installed extensions"
echo " --firefox Firefox extension"
echo " --thunderbird Thunderbird extension"
echo " --user Install/remove in user space (under $HOME)"
echo " --system Install/remove in system space (under /usr)"
echo " --path Force extension installation path"
echo " URL of .xpi extension file (download button from mozilla extension site)"
exit 1
fi
# loop to retrieve arguments
while test $# -gt 0
do
case "$1" in
"--update") EXT_ACTION="update"; shift; ;;
"--install") EXT_ACTION="install"; shift; ;;
"--remove") EXT_ACTION="remove"; shift; ;;
"--list") EXT_ACTION="list"; shift; ;;
"--firefox") EXT_FAMILY="firefox"; shift; ;;
"--thunderbird") EXT_FAMILY="thunderbird"; shift; ;;
"--user") EXT_TYPE="user"; shift; ;;
"--system") EXT_TYPE="system"; shift; ;;
"--path") shift; EXT_PATH="$1"; shift; ;;
*) EXT_URL="$1"; shift; ;;
esac
done
# check compulsory parameters
[ "${EXT_ACTION}" = "" ] && { echo "[error] You must specify the action mode as --update, --install or --remove"; exit 1; }
[ "${EXT_TYPE}" = "" ] && { echo "[error] You must specify extension type as --user or --system"; exit 1; }
# check extension URL
[ "${EXT_URL}" = "" -a "${EXT_ACTION}" != "list" ] && { echo "[error] You must specify the extension URL"; exit 1; }
# -------------------------
# Get extension family
# -------------------------
# if extension family is not set
if [ "${EXT_FAMILY}" = "" -a "${EXT_URL}" != "" ]
then
# determine if we are dealing with a firefox extension
IS_FIREFOX=$(echo "${EXT_URL}" | grep "/firefox")
[ "${IS_FIREFOX}" != "" ] && EXT_FAMILY="firefox"
# determine if we are dealing with a thunderbird extension
IS_THUNDERBIRD=$(echo "${EXT_URL}" | grep "/thunderbird")
[ "${IS_THUNDERBIRD}" != "" ] && EXT_FAMILY="thunderbird"
fi
# check extension family is defined
[ "${EXT_FAMILY}" = "" ] && { echo "[error] Could not determine extension. Please set as --firefox or --thunderbird"; exit 1; }
# ------------------------------------
# Set installation root and mode
# ------------------------------------
# if installation in user mode
if [ "${EXT_TYPE}" = "user" ]
then
# set user space installation path
[ "${EXT_FAMILY}" = "firefox" ] && PATH_USER="$HOME/.mozilla/firefox" || PATH_USER="$HOME/.thunderbird"
# get profile path
PROFILE_PATH=$(grep "Path=" "${PATH_USER}/profiles.ini" | head -n 1 | cut -d'=' -f2)
# set user profile path
[ "${PROFILE_PATH}" != "" ] && EXT_PATH="${PATH_USER}/${PROFILE_PATH}/extensions"
# if no profile, error message
[ "${EXT_PATH}" = "" ] && echo "[error] User profile doesn't exist"
# else, if system installation path has not been set, analyse distribution
elif [ "${EXT_PATH}" = "" ]
then
# detect architecture
ARCHITECTURE=$(arch)
# if system is debian based (Debian, Ubuntu, Fedora, ...)
if [ -f /etc/debian_version ]
then
[ "${EXT_FAMILY}" = "firefox" ] && EXT_PATH="/usr/lib/firefox-addons/extensions" || EXT_PATH="/usr/lib/thunderbird-addons/extensions"
# else, if system is Fedora 64
elif [ "${ARCHITECTURE}" = "x86_64" ]
then
[ "${EXT_FAMILY}" = "firefox" ] && EXT_PATH="/usr/lib64/firefox/extensions" || EXT_PATH="/usr/lib64/thunderbird/extensions"
# else set for Fedora 32
else
[ "${EXT_FAMILY}" = "firefox" ] && EXT_PATH="/usr/lib/firefox/extensions" || EXT_PATH="/usr/lib/thunderbird/extensions"
fi
fi
# set user profile path or exit if it doesn't exist
[ "${EXT_PATH}" = "" ] && { echo "[error] Extension installation path could not be determined"; exit 1; }
# -----------------------------------------
# Download .xpi file and get main data
# -----------------------------------------
# set temporary file
ADDON_XPI=$(mktemp "addon-XXXXXXXX.xpi") && rm "${ADDON_XPI}"
# if list of extensions
if [ "${EXT_URL}" != "" ]
then
# download extension if not local (file:///)
if [[ "${EXT_URL}" =~ ^file:///.* ]]
then
cp "${EXT_URL#file://}" "${ADDON_XPI}"
else
# download extension
wget --quiet -O "${ADDON_XPI}" "${EXT_URL}"
# check for failure
[ $? -ne 0 ] && { echo "[error] Could not download ${EXT_URL}"; exit 1; }
fi
# extract extension UID
EXT_UID=$(GetUID "${ADDON_XPI}")
# extract extension name
EXT_NAME=$(GetName "${ADDON_XPI}")
# extract extension name
EXT_VERSION=$(GetVersion "${ADDON_XPI}")
fi
# -----------------------------------
# Installation, Update or Removal
# -----------------------------------
# if list of extensions
if [ "${EXT_ACTION}" = "list" ]
then
# list installed XPI files
ARR_XPI=( $(ls ${EXT_PATH}/*.xpi ${EXT_PATH}/../*.xpi 2>/dev/null) )
# loop thru installed XPI
for FILE_XPI in "${ARR_XPI[@]}"
do
# extract XPI extension UID
EXT_UID=$(GetUID "${FILE_XPI}")
# extract XPI extension name
EXT_NAME=$(GetName "${FILE_XPI}")
# extract XPI extension name
EXT_VERSION=$(GetVersion "${FILE_XPI}")
# display information
echo "[present] ${EXT_FAMILY} ${EXT_TYPE} extension ${EXT_NAME}, uid=${EXT_UID}, version=${EXT_VERSION}"
done
# else, if extension UID could not be determined, error
elif [ "${EXT_UID}" = "" ]
then
# error message
echo "[error] Could not retrieve extension file from server"
# else, if action is removal and extension is not installed
elif [ "${EXT_ACTION}" = "remove" -a ! -d "${EXT_PATH}/${EXT_UID}" ]
then
# error message
echo "[warning] ${EXT_FAMILY} ${EXT_TYPE} extension ${EXT_NAME}, uid=${EXT_UID} is not installed"
# else, if action is removal
elif [ "${EXT_ACTION}" = "remove" ]
then
# remove system extension directory
[ "${EXT_TYPE}" = "user" ] && rm "${EXT_PATH}/../${EXT_UID}.xpi" || sudo rm "${EXT_PATH}/../${EXT_UID}.xpi"
[ "${EXT_TYPE}" = "user" ] && rm -R "${EXT_PATH}/${EXT_UID}" || sudo rm -R "${EXT_PATH}/${EXT_UID}"
# end message
echo "[success] ${EXT_ACTION} ${EXT_FAMILY} ${EXT_TYPE} extension ${EXT_NAME}, uid=${EXT_UID}, version=${EXT_VERSION}"
# else, if action is installation only and extension already installed
elif [ "${EXT_ACTION}" = "install" -a -d "${EXT_PATH}/${EXT_UID}" ]
then
# display installation status
echo "[warning] ${EXT_FAMILY} ${EXT_TYPE} extension ${EXT_NAME}, uid=${EXT_UID} is already installed"
# else, action is update or installation
else
# if extension already installed,
if [ -d "${EXT_PATH}/${EXT_UID}" ]
then
# update extension
EXT_ACTION="update"
# remove previous extension version
[ "${EXT_TYPE}" = "user" ] && rm "${EXT_PATH}/../${EXT_UID}.xpi" || sudo rm "${EXT_PATH}/../${EXT_UID}.xpi"
[ "${EXT_TYPE}" = "user" ] && rm -R "${EXT_PATH}/${EXT_UID}" || sudo rm -R "${EXT_PATH}/${EXT_UID}"
else
# install new extension
EXT_ACTION="install"
fi
# copy .xpi to system extension path
[ "${EXT_TYPE}" = "user" ] && mkdir -p "${EXT_PATH}/.." && cp -f "${ADDON_XPI}" "${EXT_PATH}/../${EXT_UID}.xpi" \
|| sudo cp -f "${ADDON_XPI}" "${EXT_PATH}/../${EXT_UID}.xpi"
# extract extension to system extension path
[ "${EXT_TYPE}" = "user" ] && unzip -qq "${ADDON_XPI}" -d "${EXT_PATH}/${EXT_UID}" \
|| sudo unzip -qq "${ADDON_XPI}" -d "${EXT_PATH}/${EXT_UID}"
# end message
echo "[success] ${EXT_ACTION} ${EXT_FAMILY} ${EXT_TYPE} extension ${EXT_NAME}, uid=${EXT_UID}, version=${EXT_VERSION}"
fi
# -------------
# Cleanup
# -------------
# remove downloaded file
rm -f "${ADDON_XPI}"