#!/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}"