user.js

mirror of https://github.com/arkenfox/user.js
Log | Files | Refs | README

updater.sh (13923B)


      1 #!/usr/bin/env bash
      2 
      3 ## arkenfox user.js updater for macOS and Linux
      4 
      5 ## version: 4.0
      6 ## Author: Pat Johnson (@overdodactyl)
      7 ## Additional contributors: @earthlng, @ema-pe, @claustromaniac, @infinitewarp
      8 
      9 ## DON'T GO HIGHER THAN VERSION x.9 !! ( because of ASCII comparison in update_updater() )
     10 
     11 # Check if running as root
     12 if [ "${EUID:-"$(id -u)"}" -eq 0 ]; then
     13 	printf "You shouldn't run this with elevated privileges (such as with doas/sudo).\n"
     14 	exit 1
     15 fi
     16 
     17 readonly CURRDIR=$(pwd)
     18 
     19 SCRIPT_FILE=$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null)
     20 [ -z "$SCRIPT_FILE" ] && SCRIPT_FILE=${BASH_SOURCE[0]}
     21 readonly SCRIPT_DIR=$(dirname "${SCRIPT_FILE}")
     22 
     23 
     24 #########################
     25 #    Base variables     #
     26 #########################
     27 
     28 # Colors used for printing
     29 RED='\033[0;31m'
     30 BLUE='\033[0;34m'
     31 BBLUE='\033[1;34m'
     32 GREEN='\033[0;32m'
     33 ORANGE='\033[0;33m'
     34 CYAN='\033[0;36m'
     35 NC='\033[0m' # No Color
     36 
     37 # Argument defaults
     38 UPDATE='check'
     39 CONFIRM='yes'
     40 OVERRIDE='user-overrides.js'
     41 BACKUP='multiple'
     42 COMPARE=false
     43 SKIPOVERRIDE=false
     44 VIEW=false
     45 PROFILE_PATH=false
     46 ESR=false
     47 
     48 # Download method priority: curl -> wget
     49 DOWNLOAD_METHOD=''
     50 if command -v curl >/dev/null; then
     51   DOWNLOAD_METHOD='curl --max-redirs 3 -so'
     52 elif command -v wget >/dev/null; then
     53   DOWNLOAD_METHOD='wget --max-redirect 3 --quiet -O'
     54 else
     55   echo -e "${RED}This script requires curl or wget.\nProcess aborted${NC}"
     56   exit 1
     57 fi
     58 
     59 
     60 show_banner() {
     61   echo -e "${BBLUE}
     62                 ############################################################################
     63                 ####                                                                    ####
     64                 ####                          arkenfox user.js                          ####
     65                 ####       Hardening the Privacy and Security Settings of Firefox       ####
     66                 ####           Maintained by @Thorin-Oakenpants and @earthlng           ####
     67                 ####            Updater for macOS and Linux by @overdodactyl            ####
     68                 ####                                                                    ####
     69                 ############################################################################"
     70   echo -e "${NC}\n"
     71   echo -e "Documentation for this script is available here: ${CYAN}https://github.com/arkenfox/user.js/wiki/5.1-Updater-[Options]#-maclinux${NC}\n"
     72 }
     73 
     74 #########################
     75 #      Arguments        #
     76 #########################
     77 
     78 usage() {
     79   echo
     80   echo -e "${BLUE}Usage: $0 [-bcdehlnrsuv] [-p PROFILE] [-o OVERRIDE]${NC}" 1>&2  # Echo usage string to standard error
     81   echo -e "
     82 Optional Arguments:
     83     -h           Show this help message and exit.
     84     -p PROFILE   Path to your Firefox profile (if different than the dir of this script)
     85                  IMPORTANT: If the path contains spaces, wrap the entire argument in quotes.
     86     -l           Choose your Firefox profile from a list
     87     -u           Update updater.sh and execute silently.  Do not seek confirmation.
     88     -d           Do not look for updates to updater.sh.
     89     -s           Silently update user.js.  Do not seek confirmation.
     90     -b           Only keep one backup of each file.
     91     -c           Create a diff file comparing old and new user.js within userjs_diffs.
     92     -o OVERRIDE  Filename or path to overrides file (if different than user-overrides.js).
     93                  If used with -p, paths should be relative to PROFILE or absolute paths
     94                  If given a directory, all files inside will be appended recursively.
     95                  You can pass multiple files or directories by passing a comma separated list.
     96                      Note: If a directory is given, only files inside ending in the extension .js are appended
     97                      IMPORTANT: Do not add spaces between files/paths.  Ex: -o file1.js,file2.js,dir1
     98                      IMPORTANT: If any file/path contains spaces, wrap the entire argument in quotes.
     99                          Ex: -o \"override folder\"
    100     -n           Do not append any overrides, even if user-overrides.js exists.
    101     -v           Open the resulting user.js file.
    102     -r           Only download user.js to a temporary file and open it.
    103     -e           Activate ESR related preferences."
    104   echo
    105   exit 1
    106 }
    107 
    108 #########################
    109 #     File Handling     #
    110 #########################
    111 
    112 download_file() { # expects URL as argument ($1)
    113   declare -r tf=$(mktemp)
    114 
    115   $DOWNLOAD_METHOD "${tf}" "$1" &>/dev/null && echo "$tf" || echo '' # return the temp-filename or empty string on error
    116 }
    117 
    118 open_file() { # expects one argument: file_path
    119   if [ "$(uname)" == 'Darwin' ]; then
    120     open "$1"
    121   elif [ "$(uname -s | cut -c -5)" == "Linux" ]; then
    122     xdg-open "$1"
    123   else
    124     echo -e "${RED}Error: Sorry, opening files is not supported for your OS.${NC}"
    125   fi
    126 }
    127 
    128 readIniFile() { # expects one argument: absolute path of profiles.ini
    129   declare -r inifile="$1"
    130 
    131   # tempIni will contain: [ProfileX], Name=, IsRelative= and Path= (and Default= if present) of the only (if) or the selected (else) profile
    132   if [ "$(grep -c '^\[Profile' "${inifile}")" -eq "1" ]; then ### only 1 profile found
    133     tempIni="$(grep '^\[Profile' -A 4 "${inifile}")"
    134   else
    135     echo -e "Profiles found:\n––––––––––––––––––––––––––––––"
    136     ## cmd-substitution to strip trailing newlines and in quotes to keep internal ones:
    137     echo "$(grep --color=never -E 'Default=[^1]|\[Profile[0-9]*\]|Name=|Path=|^$' "${inifile}")"
    138     echo '––––––––––––––––––––––––––––––'
    139     read -p 'Select the profile number ( 0 for Profile0, 1 for Profile1, etc ) : ' -r
    140     echo -e "\n"
    141     if [[ $REPLY =~ ^(0|[1-9][0-9]*)$ ]]; then
    142       tempIni="$(grep "^\[Profile${REPLY}" -A 4 "${inifile}")" || {
    143         echo -e "${RED}Profile${REPLY} does not exist!${NC}" && exit 1
    144       }
    145     else
    146       echo -e "${RED}Invalid selection!${NC}" && exit 1
    147     fi
    148   fi
    149 
    150   # extracting 0 or 1 from the "IsRelative=" line
    151   declare -r pathisrel=$(sed -n 's/^IsRelative=\([01]\)$/\1/p' <<< "${tempIni}")
    152 
    153   # extracting only the path itself, excluding "Path="
    154   PROFILE_PATH=$(sed -n 's/^Path=\(.*\)$/\1/p' <<< "${tempIni}")
    155   # update global variable if path is relative
    156   [[ ${pathisrel} == "1" ]] && PROFILE_PATH="$(dirname "${inifile}")/${PROFILE_PATH}"
    157 }
    158 
    159 getProfilePath() {
    160   declare -r f1=~/Library/Application\ Support/Firefox/profiles.ini
    161   declare -r f2=~/.mozilla/firefox/profiles.ini
    162 
    163   if [ "$PROFILE_PATH" = false ]; then
    164     PROFILE_PATH="$SCRIPT_DIR"
    165   elif [ "$PROFILE_PATH" = 'list' ]; then
    166     if [[ -f "$f1" ]]; then
    167       readIniFile "$f1" # updates PROFILE_PATH or exits on error
    168     elif [[ -f "$f2" ]]; then
    169       readIniFile "$f2"
    170     else
    171       echo -e "${RED}Error: Sorry, -l is not supported for your OS${NC}"
    172       exit 1
    173     fi
    174   #else
    175     # PROFILE_PATH already set by user with -p
    176   fi
    177 }
    178 
    179 #########################
    180 #   Update updater.sh   #
    181 #########################
    182 
    183 # Returns the version number of a updater.sh file
    184 get_updater_version() {
    185   echo "$(sed -n '5 s/.*[[:blank:]]\([[:digit:]]*\.[[:digit:]]*\)/\1/p' "$1")"
    186 }
    187 
    188 # Update updater.sh
    189 # Default: Check for update, if available, ask user if they want to execute it
    190 # Args:
    191 #   -d: New version will not be looked for and update will not occur
    192 #   -u: Check for update, if available, execute without asking
    193 update_updater() {
    194   [ "$UPDATE" = 'no' ] && return 0 # User signified not to check for updates
    195 
    196   declare -r tmpfile="$(download_file 'https://raw.githubusercontent.com/arkenfox/user.js/master/updater.sh')"
    197   [ -z "${tmpfile}" ] && echo -e "${RED}Error! Could not download updater.sh${NC}" && return 1 # check if download failed
    198 
    199   if [[ $(get_updater_version "$SCRIPT_FILE") < $(get_updater_version "${tmpfile}") ]]; then
    200     if [ "$UPDATE" = 'check' ]; then
    201       echo -e "There is a newer version of updater.sh available. ${RED}Update and execute Y/N?${NC}"
    202       read -p "" -n 1 -r
    203       echo -e "\n\n"
    204       [[ $REPLY =~ ^[Yy]$ ]] || return 0   # Update available, but user chooses not to update
    205     fi
    206   else
    207     return 0   # No update available
    208   fi
    209   mv "${tmpfile}" "$SCRIPT_FILE"
    210   chmod u+x "$SCRIPT_FILE"
    211   "$SCRIPT_FILE" "$@" -d
    212   exit 0
    213 }
    214 
    215 #########################
    216 #    Update user.js     #
    217 #########################
    218 
    219 # Returns version number of a user.js file
    220 get_userjs_version() {
    221   [ -e "$1" ] && echo "$(sed -n '4p' "$1")" || echo "Not detected."
    222 }
    223 
    224 add_override() {
    225   input=$1
    226   if [ -f "$input" ]; then
    227     echo "" >> user.js
    228     cat "$input" >> user.js
    229     echo -e "Status: ${GREEN}Override file appended:${NC} ${input}"
    230   elif [ -d "$input" ]; then
    231     SAVEIFS=$IFS
    232     IFS=$'\n\b' # Set IFS
    233     FILES="${input}"/*.js
    234     for f in $FILES
    235     do
    236       add_override "$f"
    237     done
    238     IFS=$SAVEIFS # restore $IFS
    239   else
    240     echo -e "${ORANGE}Warning: Could not find override file:${NC} ${input}"
    241   fi
    242 }
    243 
    244 remove_comments() { # expects 2 arguments: from-file and to-file
    245   sed -e '/^\/\*.*\*\/[[:space:]]*$/d' -e '/^\/\*/,/\*\//d' -e 's|^[[:space:]]*//.*$||' -e '/^[[:space:]]*$/d' -e 's|);[[:space:]]*//.*|);|' "$1" > "$2"
    246 }
    247 
    248 # Applies latest version of user.js and any custom overrides
    249 update_userjs() {
    250   declare -r newfile="$(download_file 'https://raw.githubusercontent.com/arkenfox/user.js/master/user.js')"
    251   [ -z "${newfile}" ] && echo -e "${RED}Error! Could not download user.js${NC}" && return 1 # check if download failed
    252 
    253   echo -e "Please observe the following information:
    254     Firefox profile:  ${ORANGE}$(pwd)${NC}
    255     Available online: ${ORANGE}$(get_userjs_version "$newfile")${NC}
    256     Currently using:  ${ORANGE}$(get_userjs_version user.js)${NC}\n\n"
    257 
    258   if [ "$CONFIRM" = 'yes' ]; then
    259     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}"
    260     read -p "" -n 1 -r
    261     echo -e "\n"
    262     if ! [[ $REPLY =~ ^[Yy]$ ]]; then
    263       echo -e "${RED}Process aborted${NC}"
    264       rm "$newfile"
    265       return 1
    266     fi
    267   fi
    268 
    269   # Copy a version of user.js to diffs folder for later comparison
    270   if [ "$COMPARE" = true ]; then
    271     mkdir -p userjs_diffs
    272     cp user.js userjs_diffs/past_user.js &>/dev/null
    273   fi
    274 
    275   # backup user.js
    276   mkdir -p userjs_backups
    277   local bakname="userjs_backups/user.js.backup.$(date +"%Y-%m-%d_%H%M")"
    278   [ "$BACKUP" = 'single' ] && bakname='userjs_backups/user.js.backup'
    279   cp user.js "$bakname" &>/dev/null
    280 
    281   mv "${newfile}" user.js
    282   echo -e "Status: ${GREEN}user.js has been backed up and replaced with the latest version!${NC}"
    283 
    284   if [ "$ESR" = true ]; then
    285     sed -e 's/\/\* \(ESR[0-9]\{2,\}\.x still uses all.*\)/\/\/ \1/' user.js > user.js.tmp && mv user.js.tmp user.js
    286     echo -e "Status: ${GREEN}ESR related preferences have been activated!${NC}"
    287   fi
    288 
    289   # apply overrides
    290   if [ "$SKIPOVERRIDE" = false ]; then
    291     while IFS=',' read -ra FILES; do
    292       for FILE in "${FILES[@]}"; do
    293         add_override "$FILE"
    294       done
    295     done <<< "$OVERRIDE"
    296   fi
    297 
    298   # create diff
    299   if [ "$COMPARE" = true ]; then
    300     pastuserjs='userjs_diffs/past_user.js'
    301     past_nocomments='userjs_diffs/past_userjs.txt'
    302     current_nocomments='userjs_diffs/current_userjs.txt'
    303 
    304     remove_comments "$pastuserjs" "$past_nocomments"
    305     remove_comments user.js "$current_nocomments"
    306 
    307     diffname="userjs_diffs/diff_$(date +"%Y-%m-%d_%H%M").txt"
    308     diff=$(diff -w -B -U 0 "$past_nocomments" "$current_nocomments")
    309     if [ -n "$diff" ]; then
    310       echo "$diff" > "$diffname"
    311       echo -e "Status: ${GREEN}A diff file was created:${NC} ${PWD}/${diffname}"
    312     else
    313       echo -e "Warning: ${ORANGE}Your new user.js file appears to be identical.  No diff file was created.${NC}"
    314       [ "$BACKUP" = 'multiple' ] && rm "$bakname" &>/dev/null
    315     fi
    316     rm "$past_nocomments" "$current_nocomments" "$pastuserjs" &>/dev/null
    317   fi
    318 
    319   [ "$VIEW" = true ] && open_file "${PWD}/user.js"
    320 }
    321 
    322 #########################
    323 #        Execute        #
    324 #########################
    325 
    326 if [ $# != 0 ]; then
    327   # Display usage if first argument is -help or --help
    328   if [ "$1" = '--help' ] || [ "$1" = '-help' ]; then
    329     usage
    330   else
    331     while getopts ":hp:ludsno:bcvre" opt; do
    332       case $opt in
    333         h)
    334           usage
    335           ;;
    336         p)
    337           PROFILE_PATH=${OPTARG}
    338           ;;
    339         l)
    340           PROFILE_PATH='list'
    341           ;;
    342         u)
    343           UPDATE='yes'
    344           ;;
    345         d)
    346           UPDATE='no'
    347           ;;
    348         s)
    349           CONFIRM='no'
    350           ;;
    351         n)
    352           SKIPOVERRIDE=true
    353           ;;
    354         o)
    355           OVERRIDE=${OPTARG}
    356           ;;
    357         b)
    358           BACKUP='single'
    359           ;;
    360         c)
    361           COMPARE=true
    362           ;;
    363         v)
    364           VIEW=true
    365           ;;
    366         e)
    367           ESR=true
    368           ;;
    369         r)
    370           tfile="$(download_file 'https://raw.githubusercontent.com/arkenfox/user.js/master/user.js')"
    371           [ -z "${tfile}" ] && echo -e "${RED}Error! Could not download user.js${NC}" && exit 1 # check if download failed
    372           mv "$tfile" "${tfile}.js"
    373           echo -e "${ORANGE}Warning: user.js was saved to temporary file ${tfile}.js${NC}"
    374           open_file "${tfile}.js"
    375           exit 0
    376           ;;
    377         \?)
    378           echo -e "${RED}\n Error! Invalid option: -$OPTARG${NC}" >&2
    379           usage
    380           ;;
    381         :)
    382           echo -e "${RED}Error! Option -$OPTARG requires an argument.${NC}" >&2
    383           exit 2
    384           ;;
    385       esac
    386     done
    387   fi
    388 fi
    389 
    390 show_banner
    391 update_updater "$@"
    392 
    393 getProfilePath # updates PROFILE_PATH or exits on error
    394 cd "$PROFILE_PATH" || exit 1
    395 
    396 # Check if any files have the owner as root/wheel.
    397 if [ -n "$(find ./ -user 0)" ]; then
    398 	printf 'It looks like this script was previously run with elevated privileges,
    399 you will need to change ownership of the following files to your user:\n'
    400 	find . -user 0
    401 	cd "$CURRDIR"
    402 	exit 1
    403 fi
    404 
    405 update_userjs
    406 
    407 cd "$CURRDIR"