updater.sh - POSIX
#!/usr/bin/env sh | |
## ghacks-user.js updater for macOS and Linux | |
## version: 2.5.1 | |
## Author: Pat Johnson (@overdodactyl) | |
## Additional contributors: @earthlng, @ema-pe, @claustromaniac, @iamtpage | |
## DON'T GO HIGHER THAN VERSION x.9 !! ( because of ASCII comparison in update_updater() ) | |
set -e | |
readonly CURRDIR=$(pwd) | |
sfp=$(readlink -f "$0" 2>/dev/null || readlink -f "$0" 2>/dev/null) | |
if [ -z "$sfp" ]; then sfp="$0"; fi | |
readonly SCRIPT_DIR=$(dirname "${sfp}") | |
######################### | |
# Base variables # | |
######################### | |
# 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")" ]; then | |
DOWNLOAD_METHOD="curl" | |
elif [ "$(command -v "wget")" ]; then | |
DOWNLOAD_METHOD="wget" | |
else | |
echo "This script requires curl or wget." | |
printf "\n" | |
echo "Process aborted" | |
exit 0 | |
fi | |
show_banner () { | |
printf "\n" | |
echo " ############################################################################" | |
echo " #### ####" | |
echo " #### ghacks user.js ####" | |
echo " #### Hardening the Privacy and Security Settings of Firefox ####" | |
echo " #### Maintained by @Thorin-Oakenpants and @earthlng ####" | |
echo " #### Updater for macOS and Linux by @overdodactyl ####" | |
echo " #### ####" | |
echo " ############################################################################" | |
printf "\n" | |
echo "Documentation for this script is available here: ${CYAN}https://github.com/ghacksuserjs/ghacks-user.js/wiki/3.3-Updater-Scripts" | |
printf "\n" | |
} | |
######################### | |
# Arguments # | |
######################### | |
usage() { | |
printf "\n" | |
echo "Usage: $0 [-h] [-p PROFILE] [-u] [-d] [-s] [-n] [-b] [-c] [-v] [-r] [-e] [-o OVERRIDE]" 1>&2 # Echo usage string to standard error | |
print "\n" | |
echo "Optional Arguments:" | |
printf "\t-h,\t\t Show this help message and exit." | |
printf "\t-p PROFILE,\t Path to your Firefox profile (if different than the dir of this script)" | |
printf "\t\t\t IMPORTANT: if the path include spaces, wrap the entire argument in quotes." | |
printf "\t-l, \t\t Choose your Firefox profile from a list" | |
printf "\t-u,\t\t Update updater.sh and execute silently. Do not seek confirmation." | |
printf "\t-d,\t\t Do not look for updates to updater.sh." | |
printf "\t-s,\t\t Silently update user.js. Do not seek confirmation." | |
printf "\t-b,\t\t Only keep one backup of each file." | |
printf "\t-c,\t\t Create a diff file comparing old and new user.js within userjs_diffs. " | |
printf "\t-o OVERRIDE,\t Filename or path to overrides file (if different than user-overrides.js)." | |
printf "\t\t\t If used with -p, paths should be relative to PROFILE or absolute paths" | |
printf "\t\t\t If given a directory, all files inside will be appended recursively." | |
printf "\t\t\t You can pass multiple files or directories by passing a comma separated list." | |
printf "\t\t\t\t Note: If a directory is given, only files inside ending in the extension .js are appended" | |
printf "\t\t\t\t IMPORTANT: do not add spaces between files/paths. Ex: -o file1.js,file2.js,dir1" | |
printf "\t\t\t\t IMPORTANT: if any files/paths include spaces, wrap the entire argument in quotes." | |
printf "\t\t\t\t\t Ex: -o \"override folder\" " | |
printf "\t-n,\t\t Do not append any overrides, even if user-overrides.js exists." | |
printf "\t-v,\t\t Open the resulting user.js file." | |
printf "\t-r,\t\t Only download user.js to a temporary file and open it." | |
printf "\t-e,\t\t Activate ESR related preferences." | |
printf "\n" | |
echo "Deprecated Arguments (they still work for now):" | |
printf "\t-donotupdate,\t Use instead -d" | |
printf "\t-update,\t Use instead -u" | |
printf "\n" | |
exit 1 | |
} | |
legacy_argument () { | |
printf "\nWarning: command line arguments have changed." | |
echo "$1 has been deprecated and may not work in the future." | |
printf "\n" | |
echo "Please view the new options using the -h argument." | |
} | |
######################### | |
# File Handling # | |
######################### | |
# Download files | |
download_file () { | |
URL="$1" | |
TF="$(mktemp)" | |
export URL | |
export TF | |
DLCMD="" | |
if [ $DOWNLOAD_METHOD = "curl" ]; then | |
DLCMD="curl -o $TF $URL" | |
else | |
DLCMD="wget -O $TF $URL" | |
fi | |
export DLCMD | |
$DLCMD > /dev/null 2>&1 && echo "$TF" || echo "" # return the temp-filename (or empty string on error) | |
} | |
open_file () { #expects one argument: file_path | |
KERNEL="$(uname -s)" | |
if [ "$(uname)" = "Darwin" ]; then | |
open "$1" | |
elif echo "$KERNEL" | grep -q -E -o "^Linux"; then | |
xdg-open "$1" | |
else | |
echo "Error: Sorry, opening files is not supported for your OS." | |
fi | |
} | |
readIniFile () { # expects one argument: absolute path of profiles.ini | |
inifile="$1" | |
tfile="$(mktemp)" | |
if [ "$(grep -c "^\[Profile" "$inifile")" = "1" ]; then ### only 1 profile found | |
grep "^\[Profile" -A 4 "$inifile" | grep -v "^\[Profile" > "$tfile" | |
else | |
grep -E -v "^\[General\]|^StartWithLastProfile=|^IsRelative=" "$inifile" | |
echo "" | |
echo "Select the profile number ( 0 for Profile0, 1 for Profile1, etc ) : " | |
read -r REPLY | |
echo "" | |
if echo "$REPLY" | grep -q -E "^(0|[1-9][0-9]*)$"; then | |
if grep "^\[Profile'${REPLY}" -A 4 "$inifile" | grep -v "^\[Profile'${REPLY}" > "$tfile"; then | |
echo "Profile${REPLY} does not exist!" && exit 1 | |
fi | |
else | |
echo "Invalid selection!" && exit 1 | |
fi | |
fi | |
profpath="$(grep "^Path=" "$tfile")" | |
pathisrel="$(grep "^IsRelative=" "$tfile")" | |
rm "$tfile" | |
# update global variable | |
if [ "${pathisrel#*=}" = "1" ]; then | |
PROFILE_PATH="$(dirname "$inifile")/${profpath#*=}" | |
else | |
PROFILE_PATH="${profpath#*=}" | |
fi | |
} | |
getProfilePath () { | |
f1="$HOME/Library/Application\ Support/Firefox/profiles.ini" | |
f2="$HOME/.mozilla/firefox/profiles.ini" | |
if [ "$PROFILE_PATH" = false ]; then | |
PROFILE_PATH="$SCRIPT_DIR" | |
elif [ "$PROFILE_PATH" = "list" ]; then | |
ini="" | |
if [ -f "$f1" ]; then | |
ini="$f1" | |
elif [ -f "$f2" ]; then | |
ini="$f2" | |
else | |
echo "Error: Sorry, -l is not supported for your OS" | |
exit 1 | |
fi | |
readIniFile "$ini" # updates PROFILE_PATH or exits on error | |
#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 () { | |
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: | |
# -donotupdate: New version will not be looked for and update will not occur | |
# -update: Check for update, if available, execute without asking | |
update_updater () { | |
if [ "$UPDATE" = "no" ]; then | |
return 0 # User signified not to check for updates | |
fi | |
tmpfile="$(download_file "https://raw.githubusercontent.com/ghacksuserjs/ghacks-user.js/master/updater.sh")" | |
if [ "$(get_updater_version "${SCRIPT_DIR}/updater.sh")" != "$(get_updater_version "${tmpfile}")" ]; then | |
if [ "$UPDATE" = "check" ]; then | |
echo "There is a different version of updater.sh available. Update and execute Y/N?" | |
read -r REPLY | |
printf "\n\n" | |
if echo "$REPLY" | grep -q -E "^[Nn]$"; then | |
return 0 # Update available, but user chooses not to update | |
fi | |
fi | |
else | |
return 0 # No update available | |
fi | |
mv "${tmpfile}" "${SCRIPT_DIR}/updater.sh" | |
chmod u+x "${SCRIPT_DIR}/updater.sh" | |
"${SCRIPT_DIR}/updater.sh" "$@" -d | |
exit 1 | |
} | |
######################### | |
# Update user.js # | |
######################### | |
# Returns version number of a user.js file | |
get_userjs_version () { | |
if [ -e "$1" ]; then | |
sed -n "4p" "$1" | |
else | |
echo "Not detected." | |
fi | |
} | |
add_override () { | |
input="$1" | |
if [ -f "$input" ]; then | |
printf "\n" >> user.js | |
cat "$input" >> user.js | |
echo "Status: Override file appended: ${input}" | |
elif [ -d "$input" ]; then | |
FSAVEIFS=$IFS | |
IFS="$(printf '\n\b')" # Set IFS | |
FILES="( ${input}/*.js )" | |
for f in $FILES | |
do | |
add_override "$f" | |
done | |
IFS=$FSAVEIFS # restore $IFS | |
else | |
echo "Warning: Could not find override file: ${input}" | |
fi | |
} | |
remove_comments () { # expects 2 arguments: from-file and to-file | |
sed -e 's/^[:space:]*\/\/.*$//' -e '/^\/\*/,/\*\//d' -e '/^[:space:]*$/d' -e 's/);[:space:]*\/\/.*/);/' "$1" > "$2" | |
} | |
# Applies latest version of user.js and any custom overrides | |
update_userjs () { | |
NEWFILE="$(download_file "https://raw.githubusercontent.com/ghacksuserjs/ghacks-user.js/master/user.js")" | |
export NEWFILE | |
echo "Please observe the following information:" | |
printf "\tFirefox profile: %s\n" "$(pwd)" | |
printf "\tAvailable online: %s\n" "$(get_userjs_version "$NEWFILE")" | |
printf "\tCurrently using: %s\n\n" "$(get_userjs_version user.js)" | |
if [ "$CONFIRM" = "yes" ]; then | |
echo "This script will update to the latest user.js file and append any custom configurations from user-overrides.js. Continue Y/N? " | |
read -r REPLY | |
printf "\n" | |
if echo "$REPLY" | grep -q -E "^[Nn]$"; then | |
echo "Process aborted" | |
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 2>&1 | |
fi | |
# backup user.js | |
mkdir -p userjs_backups | |
bakname="userjs_backups/user.js.backup.$(date +"%Y-%m-%d_%H%M")" | |
if [ "$BACKUP" = "single" ]; then | |
bakname="userjs_backups/user.js.backup" | |
fi | |
cp user.js "$bakname" > /dev/null 2>&1 | |
mv "$NEWFILE" user.js | |
echo "Status: user.js has been backed up and replaced with the latest version!" | |
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 "Status: ESR related preferences have been activated!" | |
fi | |
# apply overrides | |
if [ "$SKIPOVERRIDE" = false ]; then | |
for FILE in $(echo $OVERRIDE | tr "," "\n") | |
do | |
add_override "$FILE" | |
done | |
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 [ ! -z "$diff" ]; then | |
echo "$diff" > "$diffname" | |
echo "Status: A diff file was created: ${PWD}/${diffname}" | |
else | |
echo "Warning: Your new user.js file appears to be identical. No diff file was created." | |
if [ "$BACKUP" = "multiple" ]; then | |
rm "$bakname" > /dev/null 2>&1 | |
fi | |
fi | |
rm "$past_nocomments" "$current_nocomments" "$pastuserjs" > /dev/null 2>&1 | |
fi | |
if [ "$VIEW" = true ]; then open_file "${PWD}/user.js"; fi | |
} | |
######################### | |
# Execute # | |
######################### | |
if [ $# != 0 ]; then | |
readonly legacy_lc="$(echo "$1" | tr "[:upper:]" "[:lower:]")" | |
# Display usage if first argument is -help or --help | |
if [ "$1" = "--help" ] || [ "$1" = "-help" ]; then | |
usage | |
elif [ "$legacy_lc" = "-donotupdate" ]; then | |
UPDATE="no" | |
legacy_argument "$1" | |
elif [ "$legacy_lc" = "-update" ]; then | |
UPDATE="yes" | |
legacy_argument "$1" | |
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/ghacksuserjs/ghacks-user.js/master/user.js") | |
mv "$tfile" "${tfile}.js" | |
echo "Warning: user.js was saved to temporary file ${tfile}.js" | |
open_file "${tfile}.js" | |
exit 1 | |
;; | |
\?) | |
echo "Error! Invalid option: -$OPTARG" >&2 | |
usage | |
;; | |
:) | |
echo "Error! Option -$OPTARG requires an argument." >&2 | |
exit 1 | |
;; | |
esac | |
done | |
fi | |
fi | |
show_banner | |
update_updater "$@" | |
getProfilePath # updates PROFILE_PATH or exits on error | |
cd "$PROFILE_PATH" && update_userjs | |
cd "$CURRDIR" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment