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"