123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- #!/bin/sh
- ## Updater für die user.js von Privacy-Handbuch.de
- ## Version: 1.4.1
- ## by: TotallyLeGIT
- ## based on the ghacks user.js updater by:
- ## Author: Pat Johnson (@overdodactyl)
- ## Additional contributors: @earthlng, @ema-pe, @claustromaniac
- ## Dependencies: ·curl or wget
- #########################
- # Misc/Helper Functions #
- #########################
- # sets variables based on the operating system reported by uname
- abstract_os() {
- if [ "$(uname)" = 'Darwin' ]; then
- readonly open_command='open'
- readonly ff_file_path="${HOME}/Library/Application Support/Firefox/profiles.ini"
- readonly tb_file_path="${HOME}/Library/Thunderbird/profiles.ini"
- readonly ff_command='/Applications/Firefox.app/Contents/MacOS/firefox --version 2>/dev/null'
- else
- readonly open_command='xdg-open'
- readonly ff_file_path="${HOME}/.mozilla/firefox/profiles.ini"
- readonly tb_file_path="${HOME}/.thunderbird/profiles.ini"
- readonly ff_command='firefox --version 2>/dev/null'
- fi
- # set effective path for each profile.ini
- [ -f "${ff_file_path}" ] && ff_file="${ff_file_path}" || ff_file=''
- [ -f "${tb_file_path}" ] && tb_file="${tb_file_path}" || tb_file=''
- readonly ff_file tb_file
- }
- # parses command line options and sets variables accordingly
- parse_options() {
- if [ "${#}" -ne 0 ]; then
- # display usage if first argument contains -help
- if printf '%s' "${1}" | grep -qi '\-help'; then
- usage
- exit 0
- else
- while getopts ':abc:de:hilno:p:rsuvy' opt; do
- case "${opt}" in
- a)
- profile_path_option='all'
- ;;
- b)
- backup_multiple='false'
- ;;
- c)
- if ! config_wanted="$(printf '%s' "${OPTARG}" \
- | tr '[:upper:]' '[:lower:]' \
- | awk '{print $1; exit}' \
- | grep '^minimal$\|^moderat$\|^streng$\|^hotspot$\|^tb$')"
- then
- printf '%s' "${red}Error: Invalid CONFIG.${neutral} Valid values"
- printf '%s\n' " are: minimal, moderat, streng, hotspot or tb"
- exit 1
- fi
- ;;
- d)
- compare='true'
- ;;
- e)
- if ff_version_wanted="$(printf '%s' "${OPTARG}" \
- | awk '{print $1; exit}' \
- | grep '^[[:digit:]]*$')"
- then
- ff_version_specified='true'
- else
- printf '%s\n' "${red}Error: VERSION must be an integer.${neutral}"
- exit 1
- fi
- ;;
- h)
- usage
- exit 0
- ;;
- i)
- view='true'
- ;;
- l)
- profile_path_option='list'
- ;;
- n)
- override='false'
- ;;
- o)
- override='true'
- override_path="${OPTARG}"
- ;;
- p)
- update_profile_path "${OPTARG}"
- ;;
- r)
- just_read='true'
- ;;
- # TODO: remove '-s' in future version
- s|u)
- update='true'
- ;;
- v)
- printf '%s\n' "PH-userjs-updater ${version}"
- exit 0
- ;;
- y)
- confirm='no'
- ;;
- \?)
- printf '%s' "${red}Error! Invalid option:" 1>&2
- printf '%s\n' " -${OPTARG}${neutral}" 1>&2
- usage
- exit 2
- ;;
- :)
- printf '%s' "${red}Error! Option -${OPTARG}" 1>&2
- printf '%s\n' " requires an argument.${neutral}" 1>&2
- usage
- exit 2
- ;;
- esac
- done
- fi
- fi
- }
- # initialize misc stuff
- # expects one argument: this script's name (usually $0)
- prepare() {
- tmp_dir="$(mktemp -d)"
- readonly tmp_dir
- trap 'exit 130' INT HUP QUIT TERM ALRM
- # shellcheck disable=SC2154
- trap 'rc="${?}"; rm -rf "${tmp_dir}"; exit "${rc}"' EXIT
- script_path="$(readlink -f "${1}" 2>/dev/null \
- || greadlink -f "${1}" 2>/dev/null || printf '%s' "${1}")"
- readonly script_path
- # Download method priority: curl > wget
- if command -v curl >/dev/null; then
- download_command='curl --max-redirs 2 --max-time 30 --fail --silent --output'
- elif command -v wget >/dev/null; then
- # -O file = --output-document=file
- download_command='wget --max-redirect=2 --timeout=30 --quiet -O'
- else
- printf '%s\n' \
- "${red}This script requires curl or wget. Process aborted.${neutral}"
- exit 2
- fi
- }
- # determines firefox update channel
- # returns 0 for release or the major version number for esr
- set_firefox_version() {
- if [ "${ff_version_specified}" = 'false' ]; then
- if ff_version_wanted="$(eval "${ff_command}")"; then
- if printf '%s' "${ff_version_wanted}" | grep -q 'esr'; then
- ff_version_wanted="$(printf '%s' "${ff_version_wanted}" \
- | cut -d ' ' -f 3 | cut -d '.' -f 1)"
- else
- ff_version_wanted='0'
- fi
- else
- printf '%s' "${yellow}Warning: No firefox executable in PATH."
- printf '%s\n' " Assuming updapte channel 'release'.${neutral}"
- ff_version_wanted='0'
- fi
- fi
- [ "${ff_version_wanted}" -eq 0 ] && esr_string='release' \
- || esr_string="ESR ${ff_version_wanted}"
- printf '%s\n' "Firefox variant: ${green}${esr_string}${neutral}"
- }
- # initialize variables needed for parse_options()
- set_variables() {
- # Colors used for printing only when stdout is a terminal
- if [ -t 1 ]; then
- red="$(tput setaf 1)$(tput bold)"
- blue="$(tput setaf 4)"
- green="$(tput setaf 2)"
- yellow="$(tput setaf 3)"
- # No Color
- neutral="$(tput sgr0)"
- else
- red=''
- blue=''
- green=''
- yellow=''
- neutral=''
- fi
- # Argument defaults
- backup_multiple='true'
- compare='false'
- config_wanted='keep'
- confirm='yes'
- ff_version_specified='false'
- just_read='false'
- # soft: look for user-overrides.js but only give info when none found
- override='soft'
- override_path='user-overrides.js'
- profile_path_option='false'
- pwd_start="$(pwd)"
- update='false'
- version='1.4.1'
- view='false'
- # constants
- readonly backup_dir='userjs_backups'
- readonly diff_dir='userjs_diffs'
- }
- show_banner() {
- printf '%s%s\n\n' "${blue}" \
- "┌───────────────────────────────────────────────┐
- │ Updater for Privacy-Handbuch's user.js │
- │ suitable for both Firefox and Thunderbird │
- └───────────────────────────────────────────────┘${neutral}
- Updater: ${blue}https://notabug.org/TotallyLeGIT/PH-userjs-updater${neutral}
- Firefox user.js: ${blue}https://www.privacy-handbuch.de/handbuch_21u.htm${neutral}
- Thunderbird user.js: ${blue}https://www.privacy-handbuch.de/handbuch_31p.htm${neutral}"
- }
- usage() {
- show_banner
- printf '%s' "${blue}Usage: ${0} [-abdhilnrsuvy] [-c CONFIG]"
- printf '%s\n\n' " [-e VERSION] [-o OVERRIDE] [-p PROFILE]${neutral}
- -a Update all Firefox and Thunderbird profiles at once while keeping
- CONFIGs the same. Profiles without a user.js will be skipped.
- -b Only keep one user.js backup and one diff file.
- -c CONFIG Specify the Firefox user.js config you want: minimal, moderat, streng or hotspot
- or for Thunderbird: tb .
- This is only needed if you want to change to another config.
- Note that this option will always have effect on all selected profiles.
- -d Create a diff file comparing old and new user.js within ${diff_dir} subdirectory.
- -e VERSION Force download of user.js for Firefox ESR with version number VERSION.
- Make sure the user.js for this version actually exists.
- Example: '-e 91' will download the user.js for Firefox ESR Version 91.x .
- Use '-e 0' to force the download of the user.js for the release update channel.
- Precedence to find Firefox update channel:
- '-e' option > read from user.js > 'firefox --version' > release (fallback)
- -h Show this help message and exit.
- -i Inspect the resulting user.js file.
- -l Interactively choose your profile from a list.
- -n Do not append any overrides even if user-overrides.js exists.
- -o OVERRIDE Filename or path to overrides file, needed if different than PROFILE/user-overrides.js .
- If used with -p, the path may be relative to PROFILE or an absolute path.
- If given a directory, all files inside ending in .js will be appended.
- -p PROFILE Path to your profile directory, needed if different than this script's location.
- May be used multiple times.
- IMPORTANT: If the profile path includes spaces, wrap whole path in quotes.
- -r Only download user.js to a temporary file and open it. This requires -c.
- -s (Deprecated.) This is an alias for '-u' until it is removed.
- -u Check for updates of this updater and install if available.
- -v Display updater version and exit.
- -y Update user.js without confirmation."
- }
- #########################
- # (Pro)File Handling #
- #########################
- # downloads a resource. expects two arguments:
- # $1 the file name, where the resource will be saved to
- # $2 the resource to download (a link)
- download_file() {
- [ -f "${1}" ] && return 0
- if ! eval "${download_command} '${1}' '${2}'"; then
- printf '%s\n' "${red}Download of resource failed:${neutral} ${2}"
- return 33
- fi
- }
- # updates profile_path (especially with list/-l) or exits on error
- get_profile_path() {
- if [ "${profile_path_option}" = 'list' ]; then
- if [ -n "${ff_file}" ]; then
- ini="${ff_file}"
- [ -n "${tb_file}" ] && ini='both' || printf 'Firefox detected.\n\n'
- elif [ -n "${tb_file}" ]; then
- ini="${tb_file}"
- printf 'Thunderbird detected.\n\n'
- else
- printf '%s' "${red}Error: Sorry, Thunderbird and Firefox could"
- printf '%s' " not be found or -l is not supported for your OS."
- printf '%s\n' " Try -p option.${neutral}"
- exit 1
- fi
- if [ "${ini}" = 'both' ]; then
- while [ -z "${reply}" ]; do
- printf '(F)irefox and (T)hunderbird were detected. Select one: f,t? '
- read -r reply
- done
- reply="$(printf '%s' "${reply}" | tr '[:upper:]' '[:lower:]')"
- if [ -z "${reply##f*}" ]; then
- ini="${ff_file}"
- printf 'Firefox was selected.\n\n'
- elif [ -z "${reply##t*}" ]; then
- ini="${tb_file}"
- printf 'Thunderbird was selected.\n\n'
- else
- printf '%s' "${red}Error: Could not parse your input.${neutral}"
- exit 1
- fi
- fi
- read_ini_file "${ini}"
- elif [ "${profile_path_option}" = 'all' ]; then
- for file in "${ff_file}" "${tb_file}"; do
- [ -f "${file}" ] \
- && profile_path_temp="$(sed -ne 's/^Path=\(.*\)$/\1/p' "${file}")"
- while read -r line; do
- if ! printf '%s' "${line}" | grep -q '^/'; then
- profile_path_to_update="${file%profiles.ini}${line}"
- else
- profile_path_to_update="${line}"
- fi
- update_profile_path "${profile_path_to_update}"
- # we don't want a subshell here
- done <<EOF
- ${profile_path_temp}
- EOF
- done
- elif [ "${profile_path_option}" = 'false' ]; then
- profile_path="$(dirname "${script_path}")"
- fi
- }
- # opens a file for the user to view
- # expects one argument: file_path
- open_file() {
- "${open_command}" "${1}" || printf '%s %s\n' "${red}Error: Sorry," \
- "opening files is not supported for your OS.${neutral}"
- }
- # parses profiles.ini to find the correct profile
- # expects one argument: absolute path of profiles.ini
- read_ini_file() {
- readonly ini_file="${1}"
- # only 1 profile found
- if [ "$(grep -c '^\[Profile' "${ini_file}")" -eq 1 ]; then
- ini_content="$(grep '^\[Profile' -A 4 "${ini_file}")"
- # more than 1 profile
- else
- printf 'Profiles found:\n'
- printf '–––––––––––––––––––––––––––––––––––\n'
- grep -E 'Default=[^1]|\[Profile[0-9]*\]|Name=|Path=|^$' "${ini_file}"
- printf '–––––––––––––––––––––––––––––––––––\n'
- printf '%s' \
- "Select the profile number ( Profile(0), Profile(1) etc. ): 1,2,…? "
- read -r reply
- printf '\n'
- ini_content="$(grep '^\[Profile'"${reply}" -A 4 "${ini_file}")" \
- || { printf '%s\n' "${red}Profile${reply} does not exist!${neutral}"
- exit 1 ; }
- fi
- profile_path_to_add="$(printf '%s' "${ini_content}" \
- | sed -n 's/^Path=\(.*\)$/\1/p')"
- path_is_rel="$(printf '%s' "${ini_content}" \
- | sed -n 's/^IsRelative=\([01]\)$/\1/p')"
- readonly path_is_rel
- # update global variable if path is relative
- [ "${path_is_rel}" -eq 1 ] \
- && profile_path_to_add="$(dirname "${ini_file}")/${profile_path_to_add}"
- update_profile_path "${profile_path_to_add}"
- }
- # adds a path to the list of profile paths, separated by newlines
- # expects one argument: a profile path
- update_profile_path() {
- # if profile path empty: update it
- if [ -z "${profile_path}" ]; then
- profile_path="${1}"
- # update profile_path_option var so we know there's a profile path
- [ "${profile_path_option}" = 'false' ] && profile_path_option='true'
- # append additional profile path
- else
- profile_path="$(printf '%s\n%s' "${profile_path}" "${1}")"
- fi
- }
- #########################
- # Update Updater #
- #########################
- # update this updater
- # expects $@ as arguments
- update_updater() {
- # update check has not been activated
- [ "${update}" = 'false' ] && return 0
- readonly new_updater="${tmp_dir}/new_updater"
- download_file "${new_updater}" \
- 'https://notabug.org/TotallyLeGIT/PH-userjs-updater/raw/main/updater' \
- || return 1
- chmod u+x "${new_updater}"
- current_updater_version="$("${script_path}" -v | cut -d ' ' -f 2)"
- new_updater_version="$("${new_updater}" -v | cut -d ' ' -f 2)"
- readonly current_updater_version_const="${current_updater_version}"
- # compare updater versions, if different
- while [ "${current_updater_version}" != "${new_updater_version}" ]; do
- difference="$((${new_updater_version%%.*}-${current_updater_version%%.*}))"
- # newer version available
- if [ "${difference}" -gt 0 ]; then
- # apply update
- mv "${new_updater}" "${script_path}"
- printf 'Updater successfully updated.\n'
- printf '%s' "Changelog: ${green}https://notabug.org/TotallyLeGIT/"
- printf '%s\n\n' "PH-userjs-updater/raw/main/CHANGELOG.md${neutral}"
- # update user.js with new version
- "${script_path}" "${@}"
- # exit with exit code from updater call above
- exit "${?}"
- # version number is equal (go to next number)
- elif [ "${difference}" -eq 0 ]; then
- current_updater_version="${current_updater_version#*.}"
- new_updater_version="${new_updater_version#*.}"
- # this should not be happening
- else
- printf '%s' "${red}Error: Something with the updater version"
- printf '%s' " seems funny. Check updater homepage"
- printf '%s\n' " and/or update manually.${neutral}"
- return 2
- fi
- done
- }
- #########################
- # Update user.js #
- #########################
- # adds given override file or all files suffixed .js in given directory
- # expects one argument: path to a file or directory
- add_override() {
- input="${1}"
- if [ -f "${input}" ]; then
- printf '\n' >> 'user.js'
- cat "${input}" >> 'user.js'
- overrides_status="${green}file appended:${neutral} ${input}"
- elif [ -d "${input}" ]; then
- files="$(find "${input}" -mindepth 1 -maxdepth 1 -name '*.js')"
- if [ -z "${files}" ]; then
- overrides_status="${yellow}empty directory:${neutral} ${input}"
- else
- printf '%s\n' "${files}" \
- | while read -r f
- do
- add_override "${f}"
- done
- # skip output because every found file produces one
- return 0
- fi
- else
- if [ "${override}" = 'soft' ]; then
- overrides_status="${green}no overrides found${neutral}"
- else
- overrides_status="${yellow}invalid path:${neutral} ${input}"
- fi
- fi
- printf '%s\n' "overrides status: ${overrides_status}"
- }
- # returns a user.js download link
- # expects one argument: the full current version (e.g.: ff91_streng_16022020)
- get_userjs_link() {
- config_is="${1%_*}"
- # will be empty or e. g. 'ff91' afterwards
- esr_is="$(printf '%s' "${config_is}" \
- | sed -ne 's/\(ff[[:digit:]]*\)_.*/\1/p')"
- # will be e. g. 'streng' afterwards
- config_is="${config_is#*_}"
- if [ "${config_wanted}" = 'keep' ]; then
- # 'keep' means to try autodetection, so 'Not detected.' is a problem
- [ "${config_is}" = 'Not detected.' ] && exit 33
- config_wanted="${config_is}"
- fi
- # TODO: remove %%2* in future version (tb2 leftover)
- if [ "${config_wanted%%2*}" != 'tb' ]; then
- # version not specified → try autodetection from user.js
- if [ "${ff_version_specified}" = 'false' ] && [ -n "${esr_is}" ]; then
- esr_url="${esr_is}/"
- # version specified or could not extract from user.js
- else
- [ "${ff_version_wanted}" -eq 0 ] && esr_url='' \
- || esr_url="ff${ff_version_wanted}/"
- fi
- fi
- printf '%s' "https://www.privacy-handbuch.de/download/"
- printf '%s' "${esr_url}${config_wanted}/user.js"
- }
- # returns the version number of a user.js file
- get_userjs_version() {
- [ -e "${1}" ] && userjs_version="$(sed -ne \
- '/user\.js\.prhdb/ s/.*"\(\([[:alnum:]]*_\)*[[:alpha:]]*_[0-9]*\)".*/\1/p' \
- -e '10 q' "${1}")"
- [ -z "${userjs_version}" ] && printf 'Not detected.' && exit 33
- printf '%s' "${userjs_version}"
- }
- # downloads a user.js, opens it and exits
- read_and_exit() {
- [ "${just_read}" = 'false' ] && return 0
- if [ "${config_wanted}" = 'keep' ]; then
- printf '%s' "${red}Error: -r option requires"
- printf '%s\n' " -c option, too.${neutral}"
- exit 1
- else
- userjs_to_read="$(mktemp -dt userjs.XXXX)/user.js"
- readonly userjs_to_read
- download_file "${userjs_to_read}" \
- "$(get_userjs_link "${config_wanted}")" \
- || exit 1
- fi
- printf '%s' "${green}user.js was saved to temporary"
- printf '%s\n' " file:${neutral} ${userjs_to_read}"
- open_file "${userjs_to_read}"
- exit 0
- }
- # applies chosen version of user.js and any custom overrides
- update_userjs() {
- current_version_long="$(get_userjs_version 'user.js')"
- printf '%s\n' "profile path: ${green}${PWD}${neutral}"
- printf '%s\n' "currently using: ${green}${current_version_long}${neutral}"
- if ! link="$(get_userjs_link "${current_version_long}")"; then
- if [ "${profile_path_option}" = 'all' ]; then
- printf '%s\n' "${yellow}Skipping profile.${neutral}"
- else
- printf '%s' "${red}Error: Could not determine which config to download."
- printf '%s\n' " Use -c CONFIG and/or -p PROFILE.${neutral}"
- fi
- return 1
- fi
- # prepare extracting CONFIG (e.g. moderat) out of previously generated link
- tmp_link="${link%/*}"
- new_userjs="${tmp_dir}/new_userjs_${tmp_link##*/}"
- download_file "${new_userjs}" "${link}" || exit 1
- if ! new_version_long="$(get_userjs_version "${new_userjs}")"; then
- printf '%s\n' "${red}Error parsing new user.js${neutral}"
- return 1
- fi
- printf '%s\n' "available online: ${green}${new_version_long}${neutral}"
- if [ "${confirm}" = 'yes' ]; then
- printf '\nThis script will update to the latest user.js'
- if [ "${override}" != 'false' ]; then
- printf ' and append custom configurations from user-overrides.js or a specified path'
- fi
- printf '.\n%s' "${red}Continue? (y)es or (n)o: y,n?${neutral} "
- read -r reply </dev/tty
- if ! printf '%s' "${reply}" | grep -q '^[yY]*$'; then
- printf '%s\n' "${yellow}Skipping profile.${neutral}"
- return 1
- fi
- fi
- # backup user.js
- mkdir -p "${backup_dir}"
- [ "${backup_multiple}" = 'true' ] && backup_date="$(date '+%FT%T')_" \
- || backup_date=''
- backup_file="${backup_dir}/${backup_date}user.js"
- [ -f 'user.js' ] && mv 'user.js' "${backup_file}"
- # install newest user.js
- mv "${new_userjs}" 'user.js'
- printf '%s\n' "user.js status: ${green}backed up and updated${neutral}"
- # apply overrides
- if [ "${override}" != 'false' ]; then
- printf '\n// user-overrides\n' >> 'user.js'
- add_override "${override_path}"
- fi
- # create diff
- if [ "${compare}" = 'true' ]; then
- diffs="$(diff -wBU 0 "${backup_file}" 'user.js')"
- if [ -n "${diffs}" ]; then
- diff_file="${diff_dir}/${backup_date}diff.txt"
- mkdir -p "${diff_dir}"
- printf '%s' "${diffs}" > "${diff_file}"
- diff_status="${green}diff file created – user.js changed${neutral}"
- else
- diff_status="${green}skipped – user.js identical${neutral}"
- fi
- printf '%s\n' "diff status: ${diff_status}"
- fi
- [ "${view}" = 'true' ] && open_file "user.js"
- }
- #########################
- # Main() #
- #########################
- set_variables
- parse_options "${@}"
- prepare "${0}"
- update_updater "${@}"
- abstract_os
- # if esr wasn't forced, try to detect it
- set_firefox_version
- read_and_exit
- get_profile_path
- while read -r line; do
- printf '————————————————————————————————————————————\n'
- # this enables us to leave relative profile paths as they are
- cd "${pwd_start}" || exit 99
- if ! cd "${line}" 2>/dev/null; then
- printf '%s' "${yellow}Warning: Profile path not found:"
- printf '%s\n' " ${green}${line}${neutral}"
- continue
- fi
- update_userjs
- done <<EOF
- ${profile_path}
- EOF
|