#!/bin/sh # shellcheck disable=SC3043,SC1090,SC3044 # silence shellcheck warnings : "${LIBS_SOURCED}" ABL_INSTALLER_VER=2 ABL_SERVICE_PATH=/etc/init.d/adblock-lean ABL_TMP_DIR=/var/run/adblock-lean/tmp ABL_UPD_DIR=${ABL_TMP_DIR}/update ABL_INST_DIR=${ABL_TMP_DIR}/remote_abl ABL_PID_DIR=/tmp/adblock-lean ABL_CONFIG_DIR=/etc/adblock-lean ABL_CONFIG_FILE=${ABL_CONFIG_DIR}/config UCL_ERR_FILE="${ABL_TMP_DIR}/uclient-fetch_err" : "${ABL_REPO_AUTHOR:=lynxthecat}" ABL_GH_URL_API="https://api.github.com/repos/${ABL_REPO_AUTHOR}/adblock-lean" ABL_MAIN_BRANCH=master ABL_FILES_REG_PATH=/etc/adblock-lean/abl-reg.md5 # silence shellcheck warnings : "${ABL_INSTALLER_VER}" "${ABL_CONFIG_FILE}" LC_ALL=C DEFAULT_IFS=' ' _NL_=' ' IFS="${DEFAULT_IFS}" _DELIM_="$(printf '\35')" if [ -z "${MSGS_DEST}" ] then if [ -t 0 ] then export MSGS_DEST=/dev/tty else export MSGS_DEST=/dev/null fi fi # $luci_skip_dialogs is set if sourced from external RPC script for luci [ -n "${luci_skip_dialogs}" ] && export ABL_LUCI_SOURCED=1 [ -z "${DO_DIALOGS}" ] && [ -z "${ABL_LUCI_SOURCED}" ] && [ -z "${APPROVE_UPD_CHANGES}" ] && [ "${MSGS_DEST}" = "/dev/tty" ] && \ DO_DIALOGS=1 if sed --version 2>/dev/null | grep -qe '(GNU sed)' then SED_CMD="sed" else SED_CMD="busybox sed" fi # exit with code ${1} # if function 'abl_luci_exit' is defined, execute it before exit cleanup_and_exit() { trap - INT TERM EXIT rm -rf "${ABL_TMP_DIR}" "${ABL_PID_DIR}" [ -n "${ABL_LUCI_SOURCED}" ] && abl_inst_luci_exit "${1}" exit "${1}" } # check if var names are safe to use with eval are_var_names_safe() { local var_name for var_name in "${@}" do case "${var_name}" in *[!a-zA-Z_]*) reg_failure "Invalid var name '${var_name}'."; return 1; esac done : } check_func() { [ "$(type "${1}" 2>/dev/null | head -n1)" = "${1} is a function" ] } check_util() { command -v "${1}" 1>/dev/null } # asks the user to pick an option # 1 - input in the format 'a|b|c' # output via $REPLY pick_opt() { while : do printf %s "${1}: " 1>${MSGS_DEST} read -r REPLY case "${REPLY}" in *[!A-Za-z0-9_]*) printf '\n%s\n\n' "Please enter ${1}" 1>${MSGS_DEST}; continue; esac eval "case \"${REPLY}\" in ${1}) return 0 ;; *) printf '\n%s\n\n' \"Please enter ${1}\" 1>${MSGS_DEST} esac" done } # checks if string $1 is included in newline-separated list $2 # if $3 is specified, uses the value as list delimiter # result via return status is_included() { local delim="${3:-"${_NL_}"}" case "$2" in "$1"|"$1${delim}"*|*"${delim}$1"|*"${delim}$1${delim}"*) return 0 ;; *) return 1 esac } try_mv() { [ -z "${1}" ] || [ -z "${2}" ] && { reg_failure "try_mv(): bad arguments."; return 1; } mv -f "${1}" "${2}" || { reg_failure "Failed to move '${1}' to '${2}'."; return 1; } : } # 0 - (optional) '-p' # 1 - path try_mkdir() { local p= [ "${1}" = '-p' ] && { p='-p'; shift; } [ -d "${1}" ] && return 0 mkdir ${p} "${1}" || { reg_failure "Failed to create directory '${1}'."; return 1; } : } # prints each argument into a separate line print_msg() { local m for m in "${@}" do printf '%s\n' "${m}" > "$MSGS_DEST" done } log_msg() { local m msgs='' msgs_prefix='' _arg err_l=info local IFS="${DEFAULT_IFS}" for _arg in "$@" do case "${_arg}" in "-err") err_l=err msgs_prefix="Error: " ;; '') msgs="${msgs}dummy${_DELIM_}" ;; *) msgs="${msgs}${msgs_prefix}${_arg}${_DELIM_}"; [ -n "${msgs_prefix}" ] && msgs_prefix= esac done msgs="${msgs%"${_DELIM_}"}" IFS="${_DELIM_}" for m in ${msgs} do IFS="${DEFAULT_IFS}" case "${m}" in dummy) echo ;; *) print_msg "${m}" logger -t abl-install -p user."${err_l}" "${m}" esac done : } reg_failure() { log_msg -err "" "${1}" luci_errors="${luci_errors}${1}${_NL_}" } # Get version and update channel of adblock-lean file # Assigns vars $2 = version, $3 = update channel # 1 - path to adblock-lean service file # Return codes: # 1 - error # 2 - no version found # 3 - 1-string version format # 4 - 2-strings version format # 5 - 2-strings version format, config v9 or higher get_abl_version() { get_ver_str() { [ -n "${1}" ] && [ -n "${2}" ] && [ -n "${3}" ] && are_var_names_safe "${1}" "${2}" || return 1 local _par res_version='' res_upd_channel='' for _par in version upd_channel do local key_ptrn='' migr_ptrn='' res='' case "${_par}" in version) key_ptrn="\\s*ABL_VERSION" [ "${4}" = '-o' ] && migr_ptrn='s/^[^_]*_//;' ;; upd_channel) key_ptrn="\\s*ABL_UPD_CHANNEL" [ "${4}" = '-o' ] && { key_ptrn="\\s*ABL_VERSION" migr_ptrn='s/_.*//;'; } esac [ "${4}" = '-o' ] && key_ptrn="\\s*#${key_ptrn}" res="$(${SED_CMD} -n "/^${key_ptrn}=/{s/^${key_ptrn}=//;s/#.*$//;s/\"//g;${migr_ptrn}p;:1 n;b1;}" "${3}")" && [ -n "${res}" ] || return 1 eval "res_${_par}=\"\${res}\"" done eval "${1}=\"\${res_upd_channel}\" ${2}=\"\${res_version}\"" : "${res_upd_channel}" "${res_version}" } local gv_ver='' gv_upd_ch='' gv_rv='' if [ -n "${2}${3}" ] then are_var_names_safe "${2}" "${3}" || return 1 eval "${2}"='' "${3}"='' fi # v0.7.3 and later if old_config_format="$(get_config_format "${1}")" && [ -n "${old_config_format}" ] && [ "${old_config_format}" -ge 9 ] && grep -q '^\s*ABL_UPD_CHANNEL=' "${1}" && get_ver_str gv_upd_ch gv_ver "${1}" then gv_rv=5 # version format in v0.7.2 and later elif grep -q '^\s*ABL_UPD_CHANNEL=' "${1}" && get_ver_str gv_upd_ch gv_ver "${1}" then gv_rv=4 # version format in v0.6.0 - v0.7.1 elif grep -q '^\s*#\s*ABL_VERSION=' "${1}" && get_ver_str gv_upd_ch gv_ver "${1}" -o then gv_rv=3 else gv_rv=2 fi : "${gv_ver}" "${gv_upd_ch}" [ -n "${2}" ] && eval "${2}"='${gv_ver}' [ -n "${3}" ] && eval "${3}"='${gv_upd_ch}' return ${gv_rv} } inst_failed() { local fail_msg="${1}" [ -s "${UCL_ERR_FILE}" ] && fail_msg="${fail_msg} uclient-fetch errors: '$(cat "${UCL_ERR_FILE}")'" [ -n "${fail_msg}" ] && reg_failure "${fail_msg}" reg_failure "Failed to install adblock-lean." rm -rf "${ABL_INST_DIR}" "${UCL_ERR_FILE}" exit 1 } failsafe_log() { printf '%s\n' "${1}" > "${MSGS_DEST:-/dev/tty}" logger -t adblock-lean "${1}" } # shellcheck disable=SC2120 # get config format from config or main script file contents # input via STDIN or ${1} get_config_format() { local conf_form_sed_expr='/^[ \t]*(CONFIG_FORMAT|#[ \t]*config_format)=v/{s/.*=v//;p;:1 n;b1;}' if [ -n "${1}" ] then ${SED_CMD} -En "${conf_form_sed_expr}" "${1}" else ${SED_CMD} -En "${conf_form_sed_expr}" fi } # Get GitHub ref and tarball url for specified component, update channel, branch and version # 1 - update channel: release|snapshot|branch=|commit= # 2 - version (optional): [version|commit_hash] # Output via variables: # $3 - github ref (version/commit hash), $4 - tarball url, $5 - version type ('version' or 'commit') get_gh_ref() { set_res_vars() { # validate resulting ref case "${gr_ref}" in *[^"${_NL_}"]*"${_NL_}"*[^"${_NL_}"]*) reg_failure "Got multiple download URLs for version '${gr_version}'." \ "If using commit hash, please specify the complete commit hash string." return 1 ;; ''|*[!a-zA-Z0-9._-]*) reg_failure "Failed to get GitHub download URL for ${gr_ver_type} '${gr_version}' (update channel: '${gr_channel}')." return 1 esac case "${gr_channel}" in release|latest) gr_channel=release gr_version="${gr_ref#v}" ;; *) gr_version="${gr_ref}" esac eval "${3}"='${gr_version}' "${4}"='${ABL_GH_URL_API}/tarball/${gr_ref}' "${5}"='${gr_ver_type}' \ "prev_ref"='${gr_ref}' "prev_ver_type"='${gr_ver_type}' \ "prev_upd_channel"='${gr_channel}' "prev_version"='${gr_version}' } get_and_process_ref() { local branch url \ channel="${1}" ptrn="${2}" branches="${3}" err_file="${4}" case "${channel}" in release) uclient-fetch "${ABL_GH_URL_API}/releases" -O- 2> "${err_file}" | { jsonfilter -e '@[@.prerelease=false]' | jsonfilter -a -e "@[@.target_commitish=\"${ABL_MAIN_BRANCH}\"].tag_name" cat 1>/dev/null } ;; snapshot|branch=*|commit=*) for branch in ${branches} do url="${ABL_GH_URL_API}/commits?sha=${branch}" uclient-fetch "${url}" -O- 2> "${err_file}" | { jsonfilter -e '@[@.commit]["url"]' | ${SED_CMD} 's/.*\///' # only leave the commit hash cat 1>/dev/null } done esac | if [ -n "${ptrn}" ] then grep "${ptrn}" else head -n1 # get latest version or commit cat 1>/dev/null fi } local gr_branches='' gr_grep_ptrn='' gr_ref='' gr_ver_type='' gr_fetch_rv=0 \ gr_fetch_tmp_dir="${ABL_UPD_DIR}/ref_fetch" \ prev_ref prev_ver_type prev_upd_channel prev_version \ gr_channel="${1}" gr_version="${2}" [ "$gr_channel" = release ] && gr_version="${gr_version#v}" local gr_ucl_err_file="${gr_fetch_tmp_dir}/ucl_err" are_var_names_safe "${3}" "${4}" "${5}" || return 1 eval "${3}='' ${4}='' ${5}=''" eval "prev_ref=\"\${prev_ref}\" prev_ver_type=\"\${prev_ver_type}\" prev_upd_channel=\"\${prev_upd_channel}\" prev_version=\"\${prev_version}\"" # if commit hash is specified and it's 40-char long, use it directly without API query or cache check case "${gr_channel}" in snapshot|branch=*|commit=*) [ "${#gr_version}" = 40 ] && gr_ref="${gr_version}" esac # if previously stored data exists, use it without API query or cache check if [ -z "${gr_ref}" ] && [ -n "${prev_ref}" ] && [ -n "${prev_ver_type}" ] && \ [ "${prev_upd_channel}" = "${gr_channel}" ] && [ "${gr_version}" = "${prev_version}" ] then gr_ref="${prev_ref}" gr_ver_type="${prev_ver_type}" elif [ -z "${gr_ref}" ] then # ref cache local cache_ttl cache_file cache_filename="${gr_version}_${gr_channel}" gr_cache_dir="/tmp/abl_cache" case "${gr_channel}" in commit=*) cache_ttl=2880 ;; # 48 hours *) cache_ttl=10 # 10 minutes esac # clean up old cache find "${gr_cache_dir:-?}" -maxdepth 1 -type f -mmin +"${cache_ttl}" -exec rm -f {} \; 2>/dev/null # check if the query is cached cache_file="$(find "${gr_cache_dir:-?}" -maxdepth 1 -type f -name "${cache_filename}" -print 2>/dev/null)" case "${cache_file}" in '') ;; # found nothing *[^"${_NL_}"]*"${_NL_}"*[^"${_NL_}"]*) # found multiple files - delete them local file IFS="${_NL_}" for file in ${cache_file} do [ -n "${file}" ] || continue rm -f "${file}" done IFS="${DEFAULT_IFS}" ;; *) # found cached query if [ -z "${IGNORE_CACHE}" ] && [ -f "${cache_file}" ] && read -r prev_ref prev_ver_type < "${cache_file}" && [ -n "${prev_ref}" ] && [ -n "${prev_ver_type}" ] then gr_ref="${prev_ref}" gr_ver_type="${prev_ver_type}" else rm -f "${cache_file:-???}" fi esac fi if [ -n "${gr_ref}" ] then set_res_vars "${@}" || return 1 return 0 fi try_mkdir -p "${gr_fetch_tmp_dir}" || return 1 rm -f "${gr_ucl_err_file}" case "${gr_channel}" in release) gr_ver_type=version [ -n "${gr_version}" ] && gr_grep_ptrn="^v${gr_version#v}$" ;; snapshot) gr_ver_type=commit gr_branches="${ABL_MAIN_BRANCH}" [ -n "${gr_version}" ] && gr_grep_ptrn="^${gr_version}$" ;; branch=*) gr_ver_type=commit gr_branches="${gr_channel#*=}" [ -n "${gr_version}" ] && gr_grep_ptrn="^${gr_version}$" ;; commit=*) gr_ver_type=commit local gr_hash="${gr_channel#*=}" if [ "${#gr_hash}" = 40 ] then # if upd. ch. is 'commit', the upd. ch. string includes commit hash - # if it's 40-char long, use it directly without API query gr_ref="${gr_hash}" else gr_branches="$( uclient-fetch "${ABL_GH_URL_API}/branches" -O- 2> "${gr_ucl_err_file}" | { jsonfilter -e '@[@]["name"]'; cat 1>/dev/null; } )" [ -n "${gr_branches}" ] || { reg_failure "Failed to get adblock-lean branches via GH API (url: '${ABL_GH_URL_API}/branches')." [ -f "${gr_ucl_err_file}" ] && log_msg "uclient-fetch log:${_NL_}$(cat "${gr_ucl_err_file}")" rm -f "${gr_ucl_err_file}" return 1 } rm -f "${gr_ucl_err_file}" gr_grep_ptrn="^${gr_hash}" fi ;; *) reg_failure "Invalid update channel '${gr_channel}'." return 1 esac # Get GH ref [ -z "${gr_ref}" ] && gr_ref="$(get_and_process_ref "${gr_channel}" "${gr_grep_ptrn}" "${gr_branches}" "${gr_ucl_err_file}")" if [ -z "${gr_ref}" ] then gr_fetch_rv=1 reg_failure "Failed to get GitHub download URL for ${gr_ver_type} '${gr_version}' (update channel: '${gr_channel}')." [ -f "${gr_ucl_err_file}" ] && log_msg "uclient-fetch output:${_NL_}$(cat "${gr_ucl_err_file}")" fi rm -rf "${gr_fetch_tmp_dir:-?}" [ "$gr_fetch_rv" = 0 ] || return 1 # write query result to cache try_mkdir -p "${gr_cache_dir}" && printf '%s\n' "${gr_ref} ${gr_ver_type}" > "${gr_cache_dir}/${cache_filename}" set_res_vars "${@}" || return 1 : } # Fetches and unpacks adblock-lean distribution # 1 - tarball url # 2 - distribution directory fetch_abl_dist() { [ -n "${1}" ] && [ -n "${2}" ] || { reg_failure "fetch_abl_dist: missing arguments."; return 1; } local tarball_url_fetch="${1}" dist_dir_fetch="${2}" local fetch_rv extract_dir fetch_dir="${dist_dir_fetch}/fetch" local tarball="${fetch_dir}/remote_abl.tar.gz" ucl_err_file="${fetch_dir}/ucl_err" \ rm -f "${ucl_err_file}" "${tarball}" rm -rf "${fetch_dir}/${ABL_REPO_AUTHOR}-adblock-lean-"* try_mkdir -p "${fetch_dir}" || return 1 uclient-fetch "${tarball_url_fetch}" -O "${tarball}" 2> "${ucl_err_file}" && grep -q "Download completed" "${ucl_err_file}" && tar -C "${fetch_dir}" -xzf "${tarball}" && extract_dir="$(find "${fetch_dir}/" -type d -name "${ABL_REPO_AUTHOR}-adblock-lean-*")" && [ -n "${extract_dir}" ] && [ "${extract_dir}" != "/" ] fetch_rv=${?} rm -f "${tarball}" [ "${fetch_rv}" != 0 ] && [ -s "${ucl_err_file}" ] && log_msg "uclient-fetch output: ${_NL_}$(cat "${ucl_err_file}")." rm -f "${ucl_err_file}" [ "${fetch_rv}" = 0 ] && { mv "${extract_dir:-?}"/* "${dist_dir_fetch:-?}/" || { rm -rf "${extract_dir:-?}"; reg_failure "Failed to move files to dist dir."; return 1; } } rm -rf "${extract_dir:-?}" "${fetch_dir:-?}" return ${fetch_rv} } clean_abl_env() { unset action ABL_CMD ABL_LIB_FILES ABL_EXTRA_FILES ABL_EXEC_FILES LIBS_SOURCED CONFIG_FORMAT unset -f abl_post_update_1 abl_post_update_2 load_config update source_libs check_libs install_abl_files cleanup_and_exit } # Prints file list from adblock-lean service file # 1 - file path # 2 - file types (EXEC|ALL) get_file_list() { clean_abl_env local _file_types="${2}" # shellcheck source=/dev/null [ -f "${1}" ] && . "${1}" || return 1 if check_func print_file_list # v0.7.2 and later then print_file_list "${_file_types}" elif check_func install_abl_files # v0.6.0-v0.7.1 then case "${_file_types}" in EXEC) printf '%s\n' "${ABL_SERVICE_PATH}" ;; *) printf '%s\n' "${ABL_SERVICE_PATH}${_NL_}${ABL_LIB_FILES}${_NL_}${ABL_EXTRA_FILES}" | ${SED_CMD} 's/\s\s*/\n/g' | ${SED_CMD} '/^$/d' esac else # v0.5.4 and earlier printf '%s\n' "${ABL_SERVICE_PATH}" fi : } # 1 - path to distribution dir # 2 - version # 3 - update channel # 4 - force file list install_abl_files() { local file preinst_path old_files='' exec_files='' \ preinst_reg_file="${dist_dir}/preinst_reg.md5" \ prev_config_format='' upd_config_format='' config_format_changed='' \ dist_dir="${1}" version="${2}" upd_channel="${3}" new_file_list="${4}" [ -n "${1}" ] && [ -n "${2}" ] && [ -n "${3}" ] || inst_failed "Missing arguments." log_msg "" "Installing new files..." # normalize path try_mkdir -p "${dist_dir}${ABL_SERVICE_PATH%/*}" mv "${dist_dir}/adblock-lean" "${dist_dir}${ABL_SERVICE_PATH}" || inst_failed # get new file list if [ -z "${new_file_list}" ] then new_file_list="$(get_file_list "${dist_dir}${ABL_SERVICE_PATH}" ALL)" && [ -n "${new_file_list}" ] || inst_failed "Failed to get the file list from fetched adblock-lean version." fi printf '%s\n' "${new_file_list}" > "${dist_dir}/new_file_list" # check new files for file in ${new_file_list} do [ -z "${file}" ] || [ -f "${dist_dir}${file}" ] && continue inst_failed "Missing file: '${dist_dir}${file}'." done # get new exec file list exec_files="$(get_file_list "${dist_dir}${ABL_SERVICE_PATH}" EXEC)" # handle update if [ -n "${IS_UPDATE}" ] then # get currently installed file list old_files="$(get_file_list "${ABL_SERVICE_PATH}" ALL)" prev_config_format="$(get_config_format < "${ABL_SERVICE_PATH}")" upd_config_format="$(get_config_format < "${dist_dir}${ABL_SERVICE_PATH}")" [ -n "${upd_config_format}" ] && [ -n "${prev_config_format}" ] && \ [ "${upd_config_format}" != "${prev_config_format}" ] && config_format_changed=1 local IFS="${_NL_}" # delete obsolete files for file in ${old_files} do case "${file}" in /*) ;; *) continue; esac # only accept absolute paths if [ -f "${file}" ] && ! is_included "${file}" "${new_file_list}" "${_NL_}" then log_msg "Deleting obsolete file ${file}." rm -f "${file}" fi done IFS="${DEFAULT_IFS}" ( clean_abl_env # shellcheck source=/dev/null if . "${dist_dir}${ABL_SERVICE_PATH}" && check_func abl_post_update_1 then abl_post_update_1 fi ) fi # version and update channel string replacement busybox sed -i " /^\s*ABL_VERSION\s*=/{s/.*/ABL_VERSION=\"${version}\"/;} /^\s*ABL_UPD_CHANNEL\s*=/{s/.*/ABL_UPD_CHANNEL=\"${upd_channel}\"/;}" \ "${dist_dir}${ABL_SERVICE_PATH}" # Check for changed files local changed_files='' unchanged_files='' man_changed_files='' if [ -s "${ABL_FILES_REG_PATH}" ] then # prefix file paths in the reg file for md5sum comparison ${SED_CMD} -E "/^$/d;s~([^ ]+$)~${dist_dir}\\1~" "${ABL_FILES_REG_PATH}" > "${preinst_reg_file}" # Detect unchanged files md5sum -c "${preinst_reg_file}" 2>/dev/null | ${SED_CMD} -n "/:\s*OK\s*$/{s/\s*:\s*OK\s*$//;s~^\s*${dist_dir}~~;p;}" > "${dist_dir}/unchanged" rm -f "${preinst_reg_file}" # Detect manually modified files man_changed_files="$(md5sum -c "${ABL_FILES_REG_PATH}" 2>/dev/null | ${SED_CMD} -n "/:\s*FAILED\s*$/{s/\s*:\s*FAILED\s*$//;p;}")" # Remove manually modified files from unchanged files if [ -n "${man_changed_files}" ] then unchanged_files="$( printf '%s\n' "${man_changed_files}" | busybox awk ' NR==FNR {man_ch[$0];next} { if ($0=="" || $0 in man_ch) {next} print $0 } ' - "${dist_dir}/unchanged" )" else unchanged_files="$(cat "${dist_dir}/unchanged")" fi rm -f "${dist_dir}/unchanged" # remove unchanged files from ${new_file_list} to reliably get a list of files to copy changed_files="$( printf '%s\n' "${unchanged_files}" | busybox awk ' NR==FNR {unch[$0];next} { if ($0=="" || $0 in unch) {next} print $0 } ' - "${dist_dir}/new_file_list" )" else changed_files="${new_file_list}" fi local IFS="${_NL_}" for file in ${unchanged_files} do [ -n "${file}" ] || continue log_msg "File '${file}' did not change - not updating." done local mod_files_bk_dir="/tmp/abl_old_modified_files" for file in ${man_changed_files} do [ -n "${file}" ] && [ -f "${file}" ] || continue log_msg "Warning: File '${file}' was manually modified - overwriting." if try_mkdir -p "${mod_files_bk_dir}" && cp "${file}" "${mod_files_bk_dir}/${file##*/}" then log_msg "Saved a backup copy of manually modified file to ${mod_files_bk_dir}/${file##*/}" else log_msg "Warning: Can not create a backup copy of manually modified file '${file}' - overwriting anyway." fi done # Copy changed files for file in ${changed_files} do preinst_path="${dist_dir}${file}" log_msg "Copying file '${file}'." try_mkdir -p "${file%/*}" && cp "${preinst_path}" "${file}" || inst_failed "Failed to copy file '${preinst_path}' to '${file}'." done # make files executable [ -n "${exec_files}" ] && { set -- ${exec_files} # relying on IFS=\n for file in "${@}" do [ -n "${file}" ] || continue chmod +x "${file}" || inst_failed "Failed to make file '$file' executable." done } # save the md5sum registry file if needed if [ -n "${changed_files}" ] || [ ! -s "${ABL_FILES_REG_PATH}" ] then # make md5sum registry of new files # relying on IFS=\n # shellcheck disable=SC2046 set -- $( printf '%s\n' "${new_file_list}" | busybox sed "/^$/d;s~^\s*~${dist_dir}~" ) && md5sums="$(md5sum "$@")" && [ -n "${md5sums}" ] && try_mkdir -p "${ABL_FILES_REG_PATH%/*}" && printf '%s\n' "${md5sums}" | busybox sed "s~\s${dist_dir}~ ~" > "${ABL_FILES_REG_PATH}" || inst_failed "Failed to register new files." fi IFS="${DEFAULT_IFS}" if [ -n "${IS_UPDATE}" ] && grep -m1 -q '[ ]*abl_post_update_2()' "${dist_dir}${ABL_SERVICE_PATH}" then ( clean_abl_env # shellcheck source=/dev/null if . "${dist_dir}${ABL_SERVICE_PATH}" && check_func abl_post_update_2 then abl_post_update_2 fi ) fi if [ -n "${config_format_changed}" ] then ( clean_abl_env failsafe_log "${_NL_}NOTE: config format has changed from v${prev_config_format} to v${upd_config_format}." export ABL_IN_INSTALL=1 # load config in new version # shellcheck source=/dev/null if . "${ABL_SERVICE_PATH}" && { ! check_func source_libs || source_libs; } && check_func load_config && load_config then : else failsafe_log "Please run 'service adblock-lean start' to initialize the new config." fi : ) fi ABL_IN_INSTALL= : } fetch_and_install() { trap 'cleanup_and_exit 1' INT TERM trap 'cleanup_and_exit ${?}' EXIT # unset vars and functions from current version to have a clean slate with the new version fetch_failed() { local fail_msg="${1}" [ -s "${UCL_ERR_FILE}" ] && fail_msg="${fail_msg} uclient-fetch errors: '$(cat "${UCL_ERR_FILE}")'" [ -n "${fail_msg}" ] && reg_failure "${fail_msg}" rm -rf "${ABL_UPD_DIR:-???}" "${ABL_PID_DIR:-???}" "${UCL_ERR_FILE:-???}" inst_failed } unexp_arg() { fetch_failed "fetch_and_install: unexpected argument '${1}'." } # v0.7.2 and earlier versions are incompatible with config v9 or later rm_incompat_config() { [ -s "${ABL_CONFIG_FILE}" ] || return 0 local old_format='' old_config_f="/tmp/adblock-lean_config.old" if old_format="$(get_config_format "${ABL_CONFIG_FILE}")" && [ -n "${old_format}" ] && [ "${old_format}" -ge 9 ] then log_msg "" "Warning: Version downgrade detected - removing incompatible config." if ! cp "${ABL_CONFIG_FILE}" "${old_config_f}" then reg_failure "Failed to save old config file as ${old_config_f}." else log_msg "Old config file was saved as ${old_config_f}." "" fi rm -f "${ABL_CONFIG_FILE}" fi } local file req_ver='' ver_str_arg='' ver_type='' dist_dir='' upd_ver='' tarball_url='' \ upd_channel='' req_upd_channel='' force_upd_channel='' IGNORE_CACHE= while getopts ":s:v:U:W:i" opt do case ${opt} in s) export sim_path="$OPTARG" ;; v) ver_str_arg=$OPTARG ;; U) force_upd_channel=$OPTARG ;; W) req_ver=$OPTARG ;; i) IGNORE_CACHE=1 ;; # global var *) unexp_arg "$OPTARG" esac done shift $((OPTIND-1)) [ -z "${*}" ] || unexp_arg "${*}" # parse version string from arguments into $req_upd_channel, $req_ver case "${ver_str_arg}" in '') ;; release|latest) req_upd_channel=release req_ver='' ;; snapshot) req_upd_channel="${ver_str_arg}" req_ver='' ;; commit=*) req_upd_channel="${ver_str_arg}" req_ver="${ver_str_arg#*=}" ;; branch=*) req_upd_channel="${ver_str_arg}" req_ver='' ;; [0-9]*|v[0-9]*) req_upd_channel=release req_ver="${ver_str_arg#*=}" req_ver="${req_ver#v}" ;; *) fetch_failed "Invalid version string '${ver_str_arg}'." esac if ${ABL_SERVICE_PATH} enabled then DO_DIALOGS=0 ${ABL_SERVICE_PATH} stop fi 2>/dev/null rm -rf "${ABL_UPD_DIR:-???}" try_mkdir -p "${ABL_UPD_DIR}" || fetch_failed upd_channel="${req_upd_channel:-"${ABL_UPD_CHANNEL}"}" upd_channel="${force_upd_channel:-"${upd_channel}"}" upd_channel="${upd_channel:-"release"}" dist_dir="${ABL_UPD_DIR}/dist" try_mkdir -p "${dist_dir}" || fetch_failed if [ -n "${sim_path}" ] then print_msg "Installing in simulation mode." [ -d "${sim_path}" ] || fetch_failed "Update simulation directory '${sim_path}' does not exist." [ -n "${ver_str_arg}" ] || fetch_failed "Specify new version string." upd_ver="${ver_str_arg}" [ -d "${sim_path}" ] || fetch_failed "Simulation source directory doesn't exist." cp -rT "${sim_path}" "${dist_dir}" log_msg "" "Installing adblock-lean version '${upd_ver}' (update channel: '${upd_channel}')." else get_gh_ref "${upd_channel}" "${req_ver}" upd_ver tarball_url ver_type || fetch_failed case "${upd_channel}" in commit=*) # set update channel to 'commit=' upd_channel="${upd_channel%=*}=${upd_ver}" esac log_msg "" "Downloading adblock-lean, ${ver_type} '${upd_ver}' (update channel: '${upd_channel}')." fetch_abl_dist "${tarball_url}" "${dist_dir}" || fetch_failed fi get_abl_version "${dist_dir}/adblock-lean" ( local gv_rv=${?} case "${gv_rv}" in 2) # no version found - call install_abl_files() from this installer rm_incompat_config export pid_file="/tmp/adblock-lean/adblock-lean.pid" # for compatibility with older versions rm -f "${ABL_FILES_REG_PATH}" install_abl_files "${dist_dir}" "${upd_ver}" "${upd_channel}" "${ABL_SERVICE_PATH}" ;; 3) # old version format - call install_abl_files() from fetched service file rm_incompat_config clean_abl_env # shellcheck source=/dev/null . "${dist_dir}/adblock-lean" || { reg_failure "Failed to source fetched script."; exit 1; } printf '%s\n' "${ABL_SERVICE_PATH} ${ABL_LIB_FILES} ${ABL_EXTRA_FILES}" > "${dist_dir}/inst_files" rm -f "${ABL_FILES_REG_PATH}" install_abl_files "${dist_dir}" "${upd_channel}_v${upd_ver}" ;; 4|5) # new version format - call install_abl_files() from fetched installer [ "${gv_rv}" = 4 ] && rm_incompat_config clean_abl_env # shellcheck source=/dev/null INST_SOURCED=1 . "${dist_dir}/abl-install.sh" || { reg_failure "Failed to source fetched install script."; exit 1; } install_abl_files "${dist_dir}" "${upd_ver}" "${upd_channel}" ;; *) reg_failure "Failed to get version from fetched adblock-lean distribution."; false ;; esac ) || exit 1 rm -rf "${ABL_UPD_DIR:-???}" "${ABL_PID_DIR:-???}" "${UCL_ERR_FILE:-???}" log_msg "" "adblock-lean (version '${upd_ver}') has been installed." if [ "${DO_DIALOGS}" = 1 ] then if [ -n "${IS_UPDATE}" ] && [ -s "${ABL_CONFIG_FILE}" ] then print_msg "" "Start adblock-lean now? (y|n)" pick_opt "y|n" if [ "$REPLY" = y ] then clean_abl_env # shellcheck source=/dev/null . "${ABL_SERVICE_PATH}" || exit 1 enable && start else exit 0 fi else print_msg "" "Set up adblock-lean now? (y|n)" pick_opt "y|n" if [ "$REPLY" = y ] then clean_abl_env # shellcheck source=/dev/null . "${ABL_SERVICE_PATH}" || exit 1 setup else exit 0 fi fi fi } [ -s "${ABL_SERVICE_PATH}" ] && IS_UPDATE=1 if [ -z "${INST_SOURCED}" ] then fetch_and_install "${@}" else : fi