updater 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. #!/bin/sh
  2. ## Updater für die user.js von Privacy-Handbuch.de
  3. ## Version: 1.2.1
  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. configIs="${configIs#*_}"
  228. [ "${configWanted}" = "keep" ] && configWanted="${configIs}" && [ "${configIs}" = "Not detected." ] && exit 33
  229. if [ "${configIs%%2*}" != "tb" ]; then
  230. [ "${esr}" = "0" ] && esrurl="" || esrurl="ff${esr}/"
  231. fi
  232. echo "https://www.privacy-handbuch.de/download/${esrurl}${configWanted}/user.js"
  233. }
  234. # Returns version number of a user.js file
  235. get_userjs_version () {
  236. [ -e "$1" ] && returnVer="$(sed -n '5 s/.*"\([[:alnum:]_]\+[0-9]\+\)".*/\1/p' "$1")"
  237. [ -z "${returnVer}" ] && echo "Not detected." && exit 33
  238. echo "${returnVer}"
  239. }
  240. # returns 0 if firefox is release or the major version number if firefox is esr
  241. is_esr () {
  242. firefox --version > /dev/null 2>&1 || return 1
  243. ffver="$(firefox --version | cut -d ' ' -f 3 | cut -d '.' -f -2)" # result is e. g.: 85.0 or 78.7
  244. [ "${ffver##*.}" -eq 0 ] && echo "0" || echo "${ffver%%.*}"
  245. }
  246. add_override () {
  247. input="$1"
  248. if [ -f "${input}" ]; then
  249. echo >> user.js
  250. cat "${input}" >> user.js
  251. echo "Status: ${green}Override file appended:${neutral} ${input}"
  252. elif [ -d "${input}" ]; then
  253. find "${input}" -mindepth 1 -maxdepth 1 -name "*.js" |
  254. while IFS= read -r f
  255. do
  256. add_override "$f"
  257. done
  258. else
  259. if [ "${OVERRIDE}" = "soft" ]; then
  260. echo "Info: No override file or directory applied."
  261. else
  262. echo "${yellow}Warning: Could not find override file or directory:${neutral} ${input}"
  263. fi
  264. fi
  265. }
  266. # Applies latest version of user.js and any custom overrides
  267. update_userjs () {
  268. currentVerFull="$(get_userjs_version user.js)"
  269. [ "${esr}" = "0" ] && esr="$(is_esr)" # if esr wasn't forced, detect it
  270. if [ "$?" -eq 1 ]; then
  271. esrstring="not found" # 'firefox --version' failed
  272. else
  273. [ "${esr}" = "0" ] && esrstring="Release" || esrstring="ESR ${esr}"
  274. fi
  275. echo "Firefox version: ${green}${esrstring}${neutral}"
  276. echo "Profile path: ${green}${PWD}${neutral}"
  277. echo "Currently using: ${green}${currentVerFull}${neutral}"
  278. # some error handling while setting variables and telling where it went wrong
  279. link="$(get_userjs_link "${currentVerFull}")"
  280. [ "$?" -eq 33 ] && echo "${red}Error: Could not determine which config to download. Use -c CONFIG and/or -p PROFILE. ${neutral}" && exit 1
  281. newfile="$(download_file "${link}")"
  282. [ "$?" -eq 33 ] && echo "${red}Error downloading file ${neutral}" && exit 1
  283. newVersion="$(get_userjs_version "${newfile}")"
  284. [ "$?" -eq 33 ] && echo "${red}Error parsing new user.js${neutral}" && exit 1
  285. echo "Available online: ${green}${newVersion}${neutral}"
  286. echo
  287. if [ $CONFIRM = 'yes' ]; then
  288. 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}"
  289. read -r reply </dev/tty
  290. echo
  291. echo "${reply}" | grep -qv "^[yY]*$" && echo "${red}Process aborted.${neutral}" && rm "${newfile}" && return 1
  292. fi
  293. # backup user.js
  294. mkdir -p "${backupsDir}"
  295. [ $BACKUP = 'single' ] && bakDate="$(date -Iseconds)." || bakDate=""
  296. bakName="${backupsDir}/${bakDate}user.js"
  297. [ -f user.js ] && mv user.js "${bakName}"
  298. # install newest user.js
  299. mv "${newfile}" user.js
  300. echo "Status: ${green}user.js has been backed up and replaced with the latest version!${neutral}"
  301. # apply overrides
  302. if [ "${OVERRIDE}" != "false" ]; then
  303. printf "\n%s\n" "// user-overrides" >> user.js
  304. add_override "${overridePath}"
  305. fi
  306. # create diff
  307. if [ "$COMPARE" = true ]; then
  308. [ "$BACKUP" = 'single' ] && diffDate="$(date -Iseconds)." || diffDate=""
  309. diffName="${diffsDir}/${diffDate}diff.txt"
  310. diffs="$(diff -wBU 0 "${bakName}" user.js)"
  311. if [ -n "${diffs}" ]; then
  312. mkdir -p "${diffsDir}"
  313. echo "${diffs}" > "${diffName}"
  314. echo "Status: ${green}A diff file was created:${neutral} ${PWD}/${diffName}"
  315. else
  316. echo "${yellow}Your new user.js file appears to be identical. No diff file was created.${neutral}"
  317. fi
  318. fi
  319. [ "$VIEW" = true ] && open_file "${PWD}/user.js"
  320. }
  321. #########################
  322. # Execute #
  323. #########################
  324. if [ $# != 0 ]; then
  325. readonly firstArg=$(echo "$1" | tr '[:upper:]' '[:lower:]')
  326. # Display usage if first argument contains -help
  327. if [ -z "${firstArg##*-help*}" ]; then
  328. usage
  329. else
  330. while getopts ":bc:de:hilno:p:rsuy" opt; do
  331. case $opt in
  332. b)
  333. BACKUP='single'
  334. ;;
  335. c)
  336. if ! configWanted="$(printf "%s" "${OPTARG}" | tr '[:upper:]' '[:lower:]' | grep -o "^minimal\|^moderat\|^streng\|^hotspot\|^tb\|^tb2")"; then
  337. echo "${red}Error: Invalid CONFIG.${neutral} Valid values are: minimal, moderat, streng, hotspot, tb or tb2"
  338. exit 1
  339. fi
  340. ;;
  341. d)
  342. COMPARE=true
  343. ;;
  344. e)
  345. esr="${OPTARG}"
  346. ;;
  347. h)
  348. usage
  349. ;;
  350. i)
  351. VIEW=true
  352. ;;
  353. l)
  354. profilePathOption='list'
  355. ;;
  356. n)
  357. OVERRIDE="false"
  358. ;;
  359. o)
  360. OVERRIDE="true"
  361. overridePath="${OPTARG}"
  362. ;;
  363. p)
  364. updateProfilePath "${OPTARG}"
  365. ;;
  366. r)
  367. if [ "$configWanted" = "keep" ]; then
  368. echo "${red}Error: -r option requires -c option, too.${neutral}"
  369. exit 1
  370. else
  371. tempFile="$(download_file "$(get_userjs_link "${configWanted}")")"
  372. [ "$?" -eq 33 ] && echo "${red}Error downloading file ${neutral}" && exit 1
  373. fi
  374. echo "${green}user.js was saved to temporary file ${tempFile}${neutral}"
  375. open_file "${tempFile}"
  376. exit 1
  377. ;;
  378. s)
  379. UPDATE='silent'
  380. ;;
  381. u)
  382. UPDATE='yes'
  383. ;;
  384. y)
  385. CONFIRM='no'
  386. ;;
  387. \?)
  388. echo
  389. echo "${red}Error! Invalid option: -$OPTARG${neutral}" 1>&2
  390. usage
  391. ;;
  392. :)
  393. echo "${red}Error! Option -$OPTARG requires an argument.${neutral}" 1>&2
  394. exit 1
  395. ;;
  396. esac
  397. done
  398. fi
  399. fi
  400. getProfilePath # updates PROFILE_PATH or exits on error
  401. echo "${PROFILE_PATH}" |
  402. while read -r path; do
  403. cd "${path}" || { echo "${red}Error: No profile path found.${neutral}" && exit 1 ; }
  404. update_userjs
  405. echo
  406. done
  407. update_updater