mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
457 lines
16 KiB
Bash
457 lines
16 KiB
Bash
#!/bin/bash
|
|
|
|
YNH_APP_BASEDIR=${YNH_APP_BASEDIR:-$(realpath ..)}
|
|
|
|
# Handle script crashes / failures
|
|
#
|
|
# [internal]
|
|
#
|
|
# usage:
|
|
# ynh_exit_properly is used only by the helper ynh_abort_if_errors.
|
|
# You should not use it directly.
|
|
# Instead, add to your script:
|
|
# ynh_clean_setup () {
|
|
# instructions...
|
|
# }
|
|
#
|
|
# This function provide a way to clean some residual of installation that not managed by remove script.
|
|
#
|
|
# It prints a warning to inform that the script was failed, and execute the ynh_clean_setup function if used in the app script
|
|
#
|
|
# Requires YunoHost version 2.6.4 or higher.
|
|
ynh_exit_properly() {
|
|
local exit_code=$?
|
|
|
|
if [[ "${YNH_APP_ACTION:-}" =~ ^install$|^upgrade$|^restore$ ]]
|
|
then
|
|
rm -rf "/var/cache/yunohost/download/"
|
|
fi
|
|
|
|
if [ "$exit_code" -eq 0 ]; then
|
|
exit 0 # Exit without error if the script ended correctly
|
|
fi
|
|
|
|
trap '' EXIT # Ignore new exit signals
|
|
# Do not exit anymore if a command fail or if a variable is empty
|
|
set +o errexit # set +e
|
|
set +o nounset # set +u
|
|
|
|
# Small tempo to avoid the next message being mixed up with other DEBUG messages
|
|
sleep 0.5
|
|
|
|
if type -t ynh_clean_setup >/dev/null; then # Check if the function exist in the app script.
|
|
ynh_clean_setup # Call the function to do specific cleaning for the app.
|
|
fi
|
|
|
|
# Exit with error status
|
|
# We don't call ynh_die basically to avoid unecessary 10-ish
|
|
# debug lines about parsing args and stuff just to exit 1..
|
|
exit 1
|
|
}
|
|
|
|
# Exits if an error occurs during the execution of the script.
|
|
#
|
|
# [packagingv1]
|
|
#
|
|
# usage: ynh_abort_if_errors
|
|
#
|
|
# This configure the rest of the script execution such that, if an error occurs
|
|
# or if an empty variable is used, the execution of the script stops immediately
|
|
# and a call to `ynh_clean_setup` is triggered if it has been defined by your script.
|
|
#
|
|
# Requires YunoHost version 2.6.4 or higher.
|
|
ynh_abort_if_errors() {
|
|
set -o errexit # set -e; Exit if a command fail
|
|
set -o nounset # set -u; And if a variable is used unset
|
|
trap ynh_exit_properly EXIT # Capturing exit signals on shell script
|
|
}
|
|
|
|
# When running an app script with packaging format >= 2, auto-enable ynh_abort_if_errors except for remove script
|
|
if [[ "${YNH_CONTEXT:-}" != "regenconf" ]] && dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} ge 2 && [[ ${YNH_APP_ACTION} != "remove" ]]
|
|
then
|
|
ynh_abort_if_errors
|
|
fi
|
|
|
|
# Curl abstraction to help with POST requests to local pages (such as installation forms)
|
|
#
|
|
# usage: ynh_local_curl "page_uri" "key1=value1" "key2=value2" ...
|
|
# | arg: page_uri - Path (relative to `$path_url`) of the page where POST data will be sent
|
|
# | arg: key1=value1 - (Optionnal) POST key and corresponding value
|
|
# | arg: key2=value2 - (Optionnal) Another POST key and corresponding value
|
|
# | arg: ... - (Optionnal) More POST keys and values
|
|
#
|
|
# example: ynh_local_curl "/install.php?installButton" "foo=$var1" "bar=$var2"
|
|
#
|
|
# For multiple calls, cookies are persisted between each call for the same app
|
|
#
|
|
# `$domain` and `$path_url` should be defined externally (and correspond to the domain.tld and the /path (of the app?))
|
|
#
|
|
# Requires YunoHost version 2.6.4 or higher.
|
|
ynh_local_curl() {
|
|
# Define url of page to curl
|
|
local local_page=$(ynh_normalize_url_path $1)
|
|
local full_path=$path_url$local_page
|
|
|
|
if [ "${path_url}" == "/" ]; then
|
|
full_path=$local_page
|
|
fi
|
|
|
|
local full_page_url=https://localhost$full_path
|
|
|
|
# Concatenate all other arguments with '&' to prepare POST data
|
|
local POST_data=""
|
|
local arg=""
|
|
for arg in "${@:2}"; do
|
|
POST_data="${POST_data}${arg}&"
|
|
done
|
|
if [ -n "$POST_data" ]; then
|
|
# Add --data arg and remove the last character, which is an unecessary '&'
|
|
POST_data="--data ${POST_data::-1}"
|
|
fi
|
|
|
|
# Wait untils nginx has fully reloaded (avoid curl fail with http2)
|
|
sleep 2
|
|
|
|
local cookiefile=/tmp/ynh-$app-cookie.txt
|
|
touch $cookiefile
|
|
chown root $cookiefile
|
|
chmod 700 $cookiefile
|
|
|
|
# Temporarily enable visitors if needed...
|
|
local visitors_enabled=$(ynh_permission_has_user "main" "visitors" && echo yes || echo no)
|
|
if [[ $visitors_enabled == "no" ]]; then
|
|
ynh_permission_update --permission "main" --add "visitors"
|
|
fi
|
|
|
|
# Curl the URL
|
|
curl --silent --show-error --insecure --location --header "Host: $domain" --resolve $domain:443:127.0.0.1 $POST_data "$full_page_url" --cookie-jar $cookiefile --cookie $cookiefile
|
|
|
|
if [[ $visitors_enabled == "no" ]]; then
|
|
ynh_permission_update --permission "main" --remove "visitors"
|
|
fi
|
|
}
|
|
|
|
# Fetch the Debian release codename
|
|
#
|
|
# [packagingv1]
|
|
#
|
|
# usage: ynh_get_debian_release
|
|
# | ret: The Debian release codename (i.e. jessie, stretch, ...)
|
|
#
|
|
# Requires YunoHost version 2.7.12 or higher.
|
|
ynh_get_debian_release() {
|
|
echo $(lsb_release --codename --short)
|
|
}
|
|
|
|
_acceptable_path_to_delete() {
|
|
local file=$1
|
|
|
|
local forbidden_paths=$(ls -d / /* /{var,home,usr}/* /etc/{default,sudoers.d,yunohost,cron*} /etc/yunohost/{apps,domains,hooks.d} /opt/yunohost 2> /dev/null)
|
|
|
|
# Legacy : A couple apps still have data in /home/$app ...
|
|
if [[ -n "${app:-}" ]]
|
|
then
|
|
forbidden_paths=$(echo "$forbidden_paths" | grep -v "/home/$app")
|
|
fi
|
|
|
|
# Use realpath to normalize the path ..
|
|
# i.e convert ///foo//bar//..///baz//// to /foo/baz
|
|
file=$(realpath --no-symlinks "$file")
|
|
if [ -z "$file" ] || grep -q -x -F "$file" <<< "$forbidden_paths"; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Remove a file or a directory securely
|
|
#
|
|
# usage: ynh_secure_remove --file=path_to_remove
|
|
# | arg: -f, --file= - File or directory to remove
|
|
#
|
|
# Requires YunoHost version 2.6.4 or higher.
|
|
ynh_secure_remove() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=f
|
|
local -A args_array=([f]=file=)
|
|
local file
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
set +o xtrace # set +x
|
|
|
|
if [ $# -ge 2 ]; then
|
|
ynh_print_warn --message="/!\ Packager ! You provided more than one argument to ynh_secure_remove but it will be ignored... Use this helper with one argument at time."
|
|
fi
|
|
|
|
if [[ -z "$file" ]]; then
|
|
ynh_print_warn --message="ynh_secure_remove called with empty argument, ignoring."
|
|
elif [[ ! -e $file ]]; then
|
|
ynh_print_info --message="'$file' wasn't deleted because it doesn't exist."
|
|
elif ! _acceptable_path_to_delete "$file"; then
|
|
ynh_print_warn --message="Not deleting '$file' because it is not an acceptable path to delete."
|
|
else
|
|
rm --recursive "$file"
|
|
fi
|
|
|
|
set -o xtrace # set -x
|
|
}
|
|
|
|
# Read the value of a key in a ynh manifest file
|
|
#
|
|
# usage: ynh_read_manifest --manifest="manifest.json" --manifest_key="key"
|
|
# | arg: -m, --manifest= - Path of the manifest to read
|
|
# | arg: -k, --manifest_key= - Name of the key to find
|
|
# | ret: the value associate to that key
|
|
#
|
|
# Requires YunoHost version 3.5.0 or higher.
|
|
ynh_read_manifest() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=mk
|
|
local -A args_array=([m]=manifest= [k]=manifest_key=)
|
|
local manifest
|
|
local manifest_key
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
|
|
if [ ! -e "${manifest:-}" ]; then
|
|
# If the manifest isn't found, try the common place for backup and restore script.
|
|
if [ -e "$YNH_APP_BASEDIR/manifest.json" ]
|
|
then
|
|
manifest="$YNH_APP_BASEDIR/manifest.json"
|
|
elif [ -e "$YNH_APP_BASEDIR/manifest.toml" ]
|
|
then
|
|
manifest="$YNH_APP_BASEDIR/manifest.toml"
|
|
else
|
|
ynh_die --message "No manifest found !?"
|
|
fi
|
|
fi
|
|
|
|
if echo "$manifest" | grep -q '\.json$'
|
|
then
|
|
jq ".$manifest_key" "$manifest" --raw-output
|
|
else
|
|
cat "$manifest" | python3 -c 'import json, toml, sys; print(json.dumps(toml.load(sys.stdin)))' | jq ".$manifest_key" --raw-output
|
|
fi
|
|
}
|
|
|
|
# Read the upstream version from the manifest or `$YNH_APP_MANIFEST_VERSION`
|
|
#
|
|
# usage: ynh_app_upstream_version [--manifest="manifest.json"]
|
|
# | arg: -m, --manifest= - Path of the manifest to read
|
|
# | ret: the version number of the upstream app
|
|
#
|
|
# If the `manifest` is not specified, the envvar `$YNH_APP_MANIFEST_VERSION` will be used.
|
|
#
|
|
# The version number in the manifest is defined by `<upstreamversion>~ynh<packageversion>`.
|
|
#
|
|
# For example, if the manifest contains `4.3-2~ynh3` the function will return `4.3-2`
|
|
#
|
|
# Requires YunoHost version 3.5.0 or higher.
|
|
ynh_app_upstream_version() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=m
|
|
local -A args_array=([m]=manifest=)
|
|
local manifest
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
manifest="${manifest:-}"
|
|
|
|
if [[ "$manifest" != "" ]] && [[ -e "$manifest" ]]; then
|
|
version_key_=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version")
|
|
else
|
|
version_key_=$YNH_APP_MANIFEST_VERSION
|
|
fi
|
|
|
|
echo "${version_key_/~ynh*/}"
|
|
}
|
|
|
|
# Read package version from the manifest
|
|
#
|
|
# [internal]
|
|
#
|
|
# usage: ynh_app_package_version [--manifest="manifest.json"]
|
|
# | arg: -m, --manifest= - Path of the manifest to read
|
|
# | ret: the version number of the package
|
|
#
|
|
# The version number in the manifest is defined by `<upstreamversion>~ynh<packageversion>`.
|
|
#
|
|
# For example, if the manifest contains `4.3-2~ynh3` the function will return `3`
|
|
#
|
|
# Requires YunoHost version 3.5.0 or higher.
|
|
ynh_app_package_version() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=m
|
|
local -A args_array=([m]=manifest=)
|
|
local manifest
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
|
|
version_key_=$YNH_APP_MANIFEST_VERSION
|
|
echo "${version_key_/*~ynh/}"
|
|
}
|
|
|
|
# Checks the app version to upgrade with the existing app version and returns:
|
|
#
|
|
# usage: ynh_check_app_version_changed
|
|
# | ret: `UPGRADE_APP` if the upstream version changed, `UPGRADE_PACKAGE` otherwise.
|
|
#
|
|
# This helper should be used to avoid an upgrade of an app, or the upstream part
|
|
# of it, when it's not needed
|
|
#
|
|
# Requires YunoHost version 3.5.0 or higher.
|
|
ynh_check_app_version_changed() {
|
|
local return_value=${YNH_APP_UPGRADE_TYPE}
|
|
|
|
if [ "$return_value" == "UPGRADE_SAME" ] || [ "$return_value" == "DOWNGRADE" ]; then
|
|
return_value="UPGRADE_APP"
|
|
fi
|
|
|
|
echo $return_value
|
|
}
|
|
|
|
# Compare the current package version against another version given as an argument.
|
|
#
|
|
# usage: ynh_compare_current_package_version --comparison (lt|le|eq|ne|ge|gt) --version <X~ynhY>
|
|
# | arg: --comparison - Comparison type. Could be : `lt` (lower than), `le` (lower or equal), `eq` (equal), `ne` (not equal), `ge` (greater or equal), `gt` (greater than)
|
|
# | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like `2.3.1~ynh4`)
|
|
# | ret: 0 if the evaluation is true, 1 if false.
|
|
#
|
|
# example: ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1
|
|
#
|
|
# This helper is usually used when we need to do some actions only for some old package versions.
|
|
#
|
|
# Generally you might probably use it as follow in the upgrade script :
|
|
# ```
|
|
# if ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1
|
|
# then
|
|
# # Do something that is needed for the package version older than 2.3.2~ynh1
|
|
# fi
|
|
# ```
|
|
#
|
|
# Requires YunoHost version 3.8.0 or higher.
|
|
ynh_compare_current_package_version() {
|
|
local legacy_args=cv
|
|
declare -Ar args_array=([c]=comparison= [v]=version=)
|
|
local version
|
|
local comparison
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
|
|
local current_version=$YNH_APP_CURRENT_VERSION
|
|
|
|
# Check the syntax of the versions
|
|
if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]]; then
|
|
ynh_die --message="Invalid argument for version."
|
|
fi
|
|
|
|
# Check validity of the comparator
|
|
if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then
|
|
ynh_die --message="Invalid comparator must be : lt, le, eq, ne, ge, gt"
|
|
fi
|
|
|
|
# Return the return value of dpkg --compare-versions
|
|
dpkg --compare-versions $current_version $comparison $version
|
|
}
|
|
|
|
# Check if we should enforce sane default permissions (= disable rwx for 'others')
|
|
# on file/folders handled with ynh_setup_source and ynh_add_config
|
|
#
|
|
# [internal]
|
|
#
|
|
# Having a file others-readable or a folder others-executable(=enterable)
|
|
# is a security risk comparable to "chmod 777"
|
|
#
|
|
# Configuration files may contain secrets. Or even just being able to enter a
|
|
# folder may allow an attacker to do nasty stuff (maybe a file or subfolder has
|
|
# some write permission enabled for 'other' and the attacker may edit the
|
|
# content or create files as leverage for priviledge escalation ...)
|
|
#
|
|
# The sane default should be to set ownership to $app:$app.
|
|
# In specific case, you may want to set the ownership to $app:www-data
|
|
# for example if nginx needs access to static files.
|
|
#
|
|
_ynh_apply_default_permissions() {
|
|
local target=$1
|
|
|
|
local ynh_requirement=$(ynh_read_manifest --manifest_key="requirements.yunohost" | tr -d '<>= ')
|
|
|
|
if dpkg --compare-versions ${YNH_APP_PACKAGING_FORMAT:-0} ge 2 || [ -z "$ynh_requirement" ] || [ "$ynh_requirement" == "null" ] || dpkg --compare-versions $ynh_requirement ge 4.2; then
|
|
chmod o-rwx $target
|
|
chmod g-w $target
|
|
chown -R root:root $target
|
|
if ynh_system_user_exists $app; then
|
|
chown $app:$app $target
|
|
fi
|
|
fi
|
|
|
|
# Crons should be owned by root
|
|
# Also we don't want systemd conf, nginx conf or others stuff to be owned by the app,
|
|
# otherwise they could self-edit their own systemd conf and escalate privilege
|
|
if grep -qE '^(/etc/cron|/etc/php|/etc/nginx/conf.d|/etc/fail2ban|/etc/systemd/system)' <<< "$target"
|
|
then
|
|
chmod 400 $target
|
|
chown root:root $target
|
|
fi
|
|
}
|
|
|
|
int_to_bool() {
|
|
sed -e 's/^1$/True/g' -e 's/^0$/False/g'
|
|
}
|
|
|
|
toml_to_json() {
|
|
python3 -c 'import toml, json, sys; print(json.dumps(toml.load(sys.stdin)))'
|
|
}
|
|
|
|
# Check if a YunoHost user exists
|
|
#
|
|
# usage: ynh_user_exists --username=username
|
|
# | arg: -u, --username= - the username to check
|
|
# | ret: 0 if the user exists, 1 otherwise.
|
|
#
|
|
# example: ynh_user_exists 'toto' || echo "User does not exist"
|
|
#
|
|
# Requires YunoHost version 2.2.4 or higher.
|
|
ynh_user_exists() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=u
|
|
local -A args_array=([u]=username=)
|
|
local username
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
|
|
yunohost user list --output-as json --quiet | jq -e ".users.\"${username}\"" >/dev/null
|
|
}
|
|
|
|
# Retrieve a YunoHost user information
|
|
#
|
|
# usage: ynh_user_get_info --username=username --key=key
|
|
# | arg: -u, --username= - the username to retrieve info from
|
|
# | arg: -k, --key= - the key to retrieve
|
|
# | ret: the value associate to that key
|
|
#
|
|
# example: mail=$(ynh_user_get_info --username="toto" --key=mail)
|
|
#
|
|
# Requires YunoHost version 2.2.4 or higher.
|
|
ynh_user_get_info() {
|
|
# Declare an array to define the options of this helper.
|
|
local legacy_args=uk
|
|
local -A args_array=([u]=username= [k]=key=)
|
|
local username
|
|
local key
|
|
# Manage arguments with getopts
|
|
ynh_handle_getopts_args "$@"
|
|
|
|
yunohost user info "$username" --output-as json --quiet | jq -r ".$key"
|
|
}
|
|
|
|
# Get the list of YunoHost users
|
|
#
|
|
# usage: ynh_user_list
|
|
# | ret: one username per line as strings
|
|
#
|
|
# example: for u in $(ynh_user_list); do ... ; done
|
|
#
|
|
# Requires YunoHost version 2.4.0 or higher.
|
|
ynh_user_list() {
|
|
yunohost user list --output-as json --quiet | jq -r ".users | keys[]"
|
|
}
|