updater 16 KB

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