diff --git a/prefsCleaner.bat b/prefsCleaner.bat new file mode 100644 index 0000000..06d78b6 --- /dev/null +++ b/prefsCleaner.bat @@ -0,0 +1,134 @@ +@ECHO OFF & SETLOCAL DisableDelayedExpansion +TITLE prefs.js cleaner + +REM ## prefs.js cleaner for Windows +REM ## /!\ This script is a vendored/adapted version of Arkenfox own prefsCleaner.bat /!\ +REM ## author: @claustromaniac +REM ## version: 2.7 + +CD /D "%~dp0" + +IF /I "%~1"=="-unattended" (SET _ua=1) + +:begin +ECHO: +ECHO: +ECHO ######################################## +ECHO #### prefs.js cleaner for Windows #### +ECHO #### by claustromaniac #### +ECHO #### v2.7 #### +ECHO ######################################## +ECHO: +CALL :message "This script should be run from your Thunderbird profile directory." +ECHO It will remove any entries from prefs.js that also exist in user.js. +CALL :message "This will allow inactive preferences to be reset to their default values." +ECHO This Thunderbird profile shouldn't be in use during the process. +CALL :message "" +TIMEOUT 1 /nobreak >nul + +IF NOT DEFINED _ua ( + CHOICE /C SHE /N /M "Start [S] Help [H] Exit [E]" + CLS + IF ERRORLEVEL 3 (EXIT /B) + IF ERRORLEVEL 2 (GOTO :showhelp) +) +IF NOT EXIST "user.js" (CALL :abort "user.js not found in the current directory." 30) +IF NOT EXIST "prefs.js" (CALL :abort "prefs.js not found in the current directory." 30) +CALL :strlenCheck +CALL :FFcheck + +CALL :message "Backing up prefs.js..." +FOR /F "delims=" %%# IN ('powershell get-date -format "{yyyyMMdd_HHmmss}"') DO @SET ldt=%%# +COPY /B /V /Y prefs.js "prefs-backup-%ldt%.js" + +CALL :message "Cleaning prefs.js..." +CALL :cleanup +CALL :message "All done!" +TIMEOUT 5 >nul +ENDLOCAL +EXIT /B + +REM ########## Abort Function ########### +:abort +CALL :message %1 +TIMEOUT %~2 >nul +EXIT +REM ########## Message Function ######### +:message +ECHO: +ECHO: %~1 +ECHO: +GOTO :EOF +REM ### string length Check Function #### +:strlenCheck +SET /a cnt=0 +setlocal ENABLEDELAYEDEXPANSION +FOR /F "tokens=1,* delims=:" %%G IN ('FINDSTR /N "^" prefs.js') DO ( + ECHO:%%H >nul + SET /a cnt += 1 + IF /I "%%G" NEQ "!cnt!" ( + ECHO: + CALL :message "ERROR: line !cnt! in prefs.js is too long." + (CALL :abort "Aborting ..." 30) + ) +) +endlocal +GOTO :EOF +REM ####### Thunderbird Check Function ###### +:FFcheck +TASKLIST /FI "IMAGENAME eq thunderbird.exe" 2>NUL | FIND /I /N "thunderbird.exe">NUL +IF NOT ERRORLEVEL 1 ( + CLS + CALL :message "Thunderbird is still running." + ECHO If you're not currently using this profile you can continue, otherwise + CALL :message "close Thunderbird first!" + ECHO: + PAUSE + CLS + CALL :message "Resuming..." + TIMEOUT 5 /nobreak >nul +) +GOTO :EOF +REM ######### Cleanup Function ########## +:cleanup +FOR /F tokens^=2^ delims^=^'^" %%G IN ('FINDSTR /R /C:"^[^\"']*user_pref[ ]*\([ ]*[\"'][^\"']*[\"'][ ]*," user.js') DO ( + IF NOT ""=="%%G" (SET "[%%G]=1") +) +( + FOR /F "tokens=1,* delims=:" %%G IN ('FINDSTR /N "^" prefs.js') DO ( + IF ""=="%%H" ( + ECHO: + ) ELSE ( + FOR /F tokens^=1^,2^ delims^=^"^' %%I IN ("%%H") DO ( + IF NOT DEFINED [%%J] (ECHO:%%H) + ) + ) + ) +)>tempcleanedprefs +MOVE /Y tempcleanedprefs prefs.js +GOTO :EOF +REM ############### Help ################## +:showhelp +MODE 80,34 +CLS +CALL :message "This script creates a backup of your prefs.js file before doing anything." +ECHO It should be safe, but you can follow these steps if something goes wrong: +ECHO: +CALL :message " 1. Make sure Thunderbird is closed." +ECHO 2. Delete prefs.js in your profile folder. +CALL :message " 3. Delete Invalidprefs.js if you have one in the same folder." +ECHO 4. Rename or copy your latest backup to prefs.js. +CALL :message " 5. Run Thunderbird and see if you notice anything wrong with it." +ECHO 6. If you do notice something wrong, especially with your extensions, +CALL :message " and/or with the UI, go to about:support, and restart Thunderbird with" +ECHO add-ons disabled. Then, restart it again normally, and see if the +CALL :message " problems were solved." +ECHO: +CALL :message "If you are able to identify the cause of your issues, please bring it up" +ECHO on thunderbird user.js GitHub repository. +ECHO: +ECHO: +PAUSE +CLS +GOTO :begin +REM ##################################### diff --git a/prefsCleaner.sh b/prefsCleaner.sh new file mode 100755 index 0000000..47e3df3 --- /dev/null +++ b/prefsCleaner.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash + +## prefs.js cleaner for Linux/Mac +## /!\ This script is a vendored/adapted version of Arkenfox own prefsCleaner.sh /!\ +## author: @claustromaniac +## version: 2.1 + +## special thanks to @overdodactyl and @earthlng for a few snippets that I stol..*cough* borrowed from the updater.sh + +## DON'T GO HIGHER THAN VERSION x.9 !! ( because of ASCII comparison in update_prefsCleaner() ) + +readonly CURRDIR=$(pwd) + +## get the full path of this script (readlink for Linux, greadlink for Mac with coreutils installed) +SCRIPT_FILE=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null) + +## fallback for Macs without coreutils +[ -z "$SCRIPT_FILE" ] && SCRIPT_FILE=${BASH_SOURCE[0]} + + +AUTOUPDATE=true +QUICKSTART=false + +## download method priority: curl -> wget +DOWNLOAD_METHOD='' +if command -v curl >/dev/null; then + DOWNLOAD_METHOD='curl --max-redirs 3 -so' +elif command -v wget >/dev/null; then + DOWNLOAD_METHOD='wget --max-redirect 3 --quiet -O' +else + AUTOUPDATE=false + echo -e "No curl or wget detected.\nAutomatic self-update disabled!" +fi + +fQuit() { + ## change directory back to the original working directory + cd "${CURRDIR}" + [ "$1" -eq 0 ] && echo -e "\n$2" || echo -e "\n$2" >&2 + exit $1 +} + +fUsage() { + echo -e "\nUsage: $0 [-ds]" + echo -e " +Optional Arguments: + -s Start immediately + -d Don't auto-update prefsCleaner.sh" +} + +download_file() { # expects URL as argument ($1) + declare -r tf=$(mktemp) + + $DOWNLOAD_METHOD "${tf}" "$1" &>/dev/null && echo "$tf" || echo '' # return the temp-filename or empty string on error +} + +fFF_check() { + # there are many ways to see if thunderbird is running or not, some more reliable than others + # this isn't elegant and might not be future-proof but should at least be compatible with any environment + while [ -e lock ]; do + echo -e "\nThis Thunderbird profile seems to be in use. Close Thunderbird and try again.\n" >&2 + read -r -p "Press any key to continue." + done +} + +## returns the version number of a prefsCleaner.sh file +get_prefsCleaner_version() { + echo "$(sed -n '5 s/.*[[:blank:]]\([[:digit:]]*\.[[:digit:]]*\)/\1/p' "$1")" +} + +## updates the prefsCleaner.sh file based on the latest public version +update_prefsCleaner() { + declare -r tmpfile="$(download_file 'https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/prefsCleaner.sh')" + [ -z "$tmpfile" ] && echo -e "Error! Could not download prefsCleaner.sh" && return 1 # check if download failed + + [[ $(get_prefsCleaner_version "$SCRIPT_FILE") == $(get_prefsCleaner_version "$tmpfile") ]] && return 0 + + mv "$tmpfile" "$SCRIPT_FILE" + chmod u+x "$SCRIPT_FILE" + "$SCRIPT_FILE" "$@" -d + exit 0 +} + +fClean() { + # the magic happens here + prefs="@@" + prefexp="user_pref[ ]*\([ ]*[\"']([^\"']+)[\"'][ ]*," + while read -r line; do + if [[ "$line" =~ $prefexp && $prefs != *"@@${BASH_REMATCH[1]}@@"* ]]; then + prefs="${prefs}${BASH_REMATCH[1]}@@" + fi + done <<< "$(grep -E "$prefexp" user.js)" + + while IFS='' read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^$prefexp ]]; then + if [[ $prefs != *"@@${BASH_REMATCH[1]}@@"* ]]; then + echo "$line" + fi + else + echo "$line" + fi + done < "$1" > prefs.js +} + +fStart() { + if [ ! -e user.js ]; then + fQuit 1 "user.js not found in the current directory." + elif [ ! -e prefs.js ]; then + fQuit 1 "prefs.js not found in the current directory." + fi + + fFF_check + mkdir -p prefsjs_backups + bakfile="prefsjs_backups/prefs.js.backup.$(date +"%Y-%m-%d_%H%M")" + mv prefs.js "${bakfile}" || fQuit 1 "Operation aborted.\nReason: Could not create backup file $bakfile" + echo -e "\nprefs.js backed up: $bakfile" + echo "Cleaning prefs.js..." + fClean "$bakfile" + fQuit 0 "All done!" +} + + +while getopts "sd" opt; do + case $opt in + s) + QUICKSTART=true + ;; + d) + AUTOUPDATE=false + ;; + esac +done + +## change directory to the Thunderbird profile directory +cd "$(dirname "${SCRIPT_FILE}")" + +# Check if running as root and if any files have the owner as root/wheel. +if [ "${EUID:-"$(id -u)"}" -eq 0 ]; then + fQuit 1 "You shouldn't run this with elevated privileges (such as with doas/sudo)." +elif [ -n "$(find ./ -user 0)" ]; then + printf 'It looks like this script was previously run with elevated privileges, +you will need to change ownership of the following files to your user:\n' + find . -user 0 + fQuit 1 +fi + +[ "$AUTOUPDATE" = true ] && update_prefsCleaner "$@" + +echo -e "\n\n" +echo " ╔══════════════════════════╗" +echo " ║ prefs.js cleaner ║" +echo " ║ by claustromaniac ║" +echo " ║ v2.1 ║" +echo " ╚══════════════════════════╝" +echo -e "\nThis script should be run from your Thunderbird profile directory.\n" +echo "It will remove any entries from prefs.js that also exist in user.js." +echo "This will allow inactive preferences to be reset to their default values." +echo -e "\nThis Thunderbird profile shouldn't be in use during the process.\n" + +[ "$QUICKSTART" = true ] && fStart + +echo -e "\nIn order to proceed, select a command below by entering its corresponding number.\n" + +select option in Start Help Exit; do + case $option in + Start) + fStart + ;; + Help) + fUsage + echo -e "\nThis script creates a backup of your prefs.js file before doing anything." + echo -e "It should be safe, but you can follow these steps if something goes wrong:\n" + echo "1. Make sure Thunderbird is closed." + echo "2. Delete prefs.js in your profile folder." + echo "3. Delete Invalidprefs.js if you have one in the same folder." + echo "4. Rename or copy your latest backup to prefs.js." + echo "5. Run Thunderbird and see if you notice anything wrong with it." + echo "6. If you do notice something wrong, especially with your extensions, and/or with the UI, go to about:support, and restart Thunderbird with add-ons disabled. Then, restart it again normally, and see if the problems were solved." + echo -e "If you are able to identify the cause of your issues, please bring it up on the thunderbird user.js GitHub repository.\n" + ;; + Exit) + fQuit 0 + ;; + esac +done + +fQuit 0 diff --git a/updater.bat b/updater.bat new file mode 100644 index 0000000..80fa7ab --- /dev/null +++ b/updater.bat @@ -0,0 +1,324 @@ +@ECHO OFF & SETLOCAL EnableDelayedExpansion +TITLE thunderbird user.js updater + +REM ## thunderbird user.js updater for Windows +REM ## /!\ This script is a vendored/adapted version of Arkenfox own updater.bat /!\ +REM ## author: @claustromaniac +REM ## version: 4.19 +REM ## instructions: https://github.com/arkenfox/user.js/wiki/5.1-Updater-[Options]#-windows + +SET v=4.19 + +VERIFY ON +CD /D "%~dp0" +SET _myname=%~n0 +SET _myparams=%* + +:parse +IF "%~1"=="" (GOTO endparse) +IF /I "%~1"=="-unattended" (SET _ua=1) +IF /I "%~1"=="-log" (SET _log=1) +IF /I "%~1"=="-logp" (SET _log=1 & SET _logp=1) +IF /I "%~1"=="-multioverrides" (SET _multi=1) +IF /I "%~1"=="-merge" (SET _merge=1) +IF /I "%~1"=="-updatebatch" (SET _updateb=1) +IF /I "%~1"=="-singlebackup" (SET _singlebackup=1) +IF /I "%~1"=="-esr" (SET _esr=1) +SHIFT +GOTO parse +:endparse + +FOR /F %%i IN ('PowerShell -Command "[Enum]::GetNames([Net.SecurityProtocolType]) -contains 'Tls12'"') DO ( + IF "%%i" == "False" ( + CALL :message "Your PowerShell version doesn't support TLS1.2 ^!" + ECHO: Instructions to update PowerShell are on the arkenfox wiki + PAUSE + EXIT + ) +) + +IF DEFINED _updateb ( + REM The normal flow here goes from phase 1 to phase 2 and then phase 3. + IF NOT "!_myname:~0,9!"=="[updated]" ( + IF EXIST "[updated]!_myname!.bat" ( + REM ## Phase 3 ##: The new script, with the original name, will: + REM * Delete the [updated]*.bat and *.bat.old scripts + REM * Begin the normal routine + FC "[updated]!_myname!.bat" "!_myname!.bat.old" >nul + IF NOT "!errorlevel!"=="0" ( + CALL :message "Script updated to version !v!" + TIMEOUT 3 >nul + ) + REN "[updated]!_myname!.bat" "[updated]!_myname!.bat.old" + DEL /F "!_myname!.bat.old" "[updated]!_myname!.bat.old" + GOTO begin + ) + REM ## Phase 1 ## + REM * Download new batch and name it [updated]*.bat + REM * Start that script in a new CMD window + REM * Exit + CALL :message "Updating script..." + REM Uncomment the next line and comment out the PowerShell call for testing. + REM COPY /B /Y "!_myname!.bat" "[updated]!_myname!.bat" >nul + CALL :psdownload https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/updater.bat "[updated]!_myname!.bat" + IF EXIST "[updated]!_myname!.bat" ( + START /min CMD /C "[updated]!_myname!.bat" !_myparams! + ) ELSE ( + CALL :message "Failed. Make sure PowerShell is allowed internet access." + TIMEOUT 120 >nul + ) + ) ELSE ( + IF "!_myname!"=="[updated]" ( + CALL :message "The [updated] label is reserved. Rename this script and try again." + TIMEOUT 300 >nul + ) ELSE ( + REM ## Phase 2 ##: The [updated]*.bat script will: + REM * Rename the old script and make a copy of itself with the original name. + REM * Run that copy in a new CMD instance + REM * Exit + IF EXIST "!_myname:~9!.bat" ( REN "!_myname:~9!.bat" "!_myname:~9!.bat.old" ) + COPY /B /Y "!_myname!.bat" "!_myname:~9!.bat" + START CMD /C "!_myname:~9!.bat" !_myparams! + ) + ) + EXIT /B +) + +:begin +CLS +ECHO: +ECHO: +ECHO: ######################################## +ECHO: #### user.js Updater for Windows #### +ECHO: #### by claustromaniac #### +ECHO: #### v!v! #### +ECHO: ######################################## +ECHO: +SET /A "_line=0" +IF NOT EXIST user.js ( + CALL :message "user.js not detected in the current directory." +) ELSE ( + FOR /F "skip=1 tokens=1,* delims=:" %%G IN (user.js) DO ( + SET /A "_line+=1" + IF !_line! GEQ 4 (GOTO exitloop) + IF !_line! EQU 1 (SET _name=%%H) + IF !_line! EQU 2 (SET _date=%%H) + IF !_line! EQU 3 (SET _version=%%G) + ) + :exitloop + IF NOT "!_name!"=="" ( + SET "_tempvar=" + IF /I NOT "!_name!"=="!_name:ghacks=!" SET _tempvar=1 + IF /I NOT "!_name!"=="!_name:arkenfox=!" SET _tempvar=1 + IF !_tempvar! EQU 1 ( + CALL :message "!_name! !_version:~2!,!_date!" + ) ELSE (CALL :message "Current user.js version not recognised.") + ) ELSE (CALL :message "Current user.js version not recognised.") +) +ECHO: +IF NOT DEFINED _ua ( + CALL :message "This batch should be run from your Thunderbird profile directory." + ECHO: It will download the latest version of thunderbird user.js from github and then + CALL :message "append any of your own changes from user-overrides.js to it." + CALL :message "Visit the wiki for more detailed information." + ECHO: + TIMEOUT 1 /nobreak >nul + CHOICE /C SHE /N /M "Start [S] Help [H] Exit [E]" + CLS + IF ERRORLEVEL 3 (EXIT /B) + IF ERRORLEVEL 2 (GOTO :showhelp) +) +IF DEFINED _log ( + CALL :log >>user.js-update-log.txt 2>&1 + IF DEFINED _logp (START user.js-update-log.txt) + EXIT /B + :log + SET _log=2 + ECHO:################################################################## + ECHO: %date%, %time% +) +IF EXIST user.js.new (DEL /F "user.js.new") +CALL :message "Retrieving latest user.js file from github repository..." +CALL :psdownload https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/user.js "user.js.new" +IF EXIST user.js.new ( + IF DEFINED _esr ( + CALL :message "Activating ESR section..." + CALL :activate user.js.new ".x still uses all the following prefs" + ) + IF DEFINED _multi ( + FORFILES /P user.js-overrides /M *.js >nul 2>&1 + IF NOT ERRORLEVEL 1 ( + IF DEFINED _merge ( + CALL :message "Merging..." + COPY /B /Y user.js-overrides\*.js user-overrides-merged.js + CALL :merge user-overrides-merged.js + COPY /B /Y user.js.new+user-overrides-merged.js user.js.new + CALL :merge user.js.new + ) ELSE ( + CALL :message "Appending..." + COPY /B /Y user.js.new+"user.js-overrides\*.js" user.js.new + ) + ) ELSE (CALL :message "No override files found.") + ) ELSE ( + IF EXIST "user-overrides.js" ( + COPY /B /Y user.js.new+"user-overrides.js" "user.js.new" + IF DEFINED _merge ( + CALL :message "Merging user-overrides.js..." + CALL :merge user.js.new + ) ELSE ( + CALL :message "user-overrides.js appended." + ) + ) ELSE (CALL :message "user-overrides.js not found.") + ) + IF EXIST user.js ( + FC user.js.new user.js >nul && SET "_changed=false" || SET "_changed=true" + ) + IF "!_changed!"=="true" ( + CALL :message "Backing up..." + IF DEFINED _singlebackup ( + MOVE /Y user.js user.js.bak >nul + ) ELSE ( + FOR /F "delims=" %%# IN ('powershell get-date -format "{yyyyMMdd_HHmmss}"') DO @SET ldt=%%# + MOVE /Y user.js "user-backup-!ldt!.js" >nul + ) + REN user.js.new user.js + CALL :message "Update complete." + ) ELSE ( + IF "!_changed!"=="false" ( + DEL /F user.js.new >nul + CALL :message "Update completed without changes." + ) ELSE ( + REN user.js.new user.js + CALL :message "Update complete." + SET "_changed=true" + ) + ) +) ELSE ( + CALL :message "Update failed. Make sure PowerShell is allowed internet access." + ECHO: No changes were made. +) +IF NOT DEFINED _log ( + IF NOT DEFINED _ua ( + IF EXIST prefsCleaner.bat ( + IF "!_changed!"=="true" ( + CALL :message "Would you like to run the prefsCleaner now?" + CHOICE /C YN /N /M "(Y/N) " + IF "1"=="!errorlevel!" ( START "" cmd.exe /C "prefsCleaner.bat" ) + ) ELSE (PAUSE) + ) ELSE (PAUSE) + ) +) +EXIT /B + +::::::::::::::: Message ::::::::::::::: +:message +SETLOCAL DisableDelayedExpansion +IF NOT "2"=="%_log%" (ECHO:) +ECHO: %~1 +IF NOT "2"=="%_log%" (ECHO:) +ENDLOCAL +GOTO :EOF + +::::::::::::::: Download ::::::::::::::: +:psdownload +( + PowerShell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; (New-Object Net.WebClient).DownloadFile('%~1', '%~2')" +) >nul 2>&1 +GOTO :EOF + +::::::::::::::: Activate Section ::::::::::::::: +:activate +:: arg1 = file +:: arg2 = line substring +SETLOCAL DisableDelayedExpansion +( + FOR /F "tokens=1,* delims=:" %%G IN ('FINDSTR /N "^" "%~1"') DO ( + SET "_temp=%%H" + SETLOCAL EnableDelayedExpansion + IF "!_temp:%~2=!"=="!_temp!" ( + ENDLOCAL & ECHO:%%H + ) ELSE ( + ECHO://!_temp:~2! + ENDLOCAL + ) + ) +)>updatertempfile +MOVE /Y updatertempfile "%~1" >nul +ENDLOCAL +GOTO :EOF + +::::::::::::::: Merge ::::::::::::::: +:merge +SETLOCAL DisableDelayedExpansion +FOR /F tokens^=2^,^*^ delims^=^'^" %%G IN ('FINDSTR /R /C:"^user_pref[ ]*\([ ]*[\"'].*[\"'][ ]*,.*\)[ ]*;" "%~1"') DO (SET "[%%G]=%%H") +FOR /F tokens^=2^,^*^ delims^=^' %%G IN ('FINDSTR /R /C:"^//// --- comment-out --- '[^'][^']*'.*" "%~1"') DO (SET "__unset__%%G=1") +( + FOR /F "tokens=1,* delims=:" %%I IN ('FINDSTR /N "^" "%~1"') DO ( + SET "_temp=%%J" + SETLOCAL EnableDelayedExpansion + IF NOT "!_temp:~0,9!"=="user_pref" ( + ENDLOCAL & ECHO:%%J + ) ELSE ( + IF "!_temp:;=!"=="!_temp!" ( + ENDLOCAL & ECHO:%%J + ) ELSE ( + ENDLOCAL + FOR /F tokens^=2^ delims^=^'^" %%K IN ("%%J") DO ( + IF NOT "_user.js.parrot"=="%%K" ( + IF DEFINED __unset__%%K ( + ECHO://%%J + ) ELSE ( + IF DEFINED [%%K] ( + SETLOCAL EnableDelayedExpansion + FOR /F "delims=" %%L IN ("![%%K]!") DO ( + ENDLOCAL & ECHO:user_pref("%%K"%%L + SET "[%%K]=" + ) + ) + ) + ) ELSE (ECHO:%%J) + ) + ) + ) + ) +)>updatertempfile +MOVE /Y updatertempfile "%~1" >nul +ENDLOCAL +GOTO :EOF + +::::::::::::::: Help ::::::::::::::: +:showhelp +MODE 80,54 +CLS +CALL :message "Available arguments (case-insensitive):" +CALL :message " -esr" +ECHO: Activate ESR related preferences +CALL :message " -log" +ECHO: Write the console output to a logfile (user.js-update-log.txt) +CALL :message " -logP" +ECHO: Like -log, but also open the logfile after updating. +CALL :message " -merge" +ECHO: Merge overrides instead of appending them. Single-line comments and +ECHO: _user.js.parrot lines are appended normally. Overrides for inactive +ECHO: user.js prefs will be appended. When -Merge and -MultiOverrides are used +ECHO: together, a user-overrides-merged.js file is also generated in the root +ECHO: directory for quick reference. It contains only the merged data from +ECHO: override files and can be safely discarded after updating, or used as the +ECHO: new user-overrides.js. When there are conflicting records for the same +ECHO: pref, the value of the last one declared will be used. Visit the wiki +ECHO: for usage examples and more detailed information. +CALL :message " -multiOverrides" +ECHO: Use any and all .js files in a user.js-overrides sub-folder as overrides +ECHO: instead of the default user-overrides.js file. Files are appended in +ECHO: alphabetical order. +CALL :message " -unattended" +ECHO: Run without user input. +CALL :message " -singleBackup" +ECHO: Use a single backup file and overwrite it on new updates, instead of +ECHO: cumulative backups. This was the default behaviour before v4.3. +CALL :message " -updateBatch" +ECHO: Update the script itself on execution, before the normal routine. +CALL :message "" +PAUSE +MODE 80,25 +GOTO :begin diff --git a/updater.sh b/updater.sh new file mode 100755 index 0000000..4d7c202 --- /dev/null +++ b/updater.sh @@ -0,0 +1,408 @@ +#!/usr/bin/env bash + +## thunderbird user.js updater for macOS and Linux +## /!\ This script is a vendored/adapted version of Arkenfox own updater.sh /!\ + +## version: 4.0 +## Author: Pat Johnson (@overdodactyl) +## Additional contributors: @earthlng, @ema-pe, @claustromaniac, @infinitewarp + +## DON'T GO HIGHER THAN VERSION x.9 !! ( because of ASCII comparison in update_updater() ) + +# Check if running as root +if [ "${EUID:-"$(id -u)"}" -eq 0 ]; then + printf "You shouldn't run this with elevated privileges (such as with doas/sudo).\n" + exit 1 +fi + +readonly CURRDIR=$(pwd) + +SCRIPT_FILE=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null) +[ -z "$SCRIPT_FILE" ] && SCRIPT_FILE=${BASH_SOURCE[0]} +readonly SCRIPT_DIR=$(dirname "${SCRIPT_FILE}") + + +######################### +# Base variables # +######################### + +# Colors used for printing +RED='\033[0;31m' +BLUE='\033[0;34m' +BBLUE='\033[1;34m' +GREEN='\033[0;32m' +ORANGE='\033[0;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Argument defaults +UPDATE='check' +CONFIRM='yes' +OVERRIDE='user-overrides.js' +BACKUP='multiple' +COMPARE=false +SKIPOVERRIDE=false +VIEW=false +PROFILE_PATH=false +ESR=false + +# Download method priority: curl -> wget +DOWNLOAD_METHOD='' +if command -v curl >/dev/null; then + DOWNLOAD_METHOD='curl --max-redirs 3 -so' +elif command -v wget >/dev/null; then + DOWNLOAD_METHOD='wget --max-redirect 3 --quiet -O' +else + echo -e "${RED}This script requires curl or wget.\nProcess aborted${NC}" + exit 1 +fi + + +show_banner() { + echo -e "${BBLUE} + ############################################################################ + #### #### + #### thunderbird user.js #### + #### Hardening the Privacy and Security Settings of Thunderbird #### + #### Maintained by @Thorin-Oakenpants and @earthlng #### + #### Updater for macOS and Linux by @overdodactyl #### + #### #### + ############################################################################" + echo -e "${NC}\n" + echo -e "Documentation for this script is available here: ${CYAN}https://github.com/arkenfox/user.js/wiki/5.1-Updater-[Options]#-maclinux${NC}\n" +} + +######################### +# Arguments # +######################### + +usage() { + echo + echo -e "${BLUE}Usage: $0 [-bcdehlnrsuv] [-p PROFILE] [-o OVERRIDE]${NC}" 1>&2 # Echo usage string to standard error + echo -e " +Optional Arguments: + -h Show this help message and exit. + -p PROFILE Path to your Thunderbird profile (if different than the dir of this script) + IMPORTANT: If the path contains spaces, wrap the entire argument in quotes. + -l Choose your Thunderbird profile from a list + -u Update updater.sh and execute silently. Do not seek confirmation. + -d Do not look for updates to updater.sh. + -s Silently update user.js. Do not seek confirmation. + -b Only keep one backup of each file. + -c Create a diff file comparing old and new user.js within userjs_diffs. + -o OVERRIDE Filename or path to overrides file (if different than user-overrides.js). + If used with -p, paths should be relative to PROFILE or absolute paths + If given a directory, all files inside will be appended recursively. + You can pass multiple files or directories by passing a comma separated list. + Note: If a directory is given, only files inside ending in the extension .js are appended + IMPORTANT: Do not add spaces between files/paths. Ex: -o file1.js,file2.js,dir1 + IMPORTANT: If any file/path contains spaces, wrap the entire argument in quotes. + Ex: -o \"override folder\" + -n Do not append any overrides, even if user-overrides.js exists. + -v Open the resulting user.js file. + -r Only download user.js to a temporary file and open it. + -e Activate ESR related preferences." + echo + exit 1 +} + +######################### +# File Handling # +######################### + +download_file() { # expects URL as argument ($1) + declare -r tf=$(mktemp) + + $DOWNLOAD_METHOD "${tf}" "$1" &>/dev/null && echo "$tf" || echo '' # return the temp-filename or empty string on error +} + +open_file() { # expects one argument: file_path + if [ "$(uname)" == 'Darwin' ]; then + open "$1" + elif [ "$(uname -s | cut -c -5)" == "Linux" ]; then + xdg-open "$1" + else + echo -e "${RED}Error: Sorry, opening files is not supported for your OS.${NC}" + fi +} + +readIniFile() { # expects one argument: absolute path of profiles.ini + declare -r inifile="$1" + + # tempIni will contain: [ProfileX], Name=, IsRelative= and Path= (and Default= if present) of the only (if) or the selected (else) profile + if [ "$(grep -c '^\[Profile' "${inifile}")" -eq "1" ]; then ### only 1 profile found + tempIni="$(grep '^\[Profile' -A 4 "${inifile}")" + else + echo -e "Profiles found:\n––––––––––––––––––––––––––––––" + ## cmd-substitution to strip trailing newlines and in quotes to keep internal ones: + echo "$(grep --color=never -E 'Default=[^1]|\[Profile[0-9]*\]|Name=|Path=|^$' "${inifile}")" + echo '––––––––––––––––––––––––––––––' + read -p 'Select the profile number ( 0 for Profile0, 1 for Profile1, etc ) : ' -r + echo -e "\n" + if [[ $REPLY =~ ^(0|[1-9][0-9]*)$ ]]; then + tempIni="$(grep "^\[Profile${REPLY}" -A 4 "${inifile}")" || { + echo -e "${RED}Profile${REPLY} does not exist!${NC}" && exit 1 + } + else + echo -e "${RED}Invalid selection!${NC}" && exit 1 + fi + fi + + # extracting 0 or 1 from the "IsRelative=" line + declare -r pathisrel=$(sed -n 's/^IsRelative=\([01]\)$/\1/p' <<< "${tempIni}") + + # extracting only the path itself, excluding "Path=" + PROFILE_PATH=$(sed -n 's/^Path=\(.*\)$/\1/p' <<< "${tempIni}") + # update global variable if path is relative + [[ ${pathisrel} == "1" ]] && PROFILE_PATH="$(dirname "${inifile}")/${PROFILE_PATH}" +} + +getProfilePath() { + declare -r f1=~/Library/Application\ Support/Thunderbird/profiles.ini + declare -r f2=~/.thunderbird/profiles.ini + + if [ "$PROFILE_PATH" = false ]; then + PROFILE_PATH="$SCRIPT_DIR" + elif [ "$PROFILE_PATH" = 'list' ]; then + if [[ -f "$f1" ]]; then + readIniFile "$f1" # updates PROFILE_PATH or exits on error + elif [[ -f "$f2" ]]; then + readIniFile "$f2" + else + echo -e "${RED}Error: Sorry, -l is not supported for your OS${NC}" + exit 1 + fi + #else + # PROFILE_PATH already set by user with -p + fi +} + +######################### +# Update updater.sh # +######################### + +# Returns the version number of a updater.sh file +get_updater_version() { + echo "$(sed -n '5 s/.*[[:blank:]]\([[:digit:]]*\.[[:digit:]]*\)/\1/p' "$1")" +} + +# Update updater.sh +# Default: Check for update, if available, ask user if they want to execute it +# Args: +# -d: New version will not be looked for and update will not occur +# -u: Check for update, if available, execute without asking +update_updater() { + [ "$UPDATE" = 'no' ] && return 0 # User signified not to check for updates + + declare -r tmpfile="$(download_file 'https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/updater.sh')" + [ -z "${tmpfile}" ] && echo -e "${RED}Error! Could not download updater.sh${NC}" && return 1 # check if download failed + + if [[ $(get_updater_version "$SCRIPT_FILE") < $(get_updater_version "${tmpfile}") ]]; then + if [ "$UPDATE" = 'check' ]; then + echo -e "There is a newer version of updater.sh available. ${RED}Update and execute Y/N?${NC}" + read -p "" -n 1 -r + echo -e "\n\n" + [[ $REPLY =~ ^[Yy]$ ]] || return 0 # Update available, but user chooses not to update + fi + else + return 0 # No update available + fi + mv "${tmpfile}" "$SCRIPT_FILE" + chmod u+x "$SCRIPT_FILE" + "$SCRIPT_FILE" "$@" -d + exit 0 +} + +######################### +# Update user.js # +######################### + +# Returns version number of a user.js file +get_userjs_version() { + [ -e "$1" ] && echo "$(sed -n '4p' "$1")" || echo "Not detected." +} + +add_override() { + input=$1 + if [ -f "$input" ]; then + echo "" >> user.js + cat "$input" >> user.js + echo -e "Status: ${GREEN}Override file appended:${NC} ${input}" + elif [ -d "$input" ]; then + SAVEIFS=$IFS + IFS=$'\n\b' # Set IFS + FILES="${input}"/*.js + for f in $FILES + do + add_override "$f" + done + IFS=$SAVEIFS # restore $IFS + else + echo -e "${ORANGE}Warning: Could not find override file:${NC} ${input}" + fi +} + +remove_comments() { # expects 2 arguments: from-file and to-file + sed -e '/^\/\*.*\*\/[[:space:]]*$/d' -e '/^\/\*/,/\*\//d' -e 's|^[[:space:]]*//.*$||' -e '/^[[:space:]]*$/d' -e 's|);[[:space:]]*//.*|);|' "$1" > "$2" +} + +# Applies latest version of user.js and any custom overrides +update_userjs() { + declare -r newfile="$(download_file 'https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/user.js')" + [ -z "${newfile}" ] && echo -e "${RED}Error! Could not download user.js${NC}" && return 1 # check if download failed + + echo -e "Please observe the following information: + Thunderbird profile: ${ORANGE}$(pwd)${NC} + Available online: ${ORANGE}$(get_userjs_version "$newfile")${NC} + Currently using: ${ORANGE}$(get_userjs_version user.js)${NC}\n\n" + + if [ "$CONFIRM" = 'yes' ]; then + echo -e "This script will update to the latest user.js file and append any custom configurations from user-overrides.js. ${RED}Continue Y/N? ${NC}" + read -p "" -n 1 -r + echo -e "\n" + if ! [[ $REPLY =~ ^[Yy]$ ]]; then + echo -e "${RED}Process aborted${NC}" + rm "$newfile" + return 1 + fi + fi + + # Copy a version of user.js to diffs folder for later comparison + if [ "$COMPARE" = true ]; then + mkdir -p userjs_diffs + cp user.js userjs_diffs/past_user.js &>/dev/null + fi + + # backup user.js + mkdir -p userjs_backups + local bakname="userjs_backups/user.js.backup.$(date +"%Y-%m-%d_%H%M")" + [ "$BACKUP" = 'single' ] && bakname='userjs_backups/user.js.backup' + cp user.js "$bakname" &>/dev/null + + mv "${newfile}" user.js + echo -e "Status: ${GREEN}user.js has been backed up and replaced with the latest version!${NC}" + + if [ "$ESR" = true ]; then + sed -e 's/\/\* \(ESR[0-9]\{2,\}\.x still uses all.*\)/\/\/ \1/' user.js > user.js.tmp && mv user.js.tmp user.js + echo -e "Status: ${GREEN}ESR related preferences have been activated!${NC}" + fi + + # apply overrides + if [ "$SKIPOVERRIDE" = false ]; then + while IFS=',' read -ra FILES; do + for FILE in "${FILES[@]}"; do + add_override "$FILE" + done + done <<< "$OVERRIDE" + fi + + # create diff + if [ "$COMPARE" = true ]; then + pastuserjs='userjs_diffs/past_user.js' + past_nocomments='userjs_diffs/past_userjs.txt' + current_nocomments='userjs_diffs/current_userjs.txt' + + remove_comments "$pastuserjs" "$past_nocomments" + remove_comments user.js "$current_nocomments" + + diffname="userjs_diffs/diff_$(date +"%Y-%m-%d_%H%M").txt" + diff=$(diff -w -B -U 0 "$past_nocomments" "$current_nocomments") + if [ -n "$diff" ]; then + echo "$diff" > "$diffname" + echo -e "Status: ${GREEN}A diff file was created:${NC} ${PWD}/${diffname}" + else + echo -e "Warning: ${ORANGE}Your new user.js file appears to be identical. No diff file was created.${NC}" + [ "$BACKUP" = 'multiple' ] && rm "$bakname" &>/dev/null + fi + rm "$past_nocomments" "$current_nocomments" "$pastuserjs" &>/dev/null + fi + + [ "$VIEW" = true ] && open_file "${PWD}/user.js" +} + +######################### +# Execute # +######################### + +if [ $# != 0 ]; then + # Display usage if first argument is -help or --help + if [ "$1" = '--help' ] || [ "$1" = '-help' ]; then + usage + else + while getopts ":hp:ludsno:bcvre" opt; do + case $opt in + h) + usage + ;; + p) + PROFILE_PATH=${OPTARG} + ;; + l) + PROFILE_PATH='list' + ;; + u) + UPDATE='yes' + ;; + d) + UPDATE='no' + ;; + s) + CONFIRM='no' + ;; + n) + SKIPOVERRIDE=true + ;; + o) + OVERRIDE=${OPTARG} + ;; + b) + BACKUP='single' + ;; + c) + COMPARE=true + ;; + v) + VIEW=true + ;; + e) + ESR=true + ;; + r) + tfile="$(download_file 'https://raw.githubusercontent.com/HorlogeSkynet/thunderbird-user.js/master/user.js')" + [ -z "${tfile}" ] && echo -e "${RED}Error! Could not download user.js${NC}" && exit 1 # check if download failed + mv "$tfile" "${tfile}.js" + echo -e "${ORANGE}Warning: user.js was saved to temporary file ${tfile}.js${NC}" + open_file "${tfile}.js" + exit 0 + ;; + \?) + echo -e "${RED}\n Error! Invalid option: -$OPTARG${NC}" >&2 + usage + ;; + :) + echo -e "${RED}Error! Option -$OPTARG requires an argument.${NC}" >&2 + exit 2 + ;; + esac + done + fi +fi + +show_banner +update_updater "$@" + +getProfilePath # updates PROFILE_PATH or exits on error +cd "$PROFILE_PATH" || exit 1 + +# Check if any files have the owner as root/wheel. +if [ -n "$(find ./ -user 0)" ]; then + printf 'It looks like this script was previously run with elevated privileges, +you will need to change ownership of the following files to your user:\n' + find . -user 0 + cd "$CURRDIR" + exit 1 +fi + +update_userjs + +cd "$CURRDIR"