updater 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. #!/bin/sh
  2. ## Updater für die user.js von Privacy-Handbuch.de
  3. ## Version: 1.3.0
  4. ## by: TotallyLeGIT
  5. ## based on the ghacks user.js updater by:
  6. ## Author: Pat Johnson (@overdodactyl)
  7. ## Additional contributors: @earthlng, @ema-pe, @claustromaniac
  8. ## Dependencies: ·curl or wget
  9. scriptLocation="$(readlink -f "${0}" 2>/dev/null || greadlink -f "${0}" 2>/dev/null || printf "%s" "${0}")"
  10. readonly scriptLocation
  11. #########################
  12. # Base variables #
  13. #########################
  14. # Colors used for printing only when stdout is a terminal
  15. if [ -t 1 ]; then
  16. red="$(tput setaf 1)$(tput bold)"
  17. blue="$(tput setaf 4)"
  18. green="$(tput setaf 2)"
  19. yellow="$(tput setaf 3)"
  20. neutral="$(tput sgr0)" # No Color
  21. else
  22. red=""
  23. blue=""
  24. green=""
  25. yellow=""
  26. neutral=""
  27. fi
  28. # Argument defaults
  29. BACKUP='multiple'
  30. COMPARE=false
  31. configWanted='keep'
  32. CONFIRM='yes'
  33. esr=0
  34. OVERRIDE="soft" # soft: look for user-overrides.js but only give info when none found
  35. overridePath='user-overrides.js'
  36. profilePathOption=false
  37. VIEW=false
  38. UPDATE='no'
  39. readonly backupsDir="userjs_backups"
  40. readonly diffsDir="userjs_diffs"
  41. # Download method priority: curl → wget
  42. if command -v curl >/dev/null; then
  43. DOWNLOAD_COMMAND='curl --max-redirs 2 --max-time 30 --fail --silent --output'
  44. elif command -v wget >/dev/null; then
  45. # -O file = --output-document=file
  46. DOWNLOAD_COMMAND='wget --max-redirect=2 --timeout=30 --quiet -O'
  47. else
  48. echo "${red}This script requires curl or wget."
  49. echo "Process aborted.${neutral}"
  50. exit 2
  51. fi
  52. show_banner () {
  53. echo "${blue}
  54. #########################################################################
  55. #### Updater for Privacy-Handbuch's user.js ####
  56. #### Suitable for both Firefox and Thunderbird ####
  57. #########################################################################${neutral}
  58. Updater: ${blue}https://notabug.org/TotallyLeGIT/PH-userjs-updater${neutral}
  59. Firefox user.js: ${blue}https://www.privacy-handbuch.de/handbuch_21u.htm${neutral}
  60. Thunderbird user.js: ${blue}https://www.privacy-handbuch.de/handbuch_31p.htm${neutral}
  61. "
  62. }
  63. #########################
  64. # Arguments #
  65. #########################
  66. usage() {
  67. show_banner
  68. echo
  69. echo "${blue}Usage: ${0} [-bdhilnrsuy] [-c CONFIG] [-e VERSION] [-o OVERRIDE] [-p PROFILE]${neutral}" 1>&2 # Echo usage string to standard error
  70. echo "
  71. -b Only keep one user.js backup and one diff file.
  72. -c CONFIG Specify the Firefox user.js config you want: minimal, moderat, streng or hotspot
  73. or for Thunderbird: tb or tb2 .
  74. This is only needed if you want to change to another config.
  75. -d Create a diff file comparing old and new user.js within ${diffsDir} subdirectory.
  76. -e VERSION Force download of user.js for Firefox ESR with version number VERSION.
  77. Example: \"-e 91\" will download the user.js for Firefox ESR Version 91.x .
  78. -h Show this help message and exit.
  79. -i Inspect the resulting user.js file.
  80. -l Interactively choose your profile from a list.
  81. -n Do not append any overrides even if user-overrides.js exists.
  82. -o OVERRIDE Filename or path to overrides file, needed if different than PROFILE/user-overrides.js .
  83. If used with -p, the path may be relative to PROFILE or an absolute path.
  84. If given a directory, all files inside ending in .js will be appended.
  85. -p PROFILE Path to your profile directory, needed if different than this script's location.
  86. May be used multiple times.
  87. IMPORTANT: If the profile path includes spaces, wrap whole path in quotes.
  88. -r Only download user.js to a temporary file and open it. This requires -c.
  89. -s Check for updates of this updater and update silently without confirmation.
  90. -u Check for updates of this updater and ask for confirmation if update available.
  91. -y Update user.js without confirmation.
  92. "
  93. }
  94. #########################
  95. # (Pro)File Handling #
  96. #########################
  97. abstract_OS () {
  98. if [ "$(uname)" = 'Darwin' ]; then
  99. readonly open_command='open'
  100. readonly version_command='/Applications/Firefox.app/Contents/MacOS/firefox --version'
  101. readonly ff_file_path="${HOME}/Library/Application Support/Firefox/profiles.ini"
  102. readonly tb_file_path="${HOME}/Library/Thunderbird/profiles.ini"
  103. else
  104. readonly open_command='xdg-open'
  105. readonly version_command='firefox --version'
  106. readonly ff_file_path="${HOME}/.mozilla/firefox/profiles.ini"
  107. readonly tb_file_path="${HOME}/.thunderbird/profiles.ini"
  108. fi
  109. # set effective path for each profile.ini
  110. [ -f "${ff_file_path}" ] && fffile="${ff_file_path}" || fffile=""
  111. [ -f "${tb_file_path}" ] && tbfile="${tb_file_path}" || tbfile=""
  112. readonly fffile tbfile
  113. }
  114. # downloads a resource. expects two arguments:
  115. # $1 the variable name, where the filename will be saved to
  116. # $2 the resource to download
  117. # example usage: download_file 'var' 'https://example.com/resource'
  118. download_file () {
  119. eval "$1=$(mktemp)"
  120. if ! eval "${DOWNLOAD_COMMAND} \$${1} ${2}"; then
  121. echo "${red}Download of resource failed:${neutral} ${2}"
  122. return 33
  123. fi
  124. }
  125. open_file () { # expects one argument: file_path
  126. eval "${open_command} ${1}" || echo "${red}Error: Sorry, opening" \
  127. "files is not supported for your OS.${neutral}"
  128. }
  129. readIniFile () { # expects one argument: absolute path of profiles.ini
  130. readonly inifile="${1}"
  131. if [ "$(grep -c '^\[Profile' "${inifile}")" -eq 1 ]; then # only 1 profile found
  132. tempIni="$(grep '^\[Profile' -A 4 "${inifile}")"
  133. else # more than 1 profile
  134. echo 'Profiles found:'
  135. echo '–––––––––––––––––––––––––––––––––––'
  136. grep --color=never -E 'Default=[^1]|\[Profile[0-9]*\]|Name=|Path=|^$' "${inifile}"
  137. echo '–––––––––––––––––––––––––––––––––––'
  138. echo 'Select the profile number ( 0 for Profile0, 1 for Profile1 etc. ):'
  139. read -r reply
  140. echo
  141. tempIni="$(grep '^\[Profile'"${reply}" -A 4 "${inifile}")" || { echo "${red}Profile${reply} does not exist!${neutral}" && exit 1 ; }
  142. fi
  143. tempProfilePath="$(echo "${tempIni}" | sed -n 's/^Path=\(.*\)$/\1/p')"
  144. pathIsRel="$(echo "${tempIni}" | sed -n 's/^IsRelative=\([01]\)$/\1/p')"
  145. readonly pathIsRel
  146. # update global variable if path is relative
  147. [ "${pathIsRel}" -eq 1 ] && tempProfilePath="$(dirname "${inifile}")/${tempProfilePath}"
  148. updateProfilePath "${tempProfilePath}"
  149. }
  150. getProfilePath () {
  151. if [ "${profilePathOption}" = 'list' ]; then
  152. if [ -n "${fffile}" ]; then
  153. ini="${fffile}"
  154. [ -n "${tbfile}" ] && ini="both" || printf "Firefox detected.\n\n"
  155. elif [ -n "${tbfile}" ]; then
  156. ini="${tbfile}"
  157. printf "Thunderbird detected.\n\n"
  158. else
  159. echo "${red}Error: Sorry, Thunderbird and Firefox could not be found or -l is not supported for your OS. Try -p option.${neutral}" && exit 1
  160. fi
  161. if [ "${ini}" = "both" ]; then
  162. echo "(F)irefox and (T)hunderbird were detected. Select one: F/T"
  163. read -r reply
  164. reply="$(printf "%s" "${reply}" | tr '[:upper:]' '[:lower:]')"
  165. { [ -z "${reply##f*}" ] && ini="${fffile}" && printf "Firefox was selected.\n\n" ; } || { [ -z "${reply##t*}" ] && ini="${tbfile}" && printf "Thunderbird was selected.\n\n" ; } || { echo "${red}Error: Sorry, could not parse your input.${neutral}" && exit 1 ; }
  166. fi
  167. readIniFile "${ini}" # updates PROFILE_PATH or exits on error
  168. elif [ "${profilePathOption}" = false ]; then
  169. PROFILE_PATH="$(dirname "${scriptLocation}")"
  170. fi
  171. }
  172. updateProfilePath () { # expects a profile path as argument
  173. # if profile path empty: update it
  174. if [ -z "${PROFILE_PATH}" ]; then
  175. PROFILE_PATH="${1}"
  176. # update profilePathOption var so we know there's a profile path
  177. [ "${profilePathOption}" = "false" ] && profilePathOption="true"
  178. else # append additional profile path
  179. PROFILE_PATH="${PROFILE_PATH}\n${1}"
  180. fi
  181. }
  182. #########################
  183. # Update updater #
  184. #########################
  185. # Returns the version number of the updater file in the first argument.
  186. get_updater_version () {
  187. sed -n '4 s/.*[[:blank:]]\([0-9]*\.[0-9]*\.[0-9]*\)[[:graph:]]*/\1/p' "${1}"
  188. }
  189. # Default: Do not check for updates of the updater at all.
  190. # Args:
  191. # -u: Check for update. if available, ask user if they want to update.
  192. # -s: Check for update, if available, execute without asking.
  193. update_updater () {
  194. if [ "${UPDATE}" = 'no' ]; then
  195. return 0 # Do not to check for updates.
  196. fi
  197. download_file 'tmp_updater' \
  198. 'https://notabug.org/TotallyLeGIT/PH-userjs-updater/raw/main/updater' \
  199. || return 1
  200. readonly tmp_updater
  201. updaterVerOld="$(get_updater_version "${scriptLocation}")"
  202. updaterVerNew="$(get_updater_version "${tmp_updater}")"
  203. readonly updaterVerOldConst="${updaterVerOld}"
  204. # compare updater versions, if different
  205. while [ ! "${updaterVerOld}" = "${updaterVerNew}" ]; do
  206. difference="$((${updaterVerNew%%.*}-${updaterVerOld%%.*}))"
  207. if [ "${difference}" -gt 0 ]; then # newer version available
  208. if [ "${UPDATE}" = 'yes' ]; then # ask to apply update
  209. echo "There is a newer version of the updater available. ${red}Update? Y/N${neutral}"
  210. read -r reply
  211. echo "${reply}" | grep -qv "^[yY]*$" && return 0 # Update available, but user chooses not to update
  212. fi
  213. # apply update, regardless if silent or after "yes"
  214. mv "${tmp_updater}" "${scriptLocation}"
  215. chmod u+x "${scriptLocation}"
  216. echo "Updater successfully updated."
  217. # show changes for this version to the user
  218. if download_file 'tmp_changelog' \
  219. 'https://notabug.org/TotallyLeGIT/PH-userjs-updater/raw/main/CHANGELOG.md'
  220. then
  221. readonly tmp_changelog
  222. echo "${green}Changelog:${neutral}"
  223. echo
  224. sed -ne '/^## '"${updaterVerOldConst}"'$/ !p' -e "/${updaterVerOldConst}/ q" "${tmp_changelog}"
  225. fi
  226. break
  227. elif [ "${difference}" -eq 0 ]; then # version number is equal (go to next number)
  228. updaterVerOld="${updaterVerOld#*.}"
  229. updaterVerNew="${updaterVerNew#*.}"
  230. else # this should not be happening
  231. echo "${red}Error: Something with the version numbers seems funny. Check updater homepage and/or update it manually.${neutral}"
  232. return 2
  233. fi
  234. done
  235. return 0
  236. }
  237. #########################
  238. # Update user.js #
  239. #########################
  240. # returns a user.js download link. expects the current version as argument. e.g.: streng_16022020
  241. get_userjs_link () {
  242. configIs="${1%_*}"
  243. configIs="${configIs#*_}"
  244. [ "${configWanted}" = "keep" ] && configWanted="${configIs}" && [ "${configIs}" = "Not detected." ] && exit 33
  245. if [ "${configIs%%2*}" != "tb" ]; then
  246. [ "${esr}" = "0" ] && esrurl="" || esrurl="ff${esr}/"
  247. fi
  248. echo "https://www.privacy-handbuch.de/download/${esrurl}${configWanted}/user.js"
  249. }
  250. # Returns version number of a user.js file
  251. get_userjs_version () {
  252. [ -e "${1}" ] && returnVer="$(sed -ne '/user\.js\.prhdb/ s/.*"\(\([[:alnum:]]*_\)*[[:alpha:]]*_[[:digit:]]*\)".*/\1/p' -e '10 q' "${1}")"
  253. #[ -e "${1}" ] && returnVer="$(sed -n '5 s/.*"\([[:alnum:]_]\+[0-9]\+\)".*/\1/p' "${1}")"
  254. [ -z "${returnVer}" ] && echo "Not detected." && exit 33
  255. echo "${returnVer}"
  256. }
  257. # returns 0 if firefox is release or the major version number if firefox is esr
  258. is_esr () {
  259. # result is e. g.: 85.0 or 78.7
  260. ffver="$(eval "${version_command}" 2>/dev/null | cut -d ' ' -f 3 \
  261. | cut -d '.' -f -2)" || return 1
  262. [ "${ffver##*.}" -eq 0 ] && echo "0" || echo "${ffver%%.*}"
  263. }
  264. add_override () {
  265. input="${1}"
  266. if [ -f "${input}" ]; then
  267. echo >> user.js
  268. cat "${input}" >> user.js
  269. echo "Status: ${green}Override file appended:${neutral} ${input}"
  270. elif [ -d "${input}" ]; then
  271. find "${input}" -mindepth 1 -maxdepth 1 -name "*.js" |
  272. while IFS= read -r f
  273. do
  274. add_override "${f}"
  275. done
  276. else
  277. if [ "${OVERRIDE}" = "soft" ]; then
  278. echo "Info: No override file or directory applied."
  279. else
  280. echo "${yellow}Warning: Could not find override file or directory:${neutral} ${input}"
  281. fi
  282. fi
  283. }
  284. # Applies latest version of user.js and any custom overrides
  285. update_userjs () {
  286. currentVerFull="$(get_userjs_version user.js)"
  287. # if esr wasn't forced, try to detect it
  288. if [ "${esr}" -eq "0" ]; then
  289. if esr="$(is_esr)"; then
  290. [ "${esr}" -eq "0" ] && esrstring="Release" || esrstring="ESR ${esr}"
  291. else
  292. esrstring="not found" # likely because 'firefox --version' failed
  293. fi
  294. fi
  295. echo "Firefox version: ${green}${esrstring}${neutral}"
  296. echo "Profile path: ${green}${PWD}${neutral}"
  297. echo "Currently using: ${green}${currentVerFull}${neutral}"
  298. # some error handling while setting variables and telling where it went wrong
  299. link="$(get_userjs_link "${currentVerFull}")"
  300. [ "${?}" -eq 33 ] && echo "${red}Error: Could not determine which config to download. Use -c CONFIG and/or -p PROFILE. ${neutral}" && exit 1
  301. download_file 'tmp_userjs' "${link}" \
  302. || exit 1
  303. readonly tmp_userjs
  304. newVersion="$(get_userjs_version "${tmp_userjs}")"
  305. [ "${?}" -eq 33 ] && echo "${red}Error parsing new user.js${neutral}" && exit 1
  306. echo "Available online: ${green}${newVersion}${neutral}"
  307. echo
  308. if [ "${CONFIRM}" = 'yes' ]; then
  309. echo "This script will update to the latest user.js file and append any custom configurations from user-overrides.js. ${red}Continue? Y/N ${neutral}"
  310. read -r reply </dev/tty
  311. echo
  312. echo "${reply}" | grep -qv "^[yY]*$" && echo "${red}Process aborted.${neutral}" && rm "${tmp_userjs}" && return 1
  313. fi
  314. # backup user.js
  315. mkdir -p "${backupsDir}"
  316. [ "${BACKUP}" = 'single' ] && bakDate="$(date -Iseconds)." || bakDate=""
  317. bakName="${backupsDir}/${bakDate}user.js"
  318. [ -f user.js ] && mv user.js "${bakName}"
  319. # install newest user.js
  320. mv "${tmp_userjs}" user.js
  321. echo "Status: ${green}user.js has been backed up and replaced with the latest version!${neutral}"
  322. # apply overrides
  323. if [ "${OVERRIDE}" != "false" ]; then
  324. printf "\n%s\n" "// user-overrides" >> user.js
  325. add_override "${overridePath}"
  326. fi
  327. # create diff
  328. if [ "${COMPARE}" = true ]; then
  329. [ "${BACKUP}" = 'single' ] && diffDate="$(date -Iseconds)." || diffDate=""
  330. diffName="${diffsDir}/${diffDate}diff.txt"
  331. diffs="$(diff -wBU 0 "${bakName}" user.js)"
  332. if [ -n "${diffs}" ]; then
  333. mkdir -p "${diffsDir}"
  334. echo "${diffs}" > "${diffName}"
  335. echo "Status: ${green}A diff file was created:${neutral} ${PWD}/${diffName}"
  336. else
  337. echo "${yellow}Your new user.js file appears to be identical. No diff file was created.${neutral}"
  338. fi
  339. fi
  340. [ "${VIEW}" = true ] && open_file "${PWD}/user.js"
  341. }
  342. #########################
  343. # Execute #
  344. #########################
  345. abstract_OS
  346. if [ ${#} != 0 ]; then
  347. firstArg="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
  348. # Display usage if first argument contains -help
  349. if [ -z "${firstArg##*-help*}" ]; then
  350. usage
  351. exit 0
  352. else
  353. while getopts ":bc:de:hilno:p:rsuy" opt; do
  354. case "${opt}" in
  355. b)
  356. BACKUP='single'
  357. ;;
  358. c)
  359. if ! configWanted="$(printf "%s" "${OPTARG}" | tr '[:upper:]' '[:lower:]' | grep -om 1 '^minimal\|^moderat\|^streng\|^hotspot\|^tb\|^tb2')"; then
  360. echo "${red}Error: Invalid CONFIG.${neutral} Valid values are: minimal, moderat, streng, hotspot, tb or tb2"
  361. exit 1
  362. fi
  363. ;;
  364. d)
  365. COMPARE=true
  366. ;;
  367. e)
  368. esr="${OPTARG}"
  369. ;;
  370. h)
  371. usage
  372. exit 0
  373. ;;
  374. i)
  375. VIEW=true
  376. ;;
  377. l)
  378. profilePathOption='list'
  379. ;;
  380. n)
  381. OVERRIDE="false"
  382. ;;
  383. o)
  384. OVERRIDE="true"
  385. overridePath="${OPTARG}"
  386. ;;
  387. p)
  388. updateProfilePath "${OPTARG}"
  389. ;;
  390. r)
  391. if [ "${configWanted}" = "keep" ]; then
  392. echo "${red}Error: -r option requires -c option, too.${neutral}"
  393. exit 1
  394. else
  395. download_file 'tmp_read_userjs' \
  396. "$(get_userjs_link "${configWanted}")" \
  397. || exit 1
  398. readonly tmp_read_userjs
  399. fi
  400. echo "${green}user.js was saved to temporary file ${tmp_read_userjs}${neutral}"
  401. open_file "${tmp_read_userjs}"
  402. exit 0
  403. ;;
  404. s)
  405. UPDATE='silent'
  406. ;;
  407. u)
  408. UPDATE='yes'
  409. ;;
  410. y)
  411. CONFIRM='no'
  412. ;;
  413. \?)
  414. echo
  415. echo "${red}Error! Invalid option: -${OPTARG}${neutral}" 1>&2
  416. usage
  417. exit 2
  418. ;;
  419. :)
  420. echo "${red}Error! Option -${OPTARG} requires an argument.${neutral}" 1>&2
  421. exit 2
  422. ;;
  423. esac
  424. done
  425. fi
  426. fi
  427. getProfilePath # updates PROFILE_PATH or exits on error
  428. echo "${PROFILE_PATH}" |
  429. while read -r path; do
  430. cd "${path}" || { echo "${red}Error: No profile path found.${neutral}" && exit 1 ; }
  431. update_userjs
  432. echo
  433. done
  434. update_updater