#!/usr/bin/env bash ## ghacks-user.js updater for macOS and Linux ## version: 2.0 ## Author: Pat Johnson (@overdodactyl) ## Additional contributors: @earthlng, @ema-pe ## DON'T GO HIGHER THAN VERSION x.9 !! ( because of ASCII comparison in check_for_update() ) ######################### # 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 ######################### # Working directory # ######################### set_wd () { currdir=$(pwd) if [ "$PROFILE_PATH" = false ]; then sfp=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null) if [ -z "$sfp" ]; then sfp=${BASH_SOURCE[0]}; fi ff_profile="$(dirname "${sfp}")" elif [ "$PROFILE_PATH" = "list" ]; then if [ "$(uname)" == "Darwin" ]; then firefox_dir=~/Library/Application\ Support/Firefox/Profiles/ elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then firefox_dir=~/.mozilla/firefox/ else echo -e ${RED}"Error: Sorry, -l is not suppported for your OS"${NC} exit 1 fi echo -e ${GREEN}"The following profiles were found:\n"${ORANGE} ls -d "$firefox_dir"* echo -e ${RED}"\nWhich profile would you like to update?"${NC} read -p "" echo -e "" ff_profile=$REPLY else ff_profile="$PROFILE_PATH" fi cd "$ff_profile" } ######################### # Arguments # ######################### usage() { echo -e ${BLUE}"\nUsage: $0 [-h] [-p PROFILE] [-u] [-d] [-s] [-n] [-b] [-c] [-v] [-r] [-o OVERRIDE]\n"${NC} 1>&2 # Echo usage string to standard error echo -e "Optional Arguments:" echo -e "\t-h,\t\t Show this help message and exit." echo -e "\t-p PROFILE,\t Path to your Firefox profile (if different than the dir of this script)" echo -e "\t-l, \t\t Choose your Firefox profile from a list" echo -e "\t\t\t Note: This will not work with portable Firefox installations." echo -e "\t\t\t IMPORTANT: if the path include spaces, wrap the entire argument in quotes." echo -e "\t-u,\t\t Update updater.sh and execute silently. Do not seek confirmation." echo -e "\t-d,\t\t Do not look for updates to updater.sh." echo -e "\t-s,\t\t Silently update user.js. Do not seek confirmation." echo -e "\t-b,\t\t Only keep one backup of each file." echo -e "\t-c,\t\t Create a diff file comparing old and new user.js within userjs_diffs. " echo -e "\t-o OVERRIDE,\t Filename or path to overrides file (if different than user-overrides.js)." echo -e "\t\t\t If used with -p, paths should be relative to PROFILE or absolute paths" echo -e "\t\t\t If given a directory, all files inside will be appended recursively." echo -e "\t\t\t You can pass multiple files or directories by passing a comma separated list." echo -e "\t\t\t\t Note: If a directory is given, only files inside ending in the extension .js are appended" echo -e "\t\t\t\t IMPORTANT: do not add spaces between files/paths. Ex: -o file1.js,file2.js,dir1" echo -e "\t\t\t\t IMPORTANT: if any files/paths include spaces, wrap the entire argument in quotes." echo -e "\t\t\t\t\t Ex: -o \"override folder\" " echo -e "\t-n,\t\t Do not append any overrides, even if user-overrides.js exists." echo -e "\t-v,\t\t Open the resulting user.js file." echo -e "\t-r,\t\t Only download user.js to the userjs_temps dir and open the file." echo -e echo -e "Deprecated Arguments (they still work for now):" echo -e "\t-donotupdate,\t Use instead -d" echo -e "\t-update,\t Use instead -u" echo -e exit 1 } legacy_argument () { arg=$1 echo -e ${ORANGE}"\nWarning: command line arguments have changed." echo -e "${arg} has been deprecated and may not work in the future.\n" echo -e "Please view the new options using the -h argument."${NC} } ######################### # File Handeling # ######################### # Download method priority: curl -> wget -> perl DOWNLOAD_METHOD="not_pearl" if [[ $(command -v "curl") ]] > /dev/null 2>&1; then DOWNLOAD_TO_FILE="curl -O" elif [[ $(command -v "wget") ]] > /dev/null 2>&1; then DOWNLOAD_TO_FILE="wget" elif [[ $(command -v "perl") ]]; then DOWNLOAD_METHOD="perl" else echo -e ${RED}"This script requires curl, wget or perl to be installed.\nProcess aborted"${NC} exit 0 fi # Download files download_file () { mkdir -p userjs_temps cd userjs_temps url=$1 if [ $DOWNLOAD_METHOD = "not_pearl" ]; then $DOWNLOAD_TO_FILE ${url} else http_url=${url/https/http} # Variables from the shell are available in Perl's %ENV hash # Need to export shell variable so it is visible to subprocesses export http_url perl -e ' use File::Fetch; my $ff = File::Fetch->new(uri => $ENV{http_url}); my $where = $ff->fetch() or die $ff->error; my $where = $ff->fetch( to => "." ); ' fi cd .. } review_userjs () { download_file "https://raw.githubusercontent.com/ghacksuserjs/ghacks-user.js/master/user.js" &>/dev/null open userjs_temps/user.js echo -e ${ORANGE}"Warning: user.js was saved to a temporary file and will be removed the next time the updater is run."${NC} exit 1 } view_userjs () { if [ "$VIEW" = true ]; then open user.js fi } # Backup a file into userjs_backups # Replace current version of a file with new one in userjs_temps backup_file () { filename=$1 mkdir -p userjs_backups bakname="userjs_backups/${filename}.backup.$(date +"%Y-%m-%d_%H%M")" if [ $BACKUP = "single" ]; then bakname="userjs_backups/${filename}.backup" fi mv "$filename" "$bakname" mv "userjs_temps/${filename}" "$filename" echo -e "Status: ${GREEN}${filename} has been backed up and replaced with the latest version!${NC}" } ######################### # Initiation # ######################### initiate () { echo -e echo -e echo -e ${BBLUE}" ############################################################################" echo -e " #### ####" echo -e " #### ghacks user.js ####" echo -e " #### Hardening the Privacy and Security Settings of Firefox ####" echo -e " #### Maintained by @Thorin-Oakenpants and @earthlng ####" echo -e " #### Updater for macOS and Linux by @overdodactyl ####" echo -e " #### ####" echo -e " ############################################################################"${NC} echo -e echo -e echo -e "Documentation for this script is available here: ${CYAN}https://github.com/ghacksuserjs/ghacks-user.js/wiki/3.3-Updater-Scripts${NC}\n" } confirmation () { download_file "https://raw.githubusercontent.com/ghacksuserjs/ghacks-user.js/master/user.js" &>/dev/null echo -e "Please observe the following information:" echo -e "\tFirefox profile: ${ORANGE}$(pwd)${NC}" echo -e "\tAvailable online: ${ORANGE}$(get_userjs_version userjs_temps/user.js)${NC}" echo -e "\tCurrently using: ${ORANGE}$(get_userjs_version user.js)\n${NC}\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 =~ ^[Nn]$ ]]; then echo -e ${RED}"Process aborted"${NC} return 1 fi fi } ######################### # Update updater.sh # ######################### # Returns the version number of a updater.sh file get_updater_version () { filename=$1 version_regex='5 s/.*[[:blank:]]\([[:digit:]]*\.[[:digit:]]*\)/\1/p' echo "$(sed -n "$version_regex" "$filename")" #echo "$(sed -n "$version_regex" "${ff_profile}/${filename}")" } # 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 # User signified not to check for updates return 0 fi download_file "https://raw.githubusercontent.com/ghacksuserjs/ghacks-user.js/master/updater.sh" &>/dev/null if ! [ "$PROFILE_PATH" = false ]; then rm -rf "${currdir}/userjs_temps" mv "$ff_profile/userjs_temps/" "$currdir" fi if [[ $(get_updater_version "${currdir}/updater.sh") < $(get_updater_version "${currdir}/userjs_temps/updater.sh") ]]; then if [ $UPDATE = "check" ]; then echo -e "There is a newer version of updater.sh available. ${RED}Download and execute Y/N?${NC}" read -p "" -n 1 -r echo -e "\n\n" if [[ $REPLY =~ ^[Nn]$ ]]; then # Update available, but user chooses not to update return 0 fi fi else # No update available return 0 fi mv "${currdir}/userjs_temps/updater.sh" "${currdir}/updater.sh" chmod +x "${currdir}/updater.sh" "${currdir}/updater.sh" "$@ -d" exit 1 } ######################### # Update user.js # ######################### # Returns version number of a user.js file get_userjs_version () { filename=$1 echo "$(sed -n "4p" "${ff_profile}/${filename}")" } 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 FSAVEIFS=$IFS IFS=$'\n\b' # Set IFS # 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 } # Applies latest version of user.js and any custom overrides update_userjs () { # 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 fi backup_file user.js if [ "$SKIPOVERRIDE" = true ]; then return 0 fi while IFS=',' read -ra FILE; do add_override "$FILE" done <<< "$OVERRIDE" } remove_comments () { from_file=$1 to_file=$2 sed -e 's/^[[:space:]]*\/\/.*$//' -e '/^\/\*/,/\*\//d' -e '/^[[:space:]]*$/d' -e 's/);[[:space:]]\/\/.*/);/' $from_file > $to_file } 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 -w -B -U 0 $past_nocomments $current_nocomments > $diffname rm $past_nocomments $current_nocomments $pastuserjs fi } if [ $# != 0 ]; then legacy_lc="$(echo $1 | tr '[A-Z]' '[a-z]')" # Display usage if first arguement 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:bcvr" 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 ;; r) review_userjs ;; \?) echo -e ${RED}"\n Error! Invalid option: -$OPTARG"${NC} >&2 usage ;; :) echo -e ${RED}"Error! Option -$OPTARG requires an argument."${NC} >&2 exit 1 ;; esac done fi fi ######################### # Execute # ######################### ## change directory to the Firefox profile directory initiate set_wd update_updater confirmation && update_userjs create_diff view_userjs rm -rf userjs_temps cd "${currdir}" rm -rf userjs_temps