Merge branch 'dev' into migrate_to_bullseye

This commit is contained in:
Alexandre Aubin 2021-10-13 14:58:29 +02:00
commit 480875f676
88 changed files with 4482 additions and 3697 deletions

2
.coveragerc Normal file
View file

@ -0,0 +1,2 @@
[report]
omit=src/yunohost/tests/*,src/yunohost/vendor/*,/usr/lib/moulinette/yunohost/*

View file

@ -36,7 +36,7 @@ full-tests:
- *install_debs
- yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns --force-diskspace
script:
- python3 -m pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml
- python3 -m pytest --cov=yunohost tests/ src/yunohost/tests/ data/hooks/diagnosis/ --junitxml=report.xml
- cd tests
- bash test_helpers.sh
needs:
@ -113,10 +113,10 @@ test-apps:
test-appscatalog:
extends: .test-stage
script:
- python3 -m pytest src/yunohost/tests/test_appscatalog.py
- python3 -m pytest src/yunohost/tests/test_app_catalog.py
only:
changes:
- src/yunohost/app.py
- src/yunohost/app_calalog.py
test-appurl:
extends: .test-stage

View file

@ -6,7 +6,9 @@
<div align="center">
![Version](https://img.shields.io/github/v/tag/yunohost/yunohost?label=version&sort=semver)
[![Build status](https://shields.io/gitlab/pipeline/yunohost/yunohost/dev)](https://gitlab.com/yunohost/yunohost/-/pipelines)
![Test coverage](https://img.shields.io/gitlab/coverage/yunohost/yunohost/dev)
[![GitHub license](https://img.shields.io/github/license/YunoHost/yunohost)](https://github.com/YunoHost/yunohost/blob/dev/LICENSE)
[![Mastodon Follow](https://img.shields.io/mastodon/follow/28084)](https://mastodon.social/@yunohost)

View file

@ -4,160 +4,152 @@
Pythonic declaration of mDNS .local domains for YunoHost
"""
import subprocess
import re
import sys
import yaml
import socket
from time import sleep
from typing import List, Dict
from zeroconf import Zeroconf, ServiceInfo
import ifaddr
from ipaddress import ip_address
from zeroconf import Zeroconf, ServiceInfo, ServiceBrowser
# Helper command taken from Moulinette
def check_output(args, stderr=subprocess.STDOUT, shell=True, **kwargs):
"""Run command with arguments and return its output as a byte string
Overwrite some of the arguments to capture standard error in the result
and use shell by default before calling subprocess.check_output.
def get_network_local_interfaces() -> Dict[str, Dict[str, List[str]]]:
"""
return (
subprocess.check_output(args, stderr=stderr, shell=shell, **kwargs)
.decode("utf-8")
.strip()
)
# Helper command taken from Moulinette
def _extract_inet(string, skip_netmask=False, skip_loopback=True):
Returns interfaces with their associated local IPs
"""
Extract IP addresses (v4 and/or v6) from a string limited to one
address by protocol
Keyword argument:
string -- String to search in
skip_netmask -- True to skip subnet mask extraction
skip_loopback -- False to include addresses reserved for the
loopback interface
Returns:
A dict of {protocol: address} with protocol one of 'ipv4' or 'ipv6'
"""
ip4_pattern = (
r"((25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}"
)
ip6_pattern = r"(((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::?((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)"
ip4_pattern += r"/[0-9]{1,2})" if not skip_netmask else ")"
ip6_pattern += r"/[0-9]{1,3})" if not skip_netmask else ")"
result = {}
for m in re.finditer(ip4_pattern, string):
addr = m.group(1)
if skip_loopback and addr.startswith("127."):
continue
# Limit to only one result
result["ipv4"] = addr
break
for m in re.finditer(ip6_pattern, string):
addr = m.group(1)
if skip_loopback and addr == "::1":
continue
# Limit to only one result
result["ipv6"] = addr
break
return result
# Helper command taken from Moulinette
def get_network_interfaces():
# Get network devices and their addresses (raw infos from 'ip addr')
devices_raw = {}
output = check_output("ip --brief a").split("\n")
for line in output:
line = line.split()
iname = line[0]
ips = ' '.join(line[2:])
devices_raw[iname] = ips
# Parse relevant informations for each of them
devices = {
name: _extract_inet(addrs)
for name, addrs in devices_raw.items()
if name != "lo"
interfaces = {
adapter.name: {
"ipv4": [ip.ip for ip in adapter.ips if ip.is_IPv4 and ip_address(ip.ip).is_private],
"ipv6": [ip.ip[0] for ip in adapter.ips if ip.is_IPv6 and ip_address(ip.ip[0]).is_private and not ip_address(ip.ip[0]).is_link_local],
}
for adapter in ifaddr.get_adapters()
if adapter.name != "lo"
}
return interfaces
return devices
if __name__ == '__main__':
# Listener class, to detect duplicates on the network
# Stores the list of servers in its list property
class Listener:
def __init__(self):
self.list = []
def remove_service(self, zeroconf, type, name):
info = zeroconf.get_service_info(type, name)
self.list.remove(info.server)
def update_service(self, zeroconf, type, name):
pass
def add_service(self, zeroconf, type, name):
info = zeroconf.get_service_info(type, name)
self.list.append(info.server[:-1])
def main() -> bool:
###
# CONFIG
###
with open('/etc/yunohost/mdns.yml', 'r') as f:
with open("/etc/yunohost/mdns.yml", "r") as f:
config = yaml.safe_load(f) or {}
updated = False
required_fields = ["interfaces", "domains"]
required_fields = ["domains"]
missing_fields = [field for field in required_fields if field not in config]
interfaces = get_network_local_interfaces()
if missing_fields:
print("The fields %s are required" % ', '.join(missing_fields))
print(f"The fields {missing_fields} are required in mdns.yml")
return False
if config['interfaces'] is None:
print('No interface listed for broadcast.')
sys.exit(0)
if "interfaces" not in config:
config["interfaces"] = [interface
for interface, local_ips in interfaces.items()
if local_ips["ipv4"]]
if 'yunohost.local' not in config['domains']:
config['domains'].append('yunohost.local')
if "ban_interfaces" in config:
config["interfaces"] = [interface
for interface in config["interfaces"]
if interface not in config["ban_interfaces"]]
zcs = {}
interfaces = get_network_interfaces()
for interface in config['interfaces']:
infos = [] # List of ServiceInfo objects, to feed Zeroconf
ips = [] # Human-readable IPs
b_ips = [] # Binary-convered IPs
# Let's discover currently published .local domains accross the network
zc = Zeroconf()
listener = Listener()
browser = ServiceBrowser(zc, "_device-info._tcp.local.", listener)
sleep(2)
browser.cancel()
zc.close()
ipv4 = interfaces[interface]['ipv4'].split('/')[0]
if ipv4:
ips.append(ipv4)
b_ips.append(socket.inet_pton(socket.AF_INET, ipv4))
# Always attempt to publish yunohost.local
if "yunohost.local" not in config["domains"]:
config["domains"].append("yunohost.local")
ipv6 = interfaces[interface]['ipv6'].split('/')[0]
if ipv6:
ips.append(ipv6)
b_ips.append(socket.inet_pton(socket.AF_INET6, ipv6))
def find_domain_not_already_published(domain):
# Try domain.local ... but if it's already published by another entity,
# try domain-2.local, domain-3.local, ...
i = 1
domain_i = domain
while domain_i in listener.list:
print(f"Uh oh, {domain_i} already exists on the network...")
i += 1
domain_i = domain.replace(".local", f"-{i}.local")
return domain_i
config['domains'] = [find_domain_not_already_published(domain) for domain in config['domains']]
zcs: Dict[Zeroconf, List[ServiceInfo]] = {}
for interface in config["interfaces"]:
if interface not in interfaces:
print(f"Interface {interface} listed in config file is not present on system.")
continue
# Only broadcast IPv4 because IPv6 is buggy ... because we ain't using python3-ifaddr >= 0.1.7
# Buster only ships 0.1.6
# Bullseye ships 0.1.7
# To be re-enabled once we're on bullseye...
# ips: List[str] = interfaces[interface]["ipv4"] + interfaces[interface]["ipv6"]
ips: List[str] = interfaces[interface]["ipv4"]
# If at least one IP is listed
if ips:
# Create a Zeroconf object, and store the ServiceInfos
zc = Zeroconf(interfaces=ips)
zcs[zc]=[]
for d in config['domains']:
d_domain=d.replace('.local','')
if '.' in d_domain:
print(d_domain+'.local: subdomains are not supported.')
else:
# Create a ServiceInfo object for each .local domain
zcs[zc].append(ServiceInfo(
type_='_device-info._tcp.local.',
name=interface+': '+d_domain+'._device-info._tcp.local.',
addresses=b_ips,
port=80,
server=d+'.',
))
print('Adding '+d+' with addresses '+str(ips)+' on interface '+interface)
if not ips:
continue
# Create a Zeroconf object, and store the ServiceInfos
zc = Zeroconf(interfaces=ips) # type: ignore
zcs[zc] = []
for d in config["domains"]:
d_domain = d.replace(".local", "")
if "." in d_domain:
print(f"{d_domain}.local: subdomains are not supported.")
continue
# Create a ServiceInfo object for each .local domain
zcs[zc].append(
ServiceInfo(
type_="_device-info._tcp.local.",
name=f"{interface}: {d_domain}._device-info._tcp.local.",
parsed_addresses=ips,
port=80,
server=f"{d}.",
)
)
print(f"Adding {d} with addresses {ips} on interface {interface}")
# Run registration
print("Registering...")
for zc, infos in zcs.items():
for info in infos:
zc.register_service(info)
zc.register_service(info, allow_name_change=True, cooperating_responders=True)
try:
print("Registered. Press Ctrl+C or stop service to stop.")
@ -168,6 +160,11 @@ if __name__ == '__main__':
finally:
print("Unregistering...")
for zc, infos in zcs.items():
for info in infos:
zc.unregister_service(info)
zc.unregister_all_services()
zc.close()
return True
if __name__ == "__main__":
sys.exit(0 if main() else 1)

View file

@ -12,31 +12,27 @@ ynh_wait_dpkg_free() {
local try
set +o xtrace # set +x
# With seq 1 17, timeout will be almost 30 minutes
for try in `seq 1 17`
do
for try in $(seq 1 17); do
# Check if /var/lib/dpkg/lock is used by another process
if lsof /var/lib/dpkg/lock > /dev/null
then
if lsof /var/lib/dpkg/lock >/dev/null; then
echo "apt is already in use..."
# Sleep an exponential time at each round
sleep $(( try * try ))
sleep $((try * try))
else
# Check if dpkg hasn't been interrupted and is fully available.
# See this for more information: https://sources.debian.org/src/apt/1.4.9/apt-pkg/deb/debsystem.cc/#L141-L174
local dpkg_dir="/var/lib/dpkg/updates/"
# For each file in $dpkg_dir
while read dpkg_file <&9
do
while read dpkg_file <&9; do
# Check if the name of this file contains only numbers.
if echo "$dpkg_file" | grep --perl-regexp --quiet "^[[:digit:]]+$"
then
if echo "$dpkg_file" | grep --perl-regexp --quiet "^[[:digit:]]+$"; then
# If so, that a remaining of dpkg.
ynh_print_err "dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem."
set -o xtrace # set -x
return 1
fi
done 9<<< "$(ls -1 $dpkg_dir)"
done 9<<<"$(ls -1 $dpkg_dir)"
set -o xtrace # set -x
return 0
fi
@ -57,7 +53,7 @@ ynh_wait_dpkg_free() {
ynh_package_is_installed() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=package= )
local -A args_array=([p]=package=)
local package
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -79,13 +75,12 @@ ynh_package_is_installed() {
ynh_package_version() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=package= )
local -A args_array=([p]=package=)
local package
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ynh_package_is_installed "$package"
then
if ynh_package_is_installed "$package"; then
dpkg-query --show --showformat='${Version}' "$package" 2>/dev/null
else
echo ''
@ -166,14 +161,14 @@ ynh_package_autopurge() {
# | arg: controlfile - path of the equivs control file
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_install_from_equivs () {
ynh_package_install_from_equivs() {
local controlfile=$1
# retrieve package information
local pkgname=$(grep '^Package: ' $controlfile | cut --delimiter=' ' --fields=2) # Retrieve the name of the debian package
local pkgversion=$(grep '^Version: ' $controlfile | cut --delimiter=' ' --fields=2) # And its version number
local pkgname=$(grep '^Package: ' $controlfile | cut --delimiter=' ' --fields=2) # Retrieve the name of the debian package
local pkgversion=$(grep '^Version: ' $controlfile | cut --delimiter=' ' --fields=2) # And its version number
[[ -z "$pkgname" || -z "$pkgversion" ]] \
&& ynh_die --message="Invalid control file" # Check if this 2 variables aren't empty.
&& ynh_die --message="Invalid control file" # Check if this 2 variables aren't empty.
# Update packages cache
ynh_package_update
@ -182,7 +177,7 @@ ynh_package_install_from_equivs () {
local TMPDIR=$(mktemp --directory)
# Force the compatibility level at 10, levels below are deprecated
echo 10 > /usr/share/equivs/template/debian/compat
echo 10 >/usr/share/equivs/template/debian/compat
# Note that the cd executes into a sub shell
# Create a fake deb package with equivs-build and the given control file
@ -190,26 +185,31 @@ ynh_package_install_from_equivs () {
# Install missing dependencies with ynh_package_install
ynh_wait_dpkg_free
cp "$controlfile" "${TMPDIR}/control"
(cd "$TMPDIR"
LC_ALL=C equivs-build ./control 1> /dev/null
LC_ALL=C dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1 | tee ./dpkg_log)
(
cd "$TMPDIR"
LC_ALL=C equivs-build ./control 1>/dev/null
LC_ALL=C dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1 | tee ./dpkg_log
)
ynh_package_install --fix-broken || \
{ # If the installation failed
# (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process)
# Parse the list of problematic dependencies from dpkg's log ...
# (relevant lines look like: "foo-ynh-deps depends on bar; however:")
local problematic_dependencies="$(cat $TMPDIR/dpkg_log | grep -oP '(?<=-ynh-deps depends on ).*(?=; however)' | tr '\n' ' ')"
# Fake an install of those dependencies to see the errors
# The sed command here is, Print only from 'Reading state info' to the end.
[[ -n "$problematic_dependencies" ]] && ynh_package_install $problematic_dependencies --dry-run 2>&1 | sed --quiet '/Reading state info/,$p' | grep -v "fix-broken\|Reading state info" >&2
ynh_die --message="Unable to install dependencies"; }
[[ -n "$TMPDIR" ]] && rm --recursive --force $TMPDIR # Remove the temp dir.
ynh_package_install --fix-broken \
|| { # If the installation failed
# (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process)
# Parse the list of problematic dependencies from dpkg's log ...
# (relevant lines look like: "foo-ynh-deps depends on bar; however:")
local problematic_dependencies="$(cat $TMPDIR/dpkg_log | grep -oP '(?<=-ynh-deps depends on ).*(?=; however)' | tr '\n' ' ')"
# Fake an install of those dependencies to see the errors
# The sed command here is, Print only from 'Reading state info' to the end.
[[ -n "$problematic_dependencies" ]] && ynh_package_install $problematic_dependencies --dry-run 2>&1 | sed --quiet '/Reading state info/,$p' | grep -v "fix-broken\|Reading state info" >&2
ynh_die --message="Unable to install dependencies"
}
[[ -n "$TMPDIR" ]] && rm --recursive --force $TMPDIR # Remove the temp dir.
# check if the package is actually installed
ynh_package_is_installed "$pkgname"
}
YNH_INSTALL_APP_DEPENDENCIES_REPLACE="true"
# Define and install dependencies with a equivs control file
#
# This helper can/should only be called once per app
@ -221,7 +221,7 @@ ynh_package_install_from_equivs () {
# | arg: "dep1|dep2|…" - You can specify alternatives. It will require to install (dep1 or dep2, etc).
#
# Requires YunoHost version 2.6.4 or higher.
ynh_install_app_dependencies () {
ynh_install_app_dependencies() {
local dependencies=$@
# Add a comma for each space between packages. But not add a comma if the space separate a version specification. (See below)
dependencies="$(echo "$dependencies" | sed 's/\([^\<=\>]\)\ \([^(]\)/\1, \2/g')"
@ -232,11 +232,10 @@ ynh_install_app_dependencies () {
if [ -z "${version}" ] || [ "$version" == "null" ]; then
version="1.0"
fi
local dep_app=${app//_/-} # Replace all '_' by '-'
local dep_app=${app//_/-} # Replace all '_' by '-'
# Handle specific versions
if [[ "$dependencies" =~ [\<=\>] ]]
then
if [[ "$dependencies" =~ [\<=\>] ]]; then
# Replace version specifications by relationships syntax
# https://www.debian.org/doc/debian-policy/ch-relationships.html
# Sed clarification
@ -248,27 +247,62 @@ ynh_install_app_dependencies () {
dependencies="$(echo "$dependencies" | sed 's/\([^(\<=\>]\)\([\<=\>]\+\)\([^,]\+\)/\1 (\2 \3)/g')"
fi
# Check for specific php dependencies which requires sury
# This grep will for example return "7.4" if dependencies is "foo bar php7.4-pwet php-gni"
# The (?<=php) syntax corresponds to lookbehind ;)
local specific_php_version=$(echo $dependencies | grep -oP '(?<=php)[0-9.]+(?=-|\>)' | sort -u)
# Ignore case where the php version found is the one available in debian vanilla
[[ "$specific_php_version" != "$YNH_DEFAULT_PHP_VERSION" ]] || specific_php_version=""
if [[ -n "$specific_php_version" ]]
then
# Cover a small edge case where a packager could have specified "php7.4-pwet php5-gni" which is confusing
[[ $(echo $specific_php_version | wc -l) -eq 1 ]] \
|| ynh_die --message="Inconsistent php versions in dependencies ... found : $specific_php_version"
dependencies+=", php${specific_php_version}, php${specific_php_version}-fpm, php${specific_php_version}-common"
ynh_add_sury
fi
# The first time we run ynh_install_app_dependencies, we will replace the
# entire control file (This is in particular meant to cover the case of
# upgrade script where ynh_install_app_dependencies is called with this
# expected effect) Otherwise, any subsequent call will add dependencies
# to those already present in the equivs control file.
if [[ $YNH_INSTALL_APP_DEPENDENCIES_REPLACE == "true" ]]
then
YNH_INSTALL_APP_DEPENDENCIES_REPLACE="false"
else
local current_dependencies=""
if ynh_package_is_installed --package="${dep_app}-ynh-deps"
then
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
current_dependencies=${current_dependencies// | /|}
fi
dependencies="$current_dependencies, $dependencies"
fi
#
# Epic ugly hack to fix the goddamn dependency nightmare of sury
# Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective
# https://github.com/YunoHost/issues/issues/1407
#
# If we require to install php dependency
if echo $dependencies | grep --quiet 'php'
then
if grep --quiet 'php' <<< "$dependencies"; then
# And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian)
if dpkg --list | grep "php7.0" | grep --quiet --invert-match "7.0.33-0+deb9"
then
if dpkg --list | grep "php7.0" | grep --quiet --invert-match "7.0.33-0+deb9"; then
# And sury ain't already in sources.lists
if ! grep --recursive --quiet "^ *deb.*sury" /etc/apt/sources.list*
then
if ! grep --recursive --quiet "^ *deb.*sury" /etc/apt/sources.list*; then
# Re-add sury
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
ynh_add_sury
fi
fi
fi
cat > /tmp/${dep_app}-ynh-deps.control << EOF # Make a control file for equivs-build
cat >/tmp/${dep_app}-ynh-deps.control <<EOF # Make a control file for equivs-build
Section: misc
Priority: optional
Package: ${dep_app}-ynh-deps
@ -279,41 +313,57 @@ Description: Fake package for ${app} (YunoHost app) dependencies
This meta-package is only responsible of installing its dependencies.
EOF
ynh_package_install_from_equivs /tmp/${dep_app}-ynh-deps.control \
|| ynh_die --message="Unable to install dependencies" # Install the fake package and its dependencies
|| ynh_die --message="Unable to install dependencies" # Install the fake package and its dependencies
rm /tmp/${dep_app}-ynh-deps.control
ynh_app_setting_set --app=$app --key=apt_dependencies --value="$dependencies"
if [[ -n "$specific_php_version" ]]
then
# Set the default php version back as the default version for php-cli.
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION
# Store phpversion into the config of this app
ynh_app_setting_set --app=$app --key=phpversion --value=$specific_php_version
# Integrate new php-fpm service in yunohost
yunohost service add php${specific_php_version}-fpm --log "/var/log/php${phpversion}-fpm.log"
elif grep --quiet 'php' <<< "$dependencies"; then
# Store phpversion into the config of this app
ynh_app_setting_set --app=$app --key=phpversion --value=$YNH_DEFAULT_PHP_VERSION
fi
}
# Add sury repository with adequate pin strategy
#
# [internal]
#
# usage: ynh_add_sury
#
ynh_add_sury() {
# Add an extra repository for those packages
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
}
# Add dependencies to install with ynh_install_app_dependencies
#
# usage: ynh_add_app_dependencies --package=phpversion [--replace]
# | arg: -p, --package= - Packages to add as dependencies for the app.
# | arg: -r, --replace - Replace dependencies instead of adding to existing ones.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_add_app_dependencies () {
ynh_add_app_dependencies() {
# Declare an array to define the options of this helper.
local legacy_args=pr
local -A args_array=( [p]=package= [r]=replace)
local -A args_array=([p]=package= [r]=replace)
local package
local replace
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
replace=${replace:-0}
local current_dependencies=""
if [ $replace -eq 0 ]
then
local dep_app=${app//_/-} # Replace all '_' by '-'
if ynh_package_is_installed --package="${dep_app}-ynh-deps"
then
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
fi
current_dependencies=${current_dependencies// | /|}
fi
ynh_install_app_dependencies "${current_dependencies}${package}"
ynh_print_warn --message="Packagers: ynh_add_app_dependencies is deprecated and is now only an alias to ynh_install_app_dependencies"
ynh_install_app_dependencies "${package}"
}
# Remove fake package and its dependencies
@ -323,9 +373,26 @@ ynh_add_app_dependencies () {
# usage: ynh_remove_app_dependencies
#
# Requires YunoHost version 2.6.4 or higher.
ynh_remove_app_dependencies () {
ynh_remove_app_dependencies() {
local dep_app=${app//_/-} # Replace all '_' by '-'
local current_dependencies=""
if ynh_package_is_installed --package="${dep_app}-ynh-deps"; then
current_dependencies="$(dpkg-query --show --showformat='${Depends}' ${dep_app}-ynh-deps) "
current_dependencies=${current_dependencies// | /|}
fi
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
# Check if this app used a specific php version ... in which case we check
# if the corresponding php-fpm is still there. Otherwise, we remove the
# service from yunohost as well
local specific_php_version=$(echo $current_dependencies | tr '-' ' ' | grep -o -E "\<php[0-9.]+\>" | sed 's/php//g' | sort | uniq)
[[ "$specific_php_version" != "$YNH_DEFAULT_PHP_VERSION" ]] || specific_php_version=""
if [[ -n "$specific_php_version" ]] && ! ynh_package_is_installed --package="php${specific_php_version}-fpm"; then
yunohost service remove php${specific_php_version}-fpm
fi
}
# Install packages from an extra repository properly.
@ -337,10 +404,10 @@ ynh_remove_app_dependencies () {
# | arg: -n, --name= - Name for the files for this repo, $app as default value.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_install_extra_app_dependencies () {
ynh_install_extra_app_dependencies() {
# Declare an array to define the options of this helper.
local legacy_args=rpkn
local -A args_array=( [r]=repo= [p]=package= [k]=key= [n]=name= )
local -A args_array=([r]=repo= [p]=package= [k]=key= [n]=name=)
local repo
local package
local key
@ -351,15 +418,14 @@ ynh_install_extra_app_dependencies () {
key=${key:-}
# Set a key only if asked
if [ -n "$key" ]
then
if [ -n "$key" ]; then
key="--key=$key"
fi
# Add an extra repository for those packages
ynh_install_extra_repo --repo="$repo" $key --priority=995 --name=$name
# Install requested dependencies from this extra repository.
ynh_add_app_dependencies --package="$package"
ynh_install_app_dependencies "$package"
# Remove this extra repository after packages are installed
ynh_remove_extra_repo --name=$app
@ -377,10 +443,10 @@ ynh_install_extra_app_dependencies () {
# | arg: -a, --append - Do not overwrite existing files.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_install_extra_repo () {
ynh_install_extra_repo() {
# Declare an array to define the options of this helper.
local legacy_args=rkpna
local -A args_array=( [r]=repo= [k]=key= [p]=priority= [n]=name= [a]=append )
local -A args_array=([r]=repo= [k]=key= [p]=priority= [n]=name= [a]=append)
local repo
local key
local priority
@ -393,8 +459,7 @@ ynh_install_extra_repo () {
key=${key:-}
priority=${priority:-}
if [ $append -eq 1 ]
then
if [ $append -eq 1 ]; then
append="--append"
wget_append="tee --append"
else
@ -423,18 +488,16 @@ ynh_install_extra_repo () {
local pin="${uri#*://}"
pin="${pin%%/*}"
# Set a priority only if asked
if [ -n "$priority" ]
then
if [ -n "$priority" ]; then
priority="--priority=$priority"
fi
ynh_pin_repo --package="*" --pin="origin \"$pin\"" $priority --name="$name" $append
# Get the public key for the repo
if [ -n "$key" ]
then
if [ -n "$key" ]; then
mkdir --parents "/etc/apt/trusted.gpg.d"
# Timeout option is here to enforce the timeout on dns query and tcp connect (c.f. man wget)
wget --timeout 900 --quiet "$key" --output-document=- | gpg --dearmor | $wget_append /etc/apt/trusted.gpg.d/$name.gpg > /dev/null
wget --timeout 900 --quiet "$key" --output-document=- | gpg --dearmor | $wget_append /etc/apt/trusted.gpg.d/$name.gpg >/dev/null
fi
# Update the list of package with the new repo
@ -449,10 +512,10 @@ ynh_install_extra_repo () {
# | arg: -n, --name= - Name for the files for this repo, $app as default value.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_remove_extra_repo () {
ynh_remove_extra_repo() {
# Declare an array to define the options of this helper.
local legacy_args=n
local -A args_array=( [n]=name= )
local -A args_array=([n]=name=)
local name
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -461,8 +524,8 @@ ynh_remove_extra_repo () {
ynh_secure_remove --file="/etc/apt/sources.list.d/$name.list"
# Sury pinning is managed by the regenconf in the core...
[[ "$name" == "extra_php_version" ]] || ynh_secure_remove "/etc/apt/preferences.d/$name"
ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.gpg" > /dev/null
ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.asc" > /dev/null
ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.gpg" >/dev/null
ynh_secure_remove --file="/etc/apt/trusted.gpg.d/$name.asc" >/dev/null
# Update the list of package to exclude the old repo
ynh_package_update
@ -484,10 +547,10 @@ ynh_remove_extra_repo () {
# ynh_add_repo --uri=http://forge.yunohost.org/debian/ --suite=stretch --component=stable
#
# Requires YunoHost version 3.8.1 or higher.
ynh_add_repo () {
ynh_add_repo() {
# Declare an array to define the options of this helper.
local legacy_args=uscna
local -A args_array=( [u]=uri= [s]=suite= [c]=component= [n]=name= [a]=append )
local -A args_array=([u]=uri= [s]=suite= [c]=component= [n]=name= [a]=append)
local uri
local suite
local component
@ -498,8 +561,7 @@ ynh_add_repo () {
name="${name:-$app}"
append=${append:-0}
if [ $append -eq 1 ]
then
if [ $append -eq 1 ]; then
append="tee --append"
else
append="tee"
@ -525,10 +587,10 @@ ynh_add_repo () {
# See https://manpages.debian.org/stretch/apt/apt_preferences.5.en.html#How_APT_Interprets_Priorities for information about pinning.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_pin_repo () {
ynh_pin_repo() {
# Declare an array to define the options of this helper.
local legacy_args=pirna
local -A args_array=( [p]=package= [i]=pin= [r]=priority= [n]=name= [a]=append )
local -A args_array=([p]=package= [i]=pin= [r]=priority= [n]=name= [a]=append)
local package
local pin
local priority
@ -541,8 +603,7 @@ ynh_pin_repo () {
name="${name:-$app}"
append=${append:-0}
if [ $append -eq 1 ]
then
if [ $append -eq 1 ]; then
append="tee --append"
else
append="tee"
@ -556,5 +617,5 @@ ynh_pin_repo () {
Pin: $pin
Pin-Priority: $priority
" \
| $append "/etc/apt/preferences.d/$name"
| $append "/etc/apt/preferences.d/$name"
}

View file

@ -67,7 +67,7 @@ ynh_backup() {
# Declare an array to define the options of this helper.
local legacy_args=sdbm
local -A args_array=( [s]=src_path= [d]=dest_path= [b]=is_big [m]=not_mandatory )
local -A args_array=([s]=src_path= [d]=dest_path= [b]=is_big [m]=not_mandatory)
local src_path
local dest_path
local is_big
@ -83,10 +83,8 @@ ynh_backup() {
# If backing up core only (used by ynh_backup_before_upgrade),
# don't backup big data items
if [ $is_big -eq 1 ] && ( [ ${do_not_backup_data:-0} -eq 1 ] || [ $BACKUP_CORE_ONLY -eq 1 ] )
then
if [ $BACKUP_CORE_ONLY -eq 1 ]
then
if [ $is_big -eq 1 ] && ([ ${do_not_backup_data:-0} -eq 1 ] || [ $BACKUP_CORE_ONLY -eq 1 ]); then
if [ $BACKUP_CORE_ONLY -eq 1 ]; then
ynh_print_info --message="$src_path will not be saved, because 'BACKUP_CORE_ONLY' is set."
else
ynh_print_info --message="$src_path will not be saved, because 'do_not_backup_data' is set."
@ -98,14 +96,11 @@ ynh_backup() {
# Format correctly source and destination paths
# ==============================================================================
# Be sure the source path is not empty
if [ ! -e "$src_path" ]
then
if [ ! -e "$src_path" ]; then
ynh_print_warn --message="Source path '${src_path}' does not exist"
if [ "$not_mandatory" == "0" ]
then
if [ "$not_mandatory" == "0" ]; then
# This is a temporary fix for fail2ban config files missing after the migration to stretch.
if echo "${src_path}" | grep --quiet "/etc/fail2ban"
then
if echo "${src_path}" | grep --quiet "/etc/fail2ban"; then
touch "${src_path}"
ynh_print_info --message="The missing file will be replaced by a dummy one for the backup !!!"
else
@ -123,13 +118,11 @@ ynh_backup() {
# If there is no destination path, initialize it with the source path
# relative to "/".
# eg: src_path=/etc/yunohost -> dest_path=etc/yunohost
if [[ -z "$dest_path" ]]
then
if [[ -z "$dest_path" ]]; then
dest_path="${src_path#/}"
else
if [[ "${dest_path:0:1}" == "/" ]]
then
if [[ "${dest_path:0:1}" == "/" ]]; then
# If the destination path is an absolute path, transform it as a path
# relative to the current working directory ($YNH_CWD)
@ -153,8 +146,7 @@ ynh_backup() {
fi
# Check if dest_path already exists in tmp archive
if [[ -e "${dest_path}" ]]
then
if [[ -e "${dest_path}" ]]; then
ynh_print_err --message="Destination path '${dest_path}' already exist"
return 1
fi
@ -171,7 +163,7 @@ ynh_backup() {
# ==============================================================================
local src=$(echo "${src_path}" | sed --regexp-extended 's/"/\"\"/g')
local dest=$(echo "${dest_path}" | sed --regexp-extended 's/"/\"\"/g')
echo "\"${src}\",\"${dest}\"" >> "${YNH_BACKUP_CSV}"
echo "\"${src}\",\"${dest}\"" >>"${YNH_BACKUP_CSV}"
# ==============================================================================
@ -185,19 +177,18 @@ ynh_backup() {
# usage: ynh_restore
#
# Requires YunoHost version 2.6.4 or higher.
ynh_restore () {
ynh_restore() {
# Deduce the relative path of $YNH_CWD
local REL_DIR="${YNH_CWD#$YNH_BACKUP_DIR/}"
REL_DIR="${REL_DIR%/}/"
# For each destination path begining by $REL_DIR
cat ${YNH_BACKUP_CSV} | tr --delete $'\r' | grep --only-matching --no-filename --perl-regexp "^\".*\",\"$REL_DIR.*\"$" | \
while read line
do
local ORIGIN_PATH=$(echo "$line" | grep --only-matching --no-filename --perl-regexp "^\"\K.*(?=\",\".*\"$)")
local ARCHIVE_PATH=$(echo "$line" | grep --only-matching --no-filename --perl-regexp "^\".*\",\"$REL_DIR\K.*(?=\"$)")
ynh_restore_file --origin_path="$ARCHIVE_PATH" --dest_path="$ORIGIN_PATH"
done
cat ${YNH_BACKUP_CSV} | tr --delete $'\r' | grep --only-matching --no-filename --perl-regexp "^\".*\",\"$REL_DIR.*\"$" \
| while read line; do
local ORIGIN_PATH=$(echo "$line" | grep --only-matching --no-filename --perl-regexp "^\"\K.*(?=\",\".*\"$)")
local ARCHIVE_PATH=$(echo "$line" | grep --only-matching --no-filename --perl-regexp "^\".*\",\"$REL_DIR\K.*(?=\"$)")
ynh_restore_file --origin_path="$ARCHIVE_PATH" --dest_path="$ORIGIN_PATH"
done
}
# Return the path in the archive where has been stocked the origin path
@ -205,7 +196,7 @@ ynh_restore () {
# [internal]
#
# usage: _get_archive_path ORIGIN_PATH
_get_archive_path () {
_get_archive_path() {
# For security reasons we use csv python library to read the CSV
python3 -c "
import sys
@ -217,7 +208,7 @@ with open(sys.argv[1], 'r') as backup_file:
print(row['dest'])
sys.exit(0)
raise Exception('Original path for %s not found' % sys.argv[2])
" "${YNH_BACKUP_CSV}" "$1"
" "${YNH_BACKUP_CSV}" "$1"
return $?
}
@ -245,10 +236,10 @@ with open(sys.argv[1], 'r') as backup_file:
#
# Requires YunoHost version 2.6.4 or higher.
# Requires YunoHost version 3.5.0 or higher for the argument --not_mandatory
ynh_restore_file () {
ynh_restore_file() {
# Declare an array to define the options of this helper.
local legacy_args=odm
local -A args_array=( [o]=origin_path= [d]=dest_path= [m]=not_mandatory )
local -A args_array=([o]=origin_path= [d]=dest_path= [m]=not_mandatory)
local origin_path
local dest_path
local not_mandatory
@ -261,10 +252,8 @@ ynh_restore_file () {
local archive_path="$YNH_CWD${origin_path}"
# If archive_path doesn't exist, search for a corresponding path in CSV
if [ ! -d "$archive_path" ] && [ ! -f "$archive_path" ] && [ ! -L "$archive_path" ]
then
if [ "$not_mandatory" == "0" ]
then
if [ ! -d "$archive_path" ] && [ ! -f "$archive_path" ] && [ ! -L "$archive_path" ]; then
if [ "$not_mandatory" == "0" ]; then
archive_path="$YNH_BACKUP_DIR/$(_get_archive_path \"$origin_path\")"
else
return 0
@ -272,14 +261,12 @@ ynh_restore_file () {
fi
# Move the old directory if it already exists
if [[ -e "${dest_path}" ]]
then
if [[ -e "${dest_path}" ]]; then
# Check if the file/dir size is less than 500 Mo
if [[ $(du --summarize --bytes ${dest_path} | cut --delimiter="/" --fields=1) -le "500000000" ]]
then
if [[ $(du --summarize --bytes ${dest_path} | cut --delimiter="/" --fields=1) -le "500000000" ]]; then
local backup_file="/home/yunohost.conf/backup/${dest_path}.backup.$(date '+%Y%m%d.%H%M%S')"
mkdir --parents "$(dirname "$backup_file")"
mv "${dest_path}" "$backup_file" # Move the current file or directory
mv "${dest_path}" "$backup_file" # Move the current file or directory
else
ynh_secure_remove --file=${dest_path}
fi
@ -289,10 +276,8 @@ ynh_restore_file () {
mkdir --parents $(dirname "$dest_path")
# Do a copy if it's just a mounting point
if mountpoint --quiet $YNH_BACKUP_DIR
then
if [[ -d "${archive_path}" ]]
then
if mountpoint --quiet $YNH_BACKUP_DIR; then
if [[ -d "${archive_path}" ]]; then
archive_path="${archive_path}/."
mkdir --parents "$dest_path"
fi
@ -323,10 +308,10 @@ ynh_bind_or_cp() {
# $app should be defined when calling this helper
#
# Requires YunoHost version 2.6.4 or higher.
ynh_store_file_checksum () {
ynh_store_file_checksum() {
# Declare an array to define the options of this helper.
local legacy_args=f
local -A args_array=( [f]=file= [u]=update_only )
local -A args_array=([f]=file= [u]=update_only)
local file
local update_only
update_only="${update_only:-0}"
@ -334,22 +319,21 @@ ynh_store_file_checksum () {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
# If update only, we don't save the new checksum if no old checksum exist
if [ $update_only -eq 1 ] ; then
if [ $update_only -eq 1 ]; then
local checksum_value=$(ynh_app_setting_get --app=$app --key=$checksum_setting_name)
if [ -z "${checksum_value}" ] ; then
if [ -z "${checksum_value}" ]; then
unset backup_file_checksum
return 0
fi
fi
ynh_app_setting_set --app=$app --key=$checksum_setting_name --value=$(md5sum "$file" | cut --delimiter=' ' --fields=1)
# If backup_file_checksum isn't empty, ynh_backup_if_checksum_is_different has made a backup
if [ -n "${backup_file_checksum-}" ]
then
if [ -n "${backup_file_checksum-}" ]; then
# Print the diff between the previous file and the new one.
# diff return 1 if the files are different, so the || true
diff --report-identical-files --unified --color=always $backup_file_checksum $file >&2 || true
@ -368,27 +352,25 @@ ynh_store_file_checksum () {
# modified config files.
#
# Requires YunoHost version 2.6.4 or higher.
ynh_backup_if_checksum_is_different () {
ynh_backup_if_checksum_is_different() {
# Declare an array to define the options of this helper.
local legacy_args=f
local -A args_array=( [f]=file= )
local -A args_array=([f]=file=)
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
local checksum_value=$(ynh_app_setting_get --app=$app --key=$checksum_setting_name)
# backup_file_checksum isn't declare as local, so it can be reuse by ynh_store_file_checksum
backup_file_checksum=""
if [ -n "$checksum_value" ]
then # Proceed only if a value was stored into the app settings
if [ -e $file ] && ! echo "$checksum_value $file" | md5sum --check --status
then # If the checksum is now different
if [ -n "$checksum_value" ]; then # Proceed only if a value was stored into the app settings
if [ -e $file ] && ! echo "$checksum_value $file" | md5sum --check --status; then # If the checksum is now different
backup_file_checksum="/home/yunohost.conf/backup/$file.backup.$(date '+%Y%m%d.%H%M%S')"
mkdir --parents "$(dirname "$backup_file_checksum")"
cp --archive "$file" "$backup_file_checksum" # Backup the current file
cp --archive "$file" "$backup_file_checksum" # Backup the current file
ynh_print_warn "File $file has been manually modified since the installation or last upgrade. So it has been duplicated in $backup_file_checksum"
echo "$backup_file_checksum" # Return the name of the backup file
echo "$backup_file_checksum" # Return the name of the backup file
fi
fi
}
@ -401,15 +383,15 @@ ynh_backup_if_checksum_is_different () {
# $app should be defined when calling this helper
#
# Requires YunoHost version 3.3.1 or higher.
ynh_delete_file_checksum () {
ynh_delete_file_checksum() {
# Declare an array to define the options of this helper.
local legacy_args=f
local -A args_array=( [f]=file= )
local -A args_array=([f]=file=)
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
ynh_app_setting_delete --app=$app --key=$checksum_setting_name
}
@ -417,7 +399,7 @@ ynh_delete_file_checksum () {
#
# [internal]
#
ynh_backup_archive_exists () {
ynh_backup_archive_exists() {
yunohost backup list --output-as json --quiet \
| jq -e --arg archive "$1" '.archives | index($archive)' >/dev/null
}
@ -436,22 +418,19 @@ ynh_backup_archive_exists () {
# ```
#
# Requires YunoHost version 2.7.2 or higher.
ynh_backup_before_upgrade () {
if [ ! -e "/etc/yunohost/apps/$app/scripts/backup" ]
then
ynh_backup_before_upgrade() {
if [ ! -e "/etc/yunohost/apps/$app/scripts/backup" ]; then
ynh_print_warn --message="This app doesn't have any backup script."
return
fi
backup_number=1
local old_backup_number=2
local app_bck=${app//_/-} # Replace all '_' by '-'
local app_bck=${app//_/-} # Replace all '_' by '-'
NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
then
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]; then
# Check if a backup already exists with the prefix 1
if ynh_backup_archive_exists "$app_bck-pre-upgrade1"
then
if ynh_backup_archive_exists "$app_bck-pre-upgrade1"; then
# Prefix becomes 2 to preserve the previous backup
backup_number=2
old_backup_number=1
@ -459,13 +438,11 @@ ynh_backup_before_upgrade () {
# Create backup
BACKUP_CORE_ONLY=1 yunohost backup create --apps $app --name $app_bck-pre-upgrade$backup_number --debug
if [ "$?" -eq 0 ]
then
if [ "$?" -eq 0 ]; then
# If the backup succeeded, remove the previous backup
if ynh_backup_archive_exists "$app_bck-pre-upgrade$old_backup_number"
then
if ynh_backup_archive_exists "$app_bck-pre-upgrade$old_backup_number"; then
# Remove the previous backup only if it exists
yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
yunohost backup delete $app_bck-pre-upgrade$old_backup_number >/dev/null
fi
else
ynh_die --message="Backup failed, the upgrade process was aborted."
@ -489,17 +466,15 @@ ynh_backup_before_upgrade () {
# ```
#
# Requires YunoHost version 2.7.2 or higher.
ynh_restore_upgradebackup () {
ynh_restore_upgradebackup() {
ynh_print_err --message="Upgrade failed."
local app_bck=${app//_/-} # Replace all '_' by '-'
local app_bck=${app//_/-} # Replace all '_' by '-'
NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
then
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]; then
# Check if an existing backup can be found before removing and restoring the application.
if ynh_backup_archive_exists "$app_bck-pre-upgrade$backup_number"
then
if ynh_backup_archive_exists "$app_bck-pre-upgrade$backup_number"; then
# Remove the application then restore it
yunohost app remove $app
# Restore the backup

View file

@ -1,10 +1,136 @@
#!/bin/bash
_ynh_app_config_get_one() {
local short_setting="$1"
local type="$2"
local bind="$3"
local getter="get__${short_setting}"
# Get value from getter if exists
if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
old[$short_setting]="$($getter)"
formats[${short_setting}]="yaml"
elif [[ "$bind" == *"("* ]] && type -t "get__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
old[$short_setting]="$("get__${bind%%(*}" $short_setting $type $bind)"
formats[${short_setting}]="yaml"
elif [[ "$bind" == "null" ]]; then
old[$short_setting]="YNH_NULL"
# Get value from app settings or from another file
elif [[ "$type" == "file" ]]; then
if [[ "$bind" == "settings" ]]; then
ynh_die --message="File '${short_setting}' can't be stored in settings"
fi
old[$short_setting]="$(ls "$(echo $bind | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)" 2>/dev/null || echo YNH_NULL)"
file_hash[$short_setting]="true"
# Get multiline text from settings or from a full file
elif [[ "$type" == "text" ]]; then
if [[ "$bind" == "settings" ]]; then
old[$short_setting]="$(ynh_app_setting_get $app $short_setting)"
elif [[ "$bind" == *":"* ]]; then
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
else
old[$short_setting]="$(cat $(echo $bind | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2>/dev/null || echo YNH_NULL)"
fi
# Get value from a kind of key/value file
else
local bind_after=""
if [[ "$bind" == "settings" ]]; then
bind=":/etc/yunohost/apps/$app/settings.yml"
fi
local bind_key_="$(echo "$bind" | cut -d: -f1)"
bind_key_=${bind_key_:-$short_setting}
if [[ "$bind_key_" == *">"* ]]; then
bind_after="$(echo "${bind_key_}" | cut -d'>' -f1)"
bind_key_="$(echo "${bind_key_}" | cut -d'>' -f2)"
fi
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
old[$short_setting]="$(ynh_read_var_in_file --file="${bind_file}" --key="${bind_key_}" --after="${bind_after}")"
fi
}
_ynh_app_config_apply_one() {
local short_setting="$1"
local setter="set__${short_setting}"
local bind="${binds[$short_setting]}"
local type="${types[$short_setting]}"
if [ "${changed[$short_setting]}" == "true" ]; then
# Apply setter if exists
if type -t $setter 2>/dev/null | grep -q '^function$' 2>/dev/null; then
$setter
elif [[ "$bind" == *"("* ]] && type -t "set__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
"set__${bind%%(*}" $short_setting $type $bind
elif [[ "$bind" == "null" ]]; then
return
# Save in a file
elif [[ "$type" == "file" ]]; then
if [[ "$bind" == "settings" ]]; then
ynh_die --message="File '${short_setting}' can't be stored in settings"
fi
local bind_file="$(echo "$bind" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
if [[ "${!short_setting}" == "" ]]; then
ynh_backup_if_checksum_is_different --file="$bind_file"
ynh_secure_remove --file="$bind_file"
ynh_delete_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' removed"
else
ynh_backup_if_checksum_is_different --file="$bind_file"
if [[ "${!short_setting}" != "$bind_file" ]]; then
cp "${!short_setting}" "$bind_file"
fi
ynh_store_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' overwritten with ${!short_setting}"
fi
# Save value in app settings
elif [[ "$bind" == "settings" ]]; then
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
ynh_print_info --message="Configuration key '$short_setting' edited in app settings"
# Save multiline text in a file
elif [[ "$type" == "text" ]]; then
if [[ "$bind" == *":"* ]]; then
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
fi
local bind_file="$(echo "$bind" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
ynh_backup_if_checksum_is_different --file="$bind_file"
echo "${!short_setting}" >"$bind_file"
ynh_store_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' overwritten with the content provided in question '${short_setting}'"
# Set value into a kind of key/value file
else
local bind_after=""
local bind_key_="$(echo "$bind" | cut -d: -f1)"
bind_key_=${bind_key_:-$short_setting}
if [[ "$bind_key_" == *">"* ]]; then
bind_after="$(echo "${bind_key_}" | cut -d'>' -f1)"
bind_key_="$(echo "${bind_key_}" | cut -d'>' -f2)"
fi
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
ynh_backup_if_checksum_is_different --file="$bind_file"
ynh_write_var_in_file --file="${bind_file}" --key="${bind_key_}" --value="${!short_setting}" --after="${bind_after}"
ynh_store_file_checksum --file="$bind_file" --update_only
# We stored the info in settings in order to be able to upgrade the app
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
ynh_print_info --message="Configuration key '$bind_key_' edited into $bind_file"
fi
fi
}
_ynh_app_config_get() {
# From settings
local lines
lines=$(python3 << EOL
lines=$(
python3 <<EOL
import toml
from collections import OrderedDict
with open("../config_panel.toml", "r") as f:
@ -24,164 +150,29 @@ for panel_name, panel in loaded_toml.items():
param.get('bind', 'settings' if param.get('type', 'string') != 'file' else 'null')
]))
EOL
)
for line in $lines
do
)
for line in $lines; do
# Split line into short_setting, type and bind
IFS=';' read short_setting type bind <<< "$line"
local getter="get__${short_setting}"
IFS=';' read short_setting type bind <<<"$line"
binds[${short_setting}]="$bind"
types[${short_setting}]="$type"
file_hash[${short_setting}]=""
formats[${short_setting}]=""
# Get value from getter if exists
if type -t $getter 2>/dev/null | grep -q '^function$' 2>/dev/null;
then
old[$short_setting]="$($getter)"
formats[${short_setting}]="yaml"
elif [[ "$bind" == "null" ]]
then
old[$short_setting]="YNH_NULL"
# Get value from app settings or from another file
elif [[ "$type" == "file" ]]
then
if [[ "$bind" == "settings" ]]
then
ynh_die --message="File '${short_setting}' can't be stored in settings"
fi
old[$short_setting]="$(ls "$(echo $bind | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)" 2> /dev/null || echo YNH_NULL)"
file_hash[$short_setting]="true"
# Get multiline text from settings or from a full file
elif [[ "$type" == "text" ]]
then
if [[ "$bind" == "settings" ]]
then
old[$short_setting]="$(ynh_app_setting_get $app $short_setting)"
elif [[ "$bind" == *":"* ]]
then
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
else
old[$short_setting]="$(cat $(echo $bind | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/) 2> /dev/null || echo YNH_NULL)"
fi
# Get value from a kind of key/value file
else
local bind_after=""
if [[ "$bind" == "settings" ]]
then
bind=":/etc/yunohost/apps/$app/settings.yml"
fi
local bind_key="$(echo "$bind" | cut -d: -f1)"
bind_key=${bind_key:-$short_setting}
if [[ "$bind_key" == *">"* ]];
then
bind_after="$(echo "${bind_key}" | cut -d'>' -f1)"
bind_key="$(echo "${bind_key}" | cut -d'>' -f2)"
fi
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
old[$short_setting]="$(ynh_read_var_in_file --file="${bind_file}" --key="${bind_key}" --after="${bind_after}")"
fi
ynh_app_config_get_one $short_setting $type $bind
done
}
_ynh_app_config_apply() {
for short_setting in "${!old[@]}"
do
local setter="set__${short_setting}"
local bind="${binds[$short_setting]}"
local type="${types[$short_setting]}"
if [ "${changed[$short_setting]}" == "true" ]
then
# Apply setter if exists
if type -t $setter 2>/dev/null | grep -q '^function$' 2>/dev/null;
then
$setter
elif [[ "$bind" == "null" ]]
then
continue
# Save in a file
elif [[ "$type" == "file" ]]
then
if [[ "$bind" == "settings" ]]
then
ynh_die --message="File '${short_setting}' can't be stored in settings"
fi
local bind_file="$(echo "$bind" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
if [[ "${!short_setting}" == "" ]]
then
ynh_backup_if_checksum_is_different --file="$bind_file"
ynh_secure_remove --file="$bind_file"
ynh_delete_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' removed"
else
ynh_backup_if_checksum_is_different --file="$bind_file"
if [[ "${!short_setting}" != "$bind_file" ]]
then
cp "${!short_setting}" "$bind_file"
fi
ynh_store_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' overwrited with ${!short_setting}"
fi
# Save value in app settings
elif [[ "$bind" == "settings" ]]
then
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
ynh_print_info --message="Configuration key '$short_setting' edited in app settings"
# Save multiline text in a file
elif [[ "$type" == "text" ]]
then
if [[ "$bind" == *":"* ]]
then
ynh_die --message="For technical reasons, multiline text '${short_setting}' can't be stored automatically in a variable file, you have to create custom getter/setter"
fi
local bind_file="$(echo "$bind" | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
ynh_backup_if_checksum_is_different --file="$bind_file"
echo "${!short_setting}" > "$bind_file"
ynh_store_file_checksum --file="$bind_file" --update_only
ynh_print_info --message="File '$bind_file' overwrited with the content you provieded in '${short_setting}' question"
# Set value into a kind of key/value file
else
local bind_after=""
local bind_key="$(echo "$bind" | cut -d: -f1)"
bind_key=${bind_key:-$short_setting}
if [[ "$bind_key" == *">"* ]];
then
bind_after="$(echo "${bind_key}" | cut -d'>' -f1)"
bind_key="$(echo "${bind_key}" | cut -d'>' -f2)"
fi
local bind_file="$(echo "$bind" | cut -d: -f2 | sed s@__FINALPATH__@$final_path@ | sed s/__APP__/$app/)"
ynh_backup_if_checksum_is_different --file="$bind_file"
ynh_write_var_in_file --file="${bind_file}" --key="${bind_key}" --value="${!short_setting}" --after="${bind_after}"
ynh_store_file_checksum --file="$bind_file" --update_only
# We stored the info in settings in order to be able to upgrade the app
ynh_app_setting_set --app=$app --key=$short_setting --value="${!short_setting}"
ynh_print_info --message="Configuration key '$bind_key' edited into $bind_file"
fi
fi
for short_setting in "${!old[@]}"; do
ynh_app_config_apply_one $short_setting
done
}
_ynh_app_config_show() {
for short_setting in "${!old[@]}"
do
if [[ "${old[$short_setting]}" != YNH_NULL ]]
then
if [[ "${formats[$short_setting]}" == "yaml" ]]
then
for short_setting in "${!old[@]}"; do
if [[ "${old[$short_setting]}" != YNH_NULL ]]; then
if [[ "${formats[$short_setting]}" == "yaml" ]]; then
ynh_return "${short_setting}:"
ynh_return "$(echo "${old[$short_setting]}" | sed 's/^/ /g')"
else
@ -197,48 +188,39 @@ _ynh_app_config_validate() {
ynh_script_progression --message="Checking what changed in the new configuration..." --weight=1
local nothing_changed=true
local changes_validated=true
for short_setting in "${!old[@]}"
do
for short_setting in "${!old[@]}"; do
changed[$short_setting]=false
if [ -z ${!short_setting+x} ]
then
if [ -z ${!short_setting+x} ]; then
# Assign the var with the old value in order to allows multiple
# args validation
declare "$short_setting"="${old[$short_setting]}"
continue
fi
if [ ! -z "${file_hash[${short_setting}]}" ]
then
if [ ! -z "${file_hash[${short_setting}]}" ]; then
file_hash[old__$short_setting]=""
file_hash[new__$short_setting]=""
if [ -f "${old[$short_setting]}" ]
then
if [ -f "${old[$short_setting]}" ]; then
file_hash[old__$short_setting]=$(sha256sum "${old[$short_setting]}" | cut -d' ' -f1)
if [ -z "${!short_setting}" ]
then
if [ -z "${!short_setting}" ]; then
changed[$short_setting]=true
nothing_changed=false
fi
fi
if [ -f "${!short_setting}" ]
then
if [ -f "${!short_setting}" ]; then
file_hash[new__$short_setting]=$(sha256sum "${!short_setting}" | cut -d' ' -f1)
if [[ "${file_hash[old__$short_setting]}" != "${file_hash[new__$short_setting]}" ]]
then
if [[ "${file_hash[old__$short_setting]}" != "${file_hash[new__$short_setting]}" ]]; then
changed[$short_setting]=true
nothing_changed=false
fi
fi
else
if [[ "${!short_setting}" != "${old[$short_setting]}" ]]
then
if [[ "${!short_setting}" != "${old[$short_setting]}" ]]; then
changed[$short_setting]=true
nothing_changed=false
fi
fi
done
if [[ "$nothing_changed" == "true" ]]
then
if [[ "$nothing_changed" == "true" ]]; then
ynh_print_info --message="Nothing has changed"
exit 0
fi
@ -246,16 +228,15 @@ _ynh_app_config_validate() {
# Run validation if something is changed
ynh_script_progression --message="Validating the new configuration..." --weight=1
for short_setting in "${!old[@]}"
do
for short_setting in "${!old[@]}"; do
[[ "${changed[$short_setting]}" == "false" ]] && continue
local result=""
if type -t validate__$short_setting | grep -q '^function$' 2>/dev/null;
then
if type -t validate__$short_setting | grep -q '^function$' 2>/dev/null; then
result="$(validate__$short_setting)"
elif [[ "$bind" == *"("* ]] && type -t "validate__${bind%%(*}" 2>/dev/null | grep -q '^function$' 2>/dev/null; then
"validate__${bind%%(*}" $short_setting
fi
if [ -n "$result" ]
then
if [ -n "$result" ]; then
#
# Return a yaml such as:
#
@ -265,8 +246,7 @@ _ynh_app_config_validate() {
#
# We use changes_validated to know if this is
# the first validation error
if [[ "$changes_validated" == true ]]
then
if [[ "$changes_validated" == true ]]; then
ynh_return "validation_errors:"
fi
ynh_return " ${short_setting}: \"$result\""
@ -276,13 +256,16 @@ _ynh_app_config_validate() {
# If validation failed, exit the script right now (instead of going into apply)
# Yunohost core will pick up the errors returned via ynh_return previously
if [[ "$changes_validated" == "false" ]]
then
if [[ "$changes_validated" == "false" ]]; then
exit 0
fi
}
ynh_app_config_get_one() {
_ynh_app_config_get_one $1 $2 $3
}
ynh_app_config_get() {
_ynh_app_config_get
}
@ -295,6 +278,9 @@ ynh_app_config_validate() {
_ynh_app_config_validate
}
ynh_app_config_apply_one() {
_ynh_app_config_apply_one $1
}
ynh_app_config_apply() {
_ynh_app_config_apply
}
@ -308,21 +294,20 @@ ynh_app_config_run() {
declare -Ag formats=()
case $1 in
show)
ynh_app_config_get
ynh_app_config_show
;;
apply)
max_progression=4
ynh_script_progression --message="Reading config panel description and current configuration..."
ynh_app_config_get
show)
ynh_app_config_get
ynh_app_config_show
;;
apply)
max_progression=4
ynh_script_progression --message="Reading config panel description and current configuration..."
ynh_app_config_get
ynh_app_config_validate
ynh_app_config_validate
ynh_script_progression --message="Applying the new configuration..."
ynh_app_config_apply
ynh_script_progression --message="Configuration of $app completed" --last
;;
ynh_script_progression --message="Applying the new configuration..."
ynh_app_config_apply
ynh_script_progression --message="Configuration of $app completed" --last
;;
esac
}

View file

@ -62,10 +62,10 @@
# ```
#
# Requires YunoHost version 4.1.0 or higher.
ynh_add_fail2ban_config () {
ynh_add_fail2ban_config() {
# Declare an array to define the options of this helper.
local legacy_args=lrmptv
local -A args_array=( [l]=logpath= [r]=failregex= [m]=max_retry= [p]=ports= [t]=use_template [v]=others_var=)
local -A args_array=([l]=logpath= [r]=failregex= [m]=max_retry= [p]=ports= [t]=use_template [v]=others_var=)
local logpath
local failregex
local max_retry
@ -81,8 +81,7 @@ ynh_add_fail2ban_config () {
[[ -z "$others_var" ]] || ynh_print_warn --message="Packagers: using --others_var is unecessary since YunoHost 4.2"
if [ $use_template -ne 1 ]
then
if [ $use_template -ne 1 ]; then
# Usage 1, no template. Build a config file from scratch.
test -n "$logpath" || ynh_die --message="ynh_add_fail2ban_config expects a logfile path as first argument and received nothing."
test -n "$failregex" || ynh_die --message="ynh_add_fail2ban_config expects a failure regex as second argument and received nothing."
@ -94,15 +93,15 @@ port = __PORTS__
filter = __APP__
logpath = __LOGPATH__
maxretry = __MAX_RETRY__
" > $YNH_APP_BASEDIR/conf/f2b_jail.conf
" >$YNH_APP_BASEDIR/conf/f2b_jail.conf
echo "
echo "
[INCLUDES]
before = common.conf
[Definition]
failregex = __FAILREGEX__
ignoreregex =
" > $YNH_APP_BASEDIR/conf/f2b_filter.conf
" >$YNH_APP_BASEDIR/conf/f2b_filter.conf
fi
ynh_add_config --template="$YNH_APP_BASEDIR/conf/f2b_jail.conf" --destination="/etc/fail2ban/jail.d/$app.conf"
@ -111,8 +110,7 @@ ignoreregex =
ynh_systemd_action --service_name=fail2ban --action=reload --line_match="(Started|Reloaded) Fail2Ban Service" --log_path=systemd
local fail2ban_error="$(journalctl --no-hostname --unit=fail2ban | tail --lines=50 | grep "WARNING.*$app.*")"
if [[ -n "$fail2ban_error" ]]
then
if [[ -n "$fail2ban_error" ]]; then
ynh_print_err --message="Fail2ban failed to load the jail for $app"
ynh_print_warn --message="${fail2ban_error#*WARNING}"
fi
@ -123,7 +121,7 @@ ignoreregex =
# usage: ynh_remove_fail2ban_config
#
# Requires YunoHost version 3.5.0 or higher.
ynh_remove_fail2ban_config () {
ynh_remove_fail2ban_config() {
ynh_secure_remove --file="/etc/fail2ban/jail.d/$app.conf"
ynh_secure_remove --file="/etc/fail2ban/filter.d/$app.conf"
ynh_systemd_action --service_name=fail2ban --action=reload

View file

@ -45,11 +45,10 @@
# e.g. for `my_helper "val1" val2`, arg1 will be filled with val1, and arg2 with val2.
#
# Requires YunoHost version 3.2.2 or higher.
ynh_handle_getopts_args () {
ynh_handle_getopts_args() {
# Manage arguments only if there's some provided
set +o xtrace # set +x
if [ $# -ne 0 ]
then
if [ $# -ne 0 ]; then
# Store arguments in an array to keep each argument separated
local arguments=("$@")
@ -58,14 +57,12 @@ ynh_handle_getopts_args () {
# ${!args_array[@]} is the list of all option_flags in the array (An option_flag is 'u' in [u]=user, user is a value)
local getopts_parameters=""
local option_flag=""
for option_flag in "${!args_array[@]}"
do
for option_flag in "${!args_array[@]}"; do
# Concatenate each option_flags of the array to build the string of arguments for getopts
# Will looks like 'abcd' for -a -b -c -d
# If the value of an option_flag finish by =, it's an option with additionnal values. (e.g. --user bob or -u bob)
# Check the last character of the value associate to the option_flag
if [ "${args_array[$option_flag]: -1}" = "=" ]
then
if [ "${args_array[$option_flag]: -1}" = "=" ]; then
# For an option with additionnal values, add a ':' after the letter for getopts.
getopts_parameters="${getopts_parameters}${option_flag}:"
else
@ -74,8 +71,7 @@ ynh_handle_getopts_args () {
# Check each argument given to the function
local arg=""
# ${#arguments[@]} is the size of the array
for arg in `seq 0 $(( ${#arguments[@]} - 1 ))`
do
for arg in $(seq 0 $((${#arguments[@]} - 1))); do
# Escape options' values starting with -. Otherwise the - will be considered as another option.
arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}-/--${args_array[$option_flag]}\\TOBEREMOVED\\-}"
# And replace long option (value of the option_flag) by the short option, the option_flag itself
@ -89,10 +85,9 @@ ynh_handle_getopts_args () {
# Read and parse all the arguments
# Use a function here, to use standart arguments $@ and be able to use shift.
parse_arg () {
parse_arg() {
# Read all arguments, until no arguments are left
while [ $# -ne 0 ]
do
while [ $# -ne 0 ]; do
# Initialize the index of getopts
OPTIND=1
# Parse with getopts only if the argument begin by -, that means the argument is an option
@ -100,11 +95,9 @@ ynh_handle_getopts_args () {
local parameter=""
getopts ":$getopts_parameters" parameter || true
if [ "$parameter" = "?" ]
then
if [ "$parameter" = "?" ]; then
ynh_die --message="Invalid argument: -${OPTARG:-}"
elif [ "$parameter" = ":" ]
then
elif [ "$parameter" = ":" ]; then
ynh_die --message="-$OPTARG parameter requires an argument."
else
local shift_value=1
@ -115,8 +108,7 @@ ynh_handle_getopts_args () {
local option_var="${args_array[$parameter]%=}"
# If this option doesn't take values
# if there's a '=' at the end of the long option name, this option takes values
if [ "${args_array[$parameter]: -1}" != "=" ]
then
if [ "${args_array[$parameter]: -1}" != "=" ]; then
# 'eval ${option_var}' will use the content of 'option_var'
eval ${option_var}=1
else
@ -126,41 +118,35 @@ ynh_handle_getopts_args () {
# If the first argument is longer than 2 characters,
# There's a value attached to the option, in the same array cell
if [ ${#all_args[0]} -gt 2 ]
then
if [ ${#all_args[0]} -gt 2 ]; then
# Remove the option and the space, so keep only the value itself.
all_args[0]="${all_args[0]#-${parameter} }"
# At this point, if all_args[0] start with "-", then the argument is not well formed
if [ "${all_args[0]:0:1}" == "-" ]
then
if [ "${all_args[0]:0:1}" == "-" ]; then
ynh_die --message="Argument \"${all_args[0]}\" not valid! Did you use a single \"-\" instead of two?"
fi
# Reduce the value of shift, because the option has been removed manually
shift_value=$(( shift_value - 1 ))
shift_value=$((shift_value - 1))
fi
# Declare the content of option_var as a variable.
eval ${option_var}=""
# Then read the array value per value
local i
for i in `seq 0 $(( ${#all_args[@]} - 1 ))`
do
for i in $(seq 0 $((${#all_args[@]} - 1))); do
# If this argument is an option, end here.
if [ "${all_args[$i]:0:1}" == "-" ]
then
if [ "${all_args[$i]:0:1}" == "-" ]; then
# Ignore the first value of the array, which is the option itself
if [ "$i" -ne 0 ]; then
break
fi
else
# Ignore empty parameters
if [ -n "${all_args[$i]}" ]
then
if [ -n "${all_args[$i]}" ]; then
# Else, add this value to this option
# Each value will be separated by ';'
if [ -n "${!option_var}" ]
then
if [ -n "${!option_var}" ]; then
# If there's already another value for this option, add a ; before adding the new value
eval ${option_var}+="\;"
fi
@ -177,7 +163,7 @@ ynh_handle_getopts_args () {
eval ${option_var}+='"${all_args[$i]}"'
fi
shift_value=$(( shift_value + 1 ))
shift_value=$((shift_value + 1))
fi
done
fi
@ -190,24 +176,23 @@ ynh_handle_getopts_args () {
# LEGACY MODE
# Check if there's getopts arguments
if [ "${arguments[0]:0:1}" != "-" ]
then
if [ "${arguments[0]:0:1}" != "-" ]; then
# If not, enter in legacy mode and manage the arguments as positionnal ones..
# Dot not echo, to prevent to go through a helper output. But print only in the log.
set -x; echo "! Helper used in legacy mode !" > /dev/null; set +x
set -x
echo "! Helper used in legacy mode !" >/dev/null
set +x
local i
for i in `seq 0 $(( ${#arguments[@]} -1 ))`
do
for i in $(seq 0 $((${#arguments[@]} - 1))); do
# Try to use legacy_args as a list of option_flag of the array args_array
# Otherwise, fallback to getopts_parameters to get the option_flag. But an associative arrays isn't always sorted in the correct order...
# Remove all ':' in getopts_parameters
getopts_parameters=${legacy_args:-${getopts_parameters//:}}
getopts_parameters=${legacy_args:-${getopts_parameters//:/}}
# Get the option_flag from getopts_parameters, by using the option_flag according to the position of the argument.
option_flag=${getopts_parameters:$i:1}
if [ -z "$option_flag" ]
then
ynh_print_warn --message="Too many arguments ! \"${arguments[$i]}\" will be ignored."
continue
if [ -z "$option_flag" ]; then
ynh_print_warn --message="Too many arguments ! \"${arguments[$i]}\" will be ignored."
continue
fi
# Use the long option, corresponding to the option_flag, as a variable
# (e.g. for [u]=user, 'user' will be used as a variable)

View file

@ -10,10 +10,10 @@
# | ret: the amount of free ram, in MB (MegaBytes)
#
# Requires YunoHost version 3.8.1 or higher.
ynh_get_ram () {
ynh_get_ram() {
# Declare an array to define the options of this helper.
local legacy_args=ftso
local -A args_array=( [f]=free [t]=total [s]=ignore_swap [o]=only_swap )
local -A args_array=([f]=free [t]=total [s]=ignore_swap [o]=only_swap)
local free
local total
local ignore_swap
@ -25,41 +25,34 @@ ynh_get_ram () {
free=${free:-0}
total=${total:-0}
if [ $free -eq $total ]
then
if [ $free -eq $total ]; then
ynh_print_warn --message="You have to choose --free or --total when using ynh_get_ram"
ram=0
# Use the total amount of ram
elif [ $free -eq 1 ]
then
elif [ $free -eq 1 ]; then
local free_ram=$(vmstat --stats --unit M | grep "free memory" | awk '{print $1}')
local free_swap=$(vmstat --stats --unit M | grep "free swap" | awk '{print $1}')
local free_ram_swap=$(( free_ram + free_swap ))
local free_ram_swap=$((free_ram + free_swap))
# Use the total amount of free ram
local ram=$free_ram_swap
if [ $ignore_swap -eq 1 ]
then
if [ $ignore_swap -eq 1 ]; then
# Use only the amount of free ram
ram=$free_ram
elif [ $only_swap -eq 1 ]
then
elif [ $only_swap -eq 1 ]; then
# Use only the amount of free swap
ram=$free_swap
fi
elif [ $total -eq 1 ]
then
elif [ $total -eq 1 ]; then
local total_ram=$(vmstat --stats --unit M | grep "total memory" | awk '{print $1}')
local total_swap=$(vmstat --stats --unit M | grep "total swap" | awk '{print $1}')
local total_ram_swap=$(( total_ram + total_swap ))
local total_ram_swap=$((total_ram + total_swap))
local ram=$total_ram_swap
if [ $ignore_swap -eq 1 ]
then
if [ $ignore_swap -eq 1 ]; then
# Use only the amount of free ram
ram=$total_ram
elif [ $only_swap -eq 1 ]
then
elif [ $only_swap -eq 1 ]; then
# Use only the amount of free swap
ram=$total_swap
fi
@ -79,10 +72,10 @@ ynh_get_ram () {
# | ret: 1 if the ram is under the requirement, 0 otherwise.
#
# Requires YunoHost version 3.8.1 or higher.
ynh_require_ram () {
ynh_require_ram() {
# Declare an array to define the options of this helper.
local legacy_args=rftso
local -A args_array=( [r]=required= [f]=free [t]=total [s]=ignore_swap [o]=only_swap )
local -A args_array=([r]=required= [f]=free [t]=total [s]=ignore_swap [o]=only_swap)
local required
local free
local total
@ -100,8 +93,7 @@ ynh_require_ram () {
local ram=$(ynh_get_ram $free $total $ignore_swap $only_swap)
if [ $ram -lt $required ]
then
if [ $ram -lt $required ]; then
return 1
else
return 0

View file

@ -10,7 +10,7 @@
ynh_die() {
# Declare an array to define the options of this helper.
local legacy_args=mc
local -A args_array=( [m]=message= [c]=ret_code= )
local -A args_array=([m]=message= [c]=ret_code=)
local message
local ret_code
# Manage arguments with getopts
@ -30,7 +30,7 @@ ynh_die() {
ynh_print_info() {
# Declare an array to define the options of this helper.
local legacy_args=m
local -A args_array=( [m]=message= )
local -A args_array=([m]=message=)
local message
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -38,31 +38,12 @@ ynh_print_info() {
echo "$message" >&$YNH_STDINFO
}
# Ignore the yunohost-cli log to prevent errors with conditional commands
#
# [internal]
#
# usage: ynh_no_log COMMAND
#
# Simply duplicate the log, execute the yunohost command and replace the log without the result of this command
# It's a very badly hack...
#
# Requires YunoHost version 2.6.4 or higher.
ynh_no_log() {
local ynh_cli_log=/var/log/yunohost/yunohost-cli.log
cp --archive ${ynh_cli_log} ${ynh_cli_log}-move
eval $@
local exit_code=$?
mv ${ynh_cli_log}-move ${ynh_cli_log}
return $exit_code
}
# Main printer, just in case in the future we have to change anything about that.
#
# [internal]
#
# Requires YunoHost version 3.2.0 or higher.
ynh_print_log () {
ynh_print_log() {
echo -e "${1}"
}
@ -72,10 +53,10 @@ ynh_print_log () {
# | arg: -m, --message= - The text to print
#
# Requires YunoHost version 3.2.0 or higher.
ynh_print_warn () {
ynh_print_warn() {
# Declare an array to define the options of this helper.
local legacy_args=m
local -A args_array=( [m]=message= )
local -A args_array=([m]=message=)
local message
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -89,10 +70,10 @@ ynh_print_warn () {
# | arg: -m, --message= - The text to print
#
# Requires YunoHost version 3.2.0 or higher.
ynh_print_err () {
ynh_print_err() {
# Declare an array to define the options of this helper.
local legacy_args=m
local -A args_array=( [m]=message= )
local -A args_array=([m]=message=)
local message
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -102,82 +83,119 @@ ynh_print_err () {
# Execute a command and print the result as an error
#
# usage: ynh_exec_err "your_command [ | other_command ]"
# usage: ynh_exec_err your command and args
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
# Note that you should NOT quote the command but only prefix it with ynh_exec_err
#
# Requires YunoHost version 3.2.0 or higher.
ynh_exec_err () {
ynh_print_err "$(eval $@)"
ynh_exec_err() {
# Boring legacy handling for when people calls ynh_exec_* wrapping the command in quotes,
# (because in the past eval was used) ...
# we detect this by checking that there's no 2nd arg, and $1 contains a space
if [[ "$#" -eq 1 ]] && [[ "$1" == *" "* ]]
then
ynh_print_err "$(eval $@)"
else
# Note that "$@" is used and not $@, c.f. https://unix.stackexchange.com/a/129077
ynh_print_err "$("$@")"
fi
}
# Execute a command and print the result as a warning
#
# usage: ynh_exec_warn "your_command [ | other_command ]"
# usage: ynh_exec_warn your command and args
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
# Note that you should NOT quote the command but only prefix it with ynh_exec_warn
#
# Requires YunoHost version 3.2.0 or higher.
ynh_exec_warn () {
ynh_print_warn "$(eval $@)"
ynh_exec_warn() {
# Boring legacy handling for when people calls ynh_exec_* wrapping the command in quotes,
# (because in the past eval was used) ...
# we detect this by checking that there's no 2nd arg, and $1 contains a space
if [[ "$#" -eq 1 ]] && [[ "$1" == *" "* ]]
then
ynh_print_warn "$(eval $@)"
else
# Note that "$@" is used and not $@, c.f. https://unix.stackexchange.com/a/129077
ynh_print_warn "$("$@")"
fi
}
# Execute a command and force the result to be printed on stdout
#
# usage: ynh_exec_warn_less "your_command [ | other_command ]"
# usage: ynh_exec_warn_less your command and args
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
# Note that you should NOT quote the command but only prefix it with ynh_exec_warn
#
# Requires YunoHost version 3.2.0 or higher.
ynh_exec_warn_less () {
eval $@ 2>&1
ynh_exec_warn_less() {
# Boring legacy handling for when people calls ynh_exec_* wrapping the command in quotes,
# (because in the past eval was used) ...
# we detect this by checking that there's no 2nd arg, and $1 contains a space
if [[ "$#" -eq 1 ]] && [[ "$1" == *" "* ]]
then
eval $@ 2>&1
else
# Note that "$@" is used and not $@, c.f. https://unix.stackexchange.com/a/129077
"$@" 2>&1
fi
}
# Execute a command and redirect stdout in /dev/null
#
# usage: ynh_exec_quiet "your_command [ | other_command ]"
# usage: ynh_exec_quiet your command and args
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
# Note that you should NOT quote the command but only prefix it with ynh_exec_warn
#
# Requires YunoHost version 3.2.0 or higher.
ynh_exec_quiet () {
eval $@ > /dev/null
ynh_exec_quiet() {
# Boring legacy handling for when people calls ynh_exec_* wrapping the command in quotes,
# (because in the past eval was used) ...
# we detect this by checking that there's no 2nd arg, and $1 contains a space
if [[ "$#" -eq 1 ]] && [[ "$1" == *" "* ]]
then
eval $@ > /dev/null
else
# Note that "$@" is used and not $@, c.f. https://unix.stackexchange.com/a/129077
"$@" > /dev/null
fi
}
# Execute a command and redirect stdout and stderr in /dev/null
#
# usage: ynh_exec_fully_quiet "your_command [ | other_command ]"
# usage: ynh_exec_quiet your command and args
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
# Note that you should NOT quote the command but only prefix it with ynh_exec_quiet
#
# Requires YunoHost version 3.2.0 or higher.
ynh_exec_fully_quiet () {
eval $@ > /dev/null 2>&1
ynh_exec_fully_quiet() {
# Boring legacy handling for when people calls ynh_exec_* wrapping the command in quotes,
# (because in the past eval was used) ...
# we detect this by checking that there's no 2nd arg, and $1 contains a space
if [[ "$#" -eq 1 ]] && [[ "$1" == *" "* ]]
then
eval $@ > /dev/null 2>&1
else
# Note that "$@" is used and not $@, c.f. https://unix.stackexchange.com/a/129077
"$@" > /dev/null 2>&1
fi
}
# Remove any logs for all the following commands.
#
# usage: ynh_print_OFF
#
# [internal]
#
# WARNING: You should be careful with this helper, and never forget to use ynh_print_ON as soon as possible to restore the logging.
#
# Requires YunoHost version 3.2.0 or higher.
ynh_print_OFF () {
ynh_print_OFF() {
exec {BASH_XTRACEFD}>/dev/null
}
@ -185,11 +203,13 @@ ynh_print_OFF () {
#
# usage: ynh_print_ON
#
# [internal]
#
# Requires YunoHost version 3.2.0 or higher.
ynh_print_ON () {
ynh_print_ON() {
exec {BASH_XTRACEFD}>&1
# Print an echo only for the log, to be able to know that ynh_print_ON has been called.
echo ynh_print_ON > /dev/null
echo ynh_print_ON >/dev/null
}
# Initial definitions for ynh_script_progression
@ -214,11 +234,11 @@ base_time=$(date +%s)
# | arg: -l, --last - Use for the last call of the helper, to fill the progression bar.
#
# Requires YunoHost version 3.5.0 or higher.
ynh_script_progression () {
ynh_script_progression() {
set +o xtrace # set +x
# Declare an array to define the options of this helper.
local legacy_args=mwtl
local -A args_array=( [m]=message= [w]=weight= [t]=time [l]=last )
local -A args_array=([m]=message= [w]=weight= [t]=time [l]=last)
local message
local weight
local time
@ -232,12 +252,11 @@ ynh_script_progression () {
last=${last:-0}
# Get execution time since the last $base_time
local exec_time=$(( $(date +%s) - $base_time ))
local exec_time=$(($(date +%s) - $base_time))
base_time=$(date +%s)
# Compute $max_progression (if we didn't already)
if [ "$max_progression" = -1 ]
then
if [ "$max_progression" = -1 ]; then
# Get the number of occurrences of 'ynh_script_progression' in the script. Except those are commented.
local helper_calls="$(grep --count "^[^#]*ynh_script_progression" $0)"
# Get the number of call with a weight value
@ -249,23 +268,22 @@ ynh_script_progression () {
local weight_valuesB="$(grep --perl-regexp "^[^#]*ynh_script_progression.*-w " $0 | sed 's/.*-w[= ]\([[:digit:]]*\).*/\1/g')"
# Each value will be on a different line.
# Remove each 'end of line' and replace it by a '+' to sum the values.
local weight_values=$(( $(echo "$weight_valuesA" | tr '\n' '+') + $(echo "$weight_valuesB" | tr '\n' '+') 0 ))
local weight_values=$(($(echo "$weight_valuesA" "$weight_valuesB" | grep -v -E '^\s*$' | tr '\n' '+' | sed 's/+$/+0/g')))
# max_progression is a total number of calls to this helper.
# Less the number of calls with a weight value.
# Plus the total of weight values
max_progression=$(( $helper_calls - $weight_calls + $weight_values ))
max_progression=$(($helper_calls - $weight_calls + $weight_values))
fi
# Increment each execution of ynh_script_progression in this script by the weight of the previous call.
increment_progression=$(( $increment_progression + $previous_weight ))
increment_progression=$(($increment_progression + $previous_weight))
# Store the weight of the current call in $previous_weight for next call
previous_weight=$weight
# Reduce $increment_progression to the size of the scale
if [ $last -eq 0 ]
then
local effective_progression=$(( $increment_progression * $progress_scale / $max_progression ))
if [ $last -eq 0 ]; then
local effective_progression=$(($increment_progression * $progress_scale / $max_progression))
# If last is specified, fill immediately the progression_bar
else
local effective_progression=$progress_scale
@ -273,19 +291,17 @@ ynh_script_progression () {
# Build $progression_bar from progress_string(0,1,2) according to $effective_progression and the weight of the current task
# expected_progression is the progression expected after the current task
local expected_progression="$(( ( $increment_progression + $weight ) * $progress_scale / $max_progression - $effective_progression ))"
if [ $last -eq 1 ]
then
local expected_progression="$((($increment_progression + $weight) * $progress_scale / $max_progression - $effective_progression))"
if [ $last -eq 1 ]; then
expected_progression=0
fi
# left_progression is the progression not yet done
local left_progression="$(( $progress_scale - $effective_progression - $expected_progression ))"
local left_progression="$(($progress_scale - $effective_progression - $expected_progression))"
# Build the progression bar with $effective_progression, work done, $expected_progression, current work and $left_progression, work to be done.
local progression_bar="${progress_string2:0:$effective_progression}${progress_string1:0:$expected_progression}${progress_string0:0:$left_progression}"
local print_exec_time=""
if [ $time -eq 1 ]
then
if [ $time -eq 1 ]; then
print_exec_time=" [$(date +%Hh%Mm,%Ss --date="0 + $exec_time sec")]"
fi
@ -299,73 +315,6 @@ ynh_script_progression () {
# usage: ynh_return somedata
#
# Requires YunoHost version 3.6.0 or higher.
ynh_return () {
echo "$1" >> "$YNH_STDRETURN"
}
# Debugger for app packagers
#
# usage: ynh_debug [--message=message] [--trace=1/0]
# | arg: -m, --message= - The text to print
# | arg: -t, --trace= - Turn on or off the trace of the script. Usefull to trace nonly a small part of a script.
#
# Requires YunoHost version 3.5.0 or higher.
ynh_debug () {
# Disable set xtrace for the helper itself, to not pollute the debug log
set +o xtrace # set +x
# Declare an array to define the options of this helper.
local legacy_args=mt
local -A args_array=( [m]=message= [t]=trace= )
local message
local trace
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Re-disable xtrace, ynh_handle_getopts_args set it back
set +o xtrace # set +x
message=${message:-}
trace=${trace:-}
if [ -n "$message" ]
then
ynh_print_log "[Debug] ${message}" >&2
fi
if [ "$trace" == "1" ]
then
ynh_debug --message="Enable debugging"
set +o xtrace # set +x
# Get the current file descriptor of xtrace
old_bash_xtracefd=$BASH_XTRACEFD
# Add the current file name and the line number of any command currently running while tracing.
PS4='$(basename ${BASH_SOURCE[0]})-L${LINENO}: '
# Force xtrace to stderr
BASH_XTRACEFD=2
# Force stdout to stderr
exec 1>&2
fi
if [ "$trace" == "0" ]
then
ynh_debug --message="Disable debugging"
set +o xtrace # set +x
# Put xtrace back to its original fild descriptor
BASH_XTRACEFD=$old_bash_xtracefd
# Restore stdout
exec 1>&1
fi
# Renable set xtrace
set -o xtrace # set -x
}
# Execute a command and print the result as debug
#
# usage: ynh_debug_exec "your_command [ | other_command ]"
# | arg: command - command to execute
#
# When using pipes, double quotes are required - otherwise, this helper will run the first command, and the whole output will be sent through the next pipe.
#
# If the command to execute uses double quotes, they have to be escaped or they will be interpreted and removed.
#
# Requires YunoHost version 3.5.0 or higher.
ynh_debug_exec () {
ynh_debug --message="$(eval $@)"
}
ynh_return() {
echo "$1" >>"$YNH_STDRETURN"
}

View file

@ -15,10 +15,10 @@
#
# Requires YunoHost version 2.6.4 or higher.
# Requires YunoHost version 3.2.0 or higher for the argument `--specific_user`
ynh_use_logrotate () {
ynh_use_logrotate() {
# Declare an array to define the options of this helper.
local legacy_args=lnuya
local -A args_array=( [l]=logfile= [n]=nonappend [u]=specific_user= [y]=non [a]=append )
local -A args_array=([l]=logfile= [n]=nonappend [u]=specific_user= [y]=non [a]=append)
# [y]=non [a]=append are only for legacy purpose, to not fail on the old option '--non-append'
local logfile
local nonappend
@ -30,22 +30,18 @@ ynh_use_logrotate () {
specific_user="${specific_user:-}"
# LEGACY CODE - PRE GETOPTS
if [ $# -gt 0 ] && [ "$1" == "--non-append" ]
then
if [ $# -gt 0 ] && [ "$1" == "--non-append" ]; then
nonappend=1
# Destroy this argument for the next command.
shift
elif [ $# -gt 1 ] && [ "$2" == "--non-append" ]
then
elif [ $# -gt 1 ] && [ "$2" == "--non-append" ]; then
nonappend=1
fi
if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]
then
if [ $# -gt 0 ] && [ "$(echo ${1:0:1})" != "-" ]; then
# If the given logfile parameter already exists as a file, or if it ends up with ".log",
# we just want to manage a single file
if [ -f "$1" ] || [ "$(echo ${1##*.})" == "log" ]
then
if [ -f "$1" ] || [ "$(echo ${1##*.})" == "log" ]; then
local logfile=$1
# Otherwise we assume we want to manage a directory and all its .log file inside
else
@ -58,22 +54,20 @@ ynh_use_logrotate () {
if [ "$nonappend" -eq 1 ]; then
customtee="tee"
fi
if [ -n "$logfile" ]
then
if [ ! -f "$1" ] && [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile
local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it.
if [ -n "$logfile" ]; then
if [ ! -f "$1" ] && [ "$(echo ${logfile##*.})" != "log" ]; then # Keep only the extension to check if it's a logfile
local logfile="$logfile/*.log" # Else, uses the directory and all logfile into it.
fi
else
logfile="/var/log/${app}/*.log" # Without argument, use a defaut directory in /var/log
fi
local su_directive=""
if [[ -n $specific_user ]]
then
if [[ -n $specific_user ]]; then
su_directive=" # Run logorotate as specific user - group
su ${specific_user%/*} ${specific_user#*/}"
fi
cat > ./${app}-logrotate << EOF # Build a config file for logrotate
cat >./${app}-logrotate <<EOF # Build a config file for logrotate
$logfile {
# Rotate if the logfile exceeds 100Mo
size 100M
@ -94,8 +88,13 @@ $logfile {
$su_directive
}
EOF
mkdir --parents $(dirname "$logfile") # Create the log directory, if not exist
cat ${app}-logrotate | $customtee /etc/logrotate.d/$app > /dev/null # Append this config to the existing config file, or replace the whole config file (depending on $customtee)
mkdir --parents $(dirname "$logfile") # Create the log directory, if not exist
cat ${app}-logrotate | $customtee /etc/logrotate.d/$app >/dev/null # Append this config to the existing config file, or replace the whole config file (depending on $customtee)
if ynh_user_exists --username="$app"; then
chown $app:$app "$logfile"
chmod o-rwx "$logfile"
fi
}
# Remove the app's logrotate config.
@ -103,7 +102,7 @@ EOF
# usage: ynh_remove_logrotate
#
# Requires YunoHost version 2.6.4 or higher.
ynh_remove_logrotate () {
ynh_remove_logrotate() {
if [ -e "/etc/logrotate.d/$app" ]; then
rm "/etc/logrotate.d/$app"
fi

View file

@ -22,8 +22,7 @@ ynh_multimedia_build_main_dir() {
mkdir -p "$MEDIA_DIRECTORY/share/eBook"
## Création des dossiers utilisateurs
for user in $(yunohost user list --output-as json | jq -r '.users | keys[]')
do
for user in $(yunohost user list --output-as json | jq -r '.users | keys[]'); do
mkdir -p "$MEDIA_DIRECTORY/$user"
mkdir -p "$MEDIA_DIRECTORY/$user/Music"
mkdir -p "$MEDIA_DIRECTORY/$user/Picture"
@ -66,22 +65,22 @@ ynh_multimedia_addfolder() {
# Declare an array to define the options of this helper.
local legacy_args=sd
local -A args_array=( [s]=source_dir= [d]=dest_dir= )
local source_dir
local dest_dir
local -A args_array=([s]=source_dir= [d]=dest_dir=)
local source_dir
local dest_dir
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Ajout d'un lien symbolique vers le dossier à partager
ln -sfn "$source_dir" "$MEDIA_DIRECTORY/$dest_dir"
ln -sfn "$source_dir" "$MEDIA_DIRECTORY/$dest_dir"
## Application des droits étendus sur le dossier ajouté
# Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:
setfacl -RnL -m g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.
setfacl -RnL -m d:g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.
setfacl -RL -m m::rwx "$source_dir"
## Application des droits étendus sur le dossier ajouté
# Droit d'écriture pour le groupe et le groupe multimedia en acl et droit de lecture pour other:
setfacl -RnL -m g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Application de la même règle que précédemment, mais par défaut pour les nouveaux fichiers.
setfacl -RnL -m d:g:$MEDIA_GROUP:rwX,g::rwX,o:r-X "$source_dir"
# Réglage du masque par défaut. Qui garantie (en principe...) un droit maximal à rwx. Donc pas de restriction de droits par l'acl.
setfacl -RL -m m::rwx "$source_dir"
}
# Allow an user to have an write authorisation in multimedia directories
@ -91,14 +90,14 @@ ynh_multimedia_addfolder() {
# | arg: -u, --user_name= - The name of the user which gain this access.
#
# Requires YunoHost version 4.2 or higher.
ynh_multimedia_addaccess () {
# Declare an array to define the options of this helper.
ynh_multimedia_addaccess() {
# Declare an array to define the options of this helper.
local legacy_args=u
declare -Ar args_array=( [u]=user_name=)
local user_name
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
declare -Ar args_array=([u]=user_name=)
local user_name
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
groupadd -f multimedia
usermod -a -G multimedia $user_name
groupadd -f multimedia
usermod -a -G multimedia $user_name
}

View file

@ -15,7 +15,7 @@
ynh_mysql_connect_as() {
# Declare an array to define the options of this helper.
local legacy_args=upd
local -A args_array=( [u]=user= [p]=password= [d]=database= )
local -A args_array=([u]=user= [p]=password= [d]=database=)
local user
local password
local database
@ -36,19 +36,18 @@ ynh_mysql_connect_as() {
ynh_mysql_execute_as_root() {
# Declare an array to define the options of this helper.
local legacy_args=sd
local -A args_array=( [s]=sql= [d]=database= )
local -A args_array=([s]=sql= [d]=database=)
local sql
local database
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
database="${database:-}"
if [ -n "$database" ]
then
if [ -n "$database" ]; then
database="--database=$database"
fi
mysql -B "$database" <<< "$sql"
mysql -B "$database" <<<"$sql"
}
# Execute a command from a file as root user
@ -61,19 +60,18 @@ ynh_mysql_execute_as_root() {
ynh_mysql_execute_file_as_root() {
# Declare an array to define the options of this helper.
local legacy_args=fd
local -A args_array=( [f]=file= [d]=database= )
local -A args_array=([f]=file= [d]=database=)
local file
local database
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
database="${database:-}"
if [ -n "$database" ]
then
if [ -n "$database" ]; then
database="--database=$database"
fi
mysql -B "$database" < "$file"
mysql -B "$database" <"$file"
}
# Create a database and grant optionnaly privilegies to a user
@ -92,8 +90,7 @@ ynh_mysql_create_db() {
local sql="CREATE DATABASE ${db};"
# grant all privilegies to user
if [[ $# -gt 1 ]]
then
if [[ $# -gt 1 ]]; then
sql+=" GRANT ALL PRIVILEGES ON ${db}.* TO '${2}'@'localhost'"
if [[ -n ${3:-} ]]; then
sql+=" IDENTIFIED BY '${3}'"
@ -131,7 +128,7 @@ ynh_mysql_drop_db() {
ynh_mysql_dump_db() {
# Declare an array to define the options of this helper.
local legacy_args=d
local -A args_array=( [d]=database= )
local -A args_array=([d]=database=)
local database
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -160,17 +157,15 @@ ynh_mysql_create_user() {
# | ret: 0 if the user exists, 1 otherwise.
#
# Requires YunoHost version 2.2.4 or higher.
ynh_mysql_user_exists()
{
ynh_mysql_user_exists() {
# Declare an array to define the options of this helper.
local legacy_args=u
local -A args_array=( [u]=user= )
local -A args_array=([u]=user=)
local user
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if [[ -z $(ynh_mysql_execute_as_root --sql="SELECT User from mysql.user WHERE User = '$user';") ]]
then
if [[ -z $(ynh_mysql_execute_as_root --sql="SELECT User from mysql.user WHERE User = '$user';") ]]; then
return 1
else
return 0
@ -200,10 +195,10 @@ ynh_mysql_drop_user() {
# It will also be stored as "`mysqlpwd`" into the app settings.
#
# Requires YunoHost version 2.6.4 or higher.
ynh_mysql_setup_db () {
ynh_mysql_setup_db() {
# Declare an array to define the options of this helper.
local legacy_args=unp
local -A args_array=( [u]=db_user= [n]=db_name= [p]=db_pwd= )
local -A args_array=([u]=db_user= [n]=db_name= [p]=db_pwd=)
local db_user
local db_name
db_pwd=""
@ -226,10 +221,10 @@ ynh_mysql_setup_db () {
# | arg: -n, --db_name= - Name of the database
#
# Requires YunoHost version 2.6.4 or higher.
ynh_mysql_remove_db () {
ynh_mysql_remove_db() {
# Declare an array to define the options of this helper.
local legacy_args=un
local -Ar args_array=( [u]=db_user= [n]=db_name= )
local -Ar args_array=([u]=db_user= [n]=db_name=)
local db_user
local db_name
# Manage arguments with getopts

View file

@ -9,18 +9,17 @@
# example: port=$(ynh_find_port --port=8080)
#
# Requires YunoHost version 2.6.4 or higher.
ynh_find_port () {
ynh_find_port() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=port= )
local -A args_array=([p]=port=)
local port
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
test -n "$port" || ynh_die --message="The argument of ynh_find_port must be a valid port."
while ! ynh_port_available --port=$port
do
port=$((port+1))
while ! ynh_port_available --port=$port; do
port=$((port + 1))
done
echo $port
}
@ -34,28 +33,25 @@ ynh_find_port () {
# example: ynh_port_available --port=1234 || ynh_die --message="Port 1234 is needs to be available for this app"
#
# Requires YunoHost version 3.8.0 or higher.
ynh_port_available () {
ynh_port_available() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=port= )
local -A args_array=([p]=port=)
local port
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Check if the port is free
if ss --numeric --listening --tcp --udp | awk '{print$5}' | grep --quiet --extended-regexp ":$port$"
then
if ss --numeric --listening --tcp --udp | awk '{print$5}' | grep --quiet --extended-regexp ":$port$"; then
return 1
# This is to cover (most) case where an app is using a port yet ain't currently using it for some reason (typically service ain't up)
elif grep -q "port: '$port'" /etc/yunohost/apps/*/settings.yml
then
elif grep -q "port: '$port'" /etc/yunohost/apps/*/settings.yml; then
return 1
else
return 0
fi
}
# Validate an IP address
#
# [internal]
@ -66,13 +62,12 @@ ynh_port_available () {
# example: ynh_validate_ip 4 111.222.333.444
#
# Requires YunoHost version 2.2.4 or higher.
ynh_validate_ip()
{
ynh_validate_ip() {
# http://stackoverflow.com/questions/319279/how-to-validate-ip-address-in-python#319298
# Declare an array to define the options of this helper.
local legacy_args=fi
local -A args_array=( [f]=family= [i]=ip_address= )
local -A args_array=([f]=family= [i]=ip_address=)
local family
local ip_address
# Manage arguments with getopts
@ -80,7 +75,7 @@ ynh_validate_ip()
[ "$family" == "4" ] || [ "$family" == "6" ] || return 1
python3 /dev/stdin << EOF
python3 /dev/stdin <<EOF
import socket
import sys
family = { "4" : socket.AF_INET, "6" : socket.AF_INET6 }
@ -101,11 +96,10 @@ EOF
# example: ynh_validate_ip4 111.222.333.444
#
# Requires YunoHost version 2.2.4 or higher.
ynh_validate_ip4()
{
ynh_validate_ip4() {
# Declare an array to define the options of this helper.
local legacy_args=i
local -A args_array=( [i]=ip_address= )
local -A args_array=([i]=ip_address=)
local ip_address
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -113,7 +107,6 @@ ynh_validate_ip4()
ynh_validate_ip --family=4 --ip_address=$ip_address
}
# Validate an IPv6 address
#
# usage: ynh_validate_ip6 --ip_address=ip_address
@ -123,11 +116,10 @@ ynh_validate_ip4()
# example: ynh_validate_ip6 2000:dead:beef::1
#
# Requires YunoHost version 2.2.4 or higher.
ynh_validate_ip6()
{
ynh_validate_ip6() {
# Declare an array to define the options of this helper.
local legacy_args=i
local -A args_array=( [i]=ip_address= )
local -A args_array=([i]=ip_address=)
local ip_address
# Manage arguments with getopts
ynh_handle_getopts_args "$@"

View file

@ -16,12 +16,11 @@
# location
#
# Requires YunoHost version 4.1.0 or higher.
ynh_add_nginx_config () {
ynh_add_nginx_config() {
local finalnginxconf="/etc/nginx/conf.d/$domain.d/$app.conf"
if [ "${path_url:-}" != "/" ]
then
if [ "${path_url:-}" != "/" ]; then
ynh_replace_string --match_string="^#sub_path_only" --replace_string="" --target_file="$YNH_APP_BASEDIR/conf/nginx.conf"
else
ynh_replace_string --match_string="^#root_path_only" --replace_string="" --target_file="$YNH_APP_BASEDIR/conf/nginx.conf"
@ -29,7 +28,6 @@ ynh_add_nginx_config () {
ynh_add_config --template="$YNH_APP_BASEDIR/conf/nginx.conf" --destination="$finalnginxconf"
ynh_systemd_action --service_name=nginx --action=reload
}
@ -38,7 +36,7 @@ ynh_add_nginx_config () {
# usage: ynh_remove_nginx_config
#
# Requires YunoHost version 2.7.2 or higher.
ynh_remove_nginx_config () {
ynh_remove_nginx_config() {
ynh_secure_remove --file="/etc/nginx/conf.d/$domain.d/$app.conf"
ynh_systemd_action --service_name=nginx --action=reload
}

View file

@ -1,6 +1,6 @@
#!/bin/bash
n_version=7.3.0
n_version=7.5.0
n_install_dir="/opt/node_n"
node_version_path="$n_install_dir/n/versions/node"
# N_PREFIX is the directory of n, it needs to be loaded as a environment variable.
@ -13,16 +13,18 @@ export N_PREFIX="$n_install_dir"
# usage: ynh_install_n
#
# Requires YunoHost version 2.7.12 or higher.
ynh_install_n () {
ynh_install_n() {
ynh_print_info --message="Installation of N - Node.js version management"
# Build an app.src for n
echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz
SOURCE_SUM=b908b0fc86922ede37e89d1030191285209d7d521507bf136e62895e5797847f" > "$YNH_APP_BASEDIR/conf/n.src"
SOURCE_SUM=d4da7ea91f680de0c9b5876e097e2a793e8234fcd0f7ca87a0599b925be087a3" >"$YNH_APP_BASEDIR/conf/n.src"
# Download and extract n
ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n
# Install n
(cd "$n_install_dir/git"
PREFIX=$N_PREFIX make install 2>&1)
(
cd "$n_install_dir/git"
PREFIX=$N_PREFIX make install 2>&1
)
}
# Load the version of node for an app, and set variables.
@ -69,7 +71,7 @@ SOURCE_SUM=b908b0fc86922ede37e89d1030191285209d7d521507bf136e62895e5797847f" > "
# - $nodejs_version: Just the version number of node for this app. Stored as 'nodejs_version' in settings.yml.
#
# Requires YunoHost version 2.7.12 or higher.
ynh_use_nodejs () {
ynh_use_nodejs() {
nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)
# Get the absolute path of this version of node
@ -109,12 +111,12 @@ ynh_use_nodejs () {
# Refer to `ynh_use_nodejs` for more information about available commands and variables
#
# Requires YunoHost version 2.7.12 or higher.
ynh_install_nodejs () {
ynh_install_nodejs() {
# Use n, https://github.com/tj/n to manage the nodejs versions
# Declare an array to define the options of this helper.
local legacy_args=n
local -A args_array=( [n]=nodejs_version= )
local -A args_array=([n]=nodejs_version=)
local nodejs_version
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -132,11 +134,9 @@ ynh_install_nodejs () {
test -x /usr/bin/npm && mv /usr/bin/npm /usr/bin/npm_n
# If n is not previously setup, install it
if ! $n_install_dir/bin/n --version > /dev/null 2>&1
then
if ! $n_install_dir/bin/n --version >/dev/null 2>&1; then
ynh_install_n
elif dpkg --compare-versions "$($n_install_dir/bin/n --version)" lt $n_version
then
elif dpkg --compare-versions "$($n_install_dir/bin/n --version)" lt $n_version; then
ynh_install_n
fi
@ -152,8 +152,7 @@ ynh_install_nodejs () {
# Install the requested version of nodejs
uname=$(uname --machine)
if [[ $uname =~ aarch64 || $uname =~ arm64 ]]
then
if [[ $uname =~ aarch64 || $uname =~ arm64 ]]; then
n $nodejs_version --arch=arm64
else
n $nodejs_version
@ -164,8 +163,7 @@ ynh_install_nodejs () {
real_nodejs_version=$(basename $real_nodejs_version)
# Create a symbolic link for this major version if the file doesn't already exist
if [ ! -e "$node_version_path/$nodejs_version" ]
then
if [ ! -e "$node_version_path/$nodejs_version" ]; then
ln --symbolic --force --no-target-directory $node_version_path/$real_nodejs_version $node_version_path/$nodejs_version
fi
@ -190,21 +188,19 @@ ynh_install_nodejs () {
# - If no other app uses node, n will be also removed.
#
# Requires YunoHost version 2.7.12 or higher.
ynh_remove_nodejs () {
ynh_remove_nodejs() {
nodejs_version=$(ynh_app_setting_get --app=$app --key=nodejs_version)
# Remove the line for this app
sed --in-place "/$YNH_APP_INSTANCE_NAME:$nodejs_version/d" "$n_install_dir/ynh_app_version"
# If no other app uses this version of nodejs, remove it.
if ! grep --quiet "$nodejs_version" "$n_install_dir/ynh_app_version"
then
if ! grep --quiet "$nodejs_version" "$n_install_dir/ynh_app_version"; then
$n_install_dir/bin/n rm $nodejs_version
fi
# If no other app uses n, remove n
if [ ! -s "$n_install_dir/ynh_app_version" ]
then
if [ ! -s "$n_install_dir/ynh_app_version" ]; then
ynh_secure_remove --file="$n_install_dir"
ynh_secure_remove --file="/usr/local/n"
sed --in-place "/N_PREFIX/d" /root/.bashrc
@ -221,9 +217,9 @@ ynh_remove_nodejs () {
# usage: ynh_cron_upgrade_node
#
# Requires YunoHost version 2.7.12 or higher.
ynh_cron_upgrade_node () {
ynh_cron_upgrade_node() {
# Build the update script
cat > "$n_install_dir/node_update.sh" << EOF
cat >"$n_install_dir/node_update.sh" <<EOF
#!/bin/bash
version_path="$node_version_path"
@ -259,7 +255,7 @@ EOF
chmod +x "$n_install_dir/node_update.sh"
# Build the cronjob
cat > "/etc/cron.daily/node_update" << EOF
cat >"/etc/cron.daily/node_update" <<EOF
#!/bin/bash
$n_install_dir/node_update.sh >> $n_install_dir/node_update.log

View file

@ -66,7 +66,7 @@
ynh_permission_create() {
# Declare an array to define the options of this helper.
local legacy_args=puAhaltP
local -A args_array=( [p]=permission= [u]=url= [A]=additional_urls= [h]=auth_header= [a]=allowed= [l]=label= [t]=show_tile= [P]=protected= )
local -A args_array=([p]=permission= [u]=url= [A]=additional_urls= [h]=auth_header= [a]=allowed= [l]=label= [t]=show_tile= [P]=protected=)
local permission
local url
local additional_urls
@ -84,13 +84,11 @@ ynh_permission_create() {
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $url ]]
then
if [[ -n $url ]]; then
url=",url='$url'"
fi
if [[ -n $additional_urls ]]
then
if [[ -n $additional_urls ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
@ -100,18 +98,15 @@ ynh_permission_create() {
additional_urls=",additional_urls=['${additional_urls//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
if [[ -n $auth_header ]]; then
if [ $auth_header == "true" ]; then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $allowed ]]
then
if [[ -n $allowed ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# By example:
@ -127,20 +122,16 @@ ynh_permission_create() {
label=",label='$permission'"
fi
if [[ -n ${show_tile:-} ]]
then
if [ $show_tile == "true" ]
then
if [[ -n ${show_tile:-} ]]; then
if [ $show_tile == "true" ]; then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
fi
fi
if [[ -n ${protected:-} ]]
then
if [ $protected == "true" ]
then
if [[ -n ${protected:-} ]]; then
if [ $protected == "true" ]; then
protected=",protected=True"
else
protected=",protected=False"
@ -161,7 +152,7 @@ ynh_permission_create() {
ynh_permission_delete() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local -A args_array=([p]=permission=)
local permission
ynh_handle_getopts_args "$@"
@ -178,7 +169,7 @@ ynh_permission_delete() {
ynh_permission_exists() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=permission= )
local -A args_array=([p]=permission=)
local permission
ynh_handle_getopts_args "$@"
@ -201,7 +192,7 @@ ynh_permission_exists() {
ynh_permission_url() {
# Declare an array to define the options of this helper.
local legacy_args=puarhc
local -A args_array=( [p]=permission= [u]=url= [a]=add_url= [r]=remove_url= [h]=auth_header= [c]=clear_urls )
local -A args_array=([p]=permission= [u]=url= [a]=add_url= [r]=remove_url= [h]=auth_header= [c]=clear_urls)
local permission
local url
local add_url
@ -215,13 +206,11 @@ ynh_permission_url() {
auth_header=${auth_header:-}
clear_urls=${clear_urls:-}
if [[ -n $url ]]
then
if [[ -n $url ]]; then
url=",url='$url'"
fi
if [[ -n $add_url ]]
then
if [[ -n $add_url ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
@ -231,8 +220,7 @@ ynh_permission_url() {
add_url=",add_url=['${add_url//;/\',\'}']"
fi
if [[ -n $remove_url ]]
then
if [[ -n $remove_url ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
@ -242,25 +230,21 @@ ynh_permission_url() {
remove_url=",remove_url=['${remove_url//;/\',\'}']"
fi
if [[ -n $auth_header ]]
then
if [ $auth_header == "true" ]
then
if [[ -n $auth_header ]]; then
if [ $auth_header == "true" ]; then
auth_header=",auth_header=True"
else
auth_header=",auth_header=False"
fi
fi
if [[ -n $clear_urls ]] && [ $clear_urls -eq 1 ]
then
if [[ -n $clear_urls ]] && [ $clear_urls -eq 1 ]; then
clear_urls=",clear_urls=True"
fi
yunohost tools shell -c "from yunohost.permission import permission_url; permission_url('$app.$permission' $url $add_url $remove_url $auth_header $clear_urls)"
}
# Update a permission for the app
#
# usage: ynh_permission_update --permission "permission" [--add="group" ["group" ...]] [--remove="group" ["group" ...]]
@ -276,7 +260,7 @@ ynh_permission_url() {
ynh_permission_update() {
# Declare an array to define the options of this helper.
local legacy_args=parltP
local -A args_array=( [p]=permission= [a]=add= [r]=remove= [l]=label= [t]=show_tile= [P]=protected= )
local -A args_array=([p]=permission= [a]=add= [r]=remove= [l]=label= [t]=show_tile= [P]=protected=)
local permission
local add
local remove
@ -290,8 +274,7 @@ ynh_permission_update() {
show_tile=${show_tile:-}
protected=${protected:-}
if [[ -n $add ]]
then
if [[ -n $add ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
@ -300,8 +283,7 @@ ynh_permission_update() {
# add=['alice', 'bob']
add=",add=['${add//';'/"','"}']"
fi
if [[ -n $remove ]]
then
if [[ -n $remove ]]; then
# Convert a list from getopts to python list
# Note that getopts separate the args with ';'
# For example:
@ -311,15 +293,12 @@ ynh_permission_update() {
remove=",remove=['${remove//';'/"','"}']"
fi
if [[ -n $label ]]
then
if [[ -n $label ]]; then
label=",label='$label'"
fi
if [[ -n $show_tile ]]
then
if [ $show_tile == "true" ]
then
if [[ -n $show_tile ]]; then
if [ $show_tile == "true" ]; then
show_tile=",show_tile=True"
else
show_tile=",show_tile=False"
@ -327,8 +306,7 @@ ynh_permission_update() {
fi
if [[ -n $protected ]]; then
if [ $protected == "true" ]
then
if [ $protected == "true" ]; then
protected=",protected=True"
else
protected=",protected=False"
@ -351,23 +329,20 @@ ynh_permission_update() {
ynh_permission_has_user() {
local legacy_args=pu
# Declare an array to define the options of this helper.
local -A args_array=( [p]=permission= [u]=user= )
local -A args_array=([p]=permission= [u]=user=)
local permission
local user
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! ynh_permission_exists --permission=$permission
then
if ! ynh_permission_exists --permission=$permission; then
return 1
fi
# Check both allowed and corresponding_users sections in the json
for section in "allowed" "corresponding_users"
do
for section in "allowed" "corresponding_users"; do
if yunohost user permission info "$app.$permission" --output-as json --quiet \
| jq -e --arg user $user --arg section $section '.[$section] | index($user)' >/dev/null
then
| jq -e --arg user $user --arg section $section '.[$section] | index($user)' >/dev/null; then
return 0
fi
done
@ -381,9 +356,8 @@ ynh_permission_has_user() {
# | exit: Return 1 if the permission doesn't exist, 0 otherwise
#
# Requires YunoHost version 4.1.2 or higher.
ynh_legacy_permissions_exists () {
for permission in "skipped" "unprotected" "protected"
do
ynh_legacy_permissions_exists() {
for permission in "skipped" "unprotected" "protected"; do
if ynh_permission_exists --permission="legacy_${permission}_uris"; then
return 0
fi
@ -402,9 +376,8 @@ ynh_legacy_permissions_exists () {
# # You can recreate the required permissions here with ynh_permission_create
# fi
# Requires YunoHost version 4.1.2 or higher.
ynh_legacy_permissions_delete_all () {
for permission in "skipped" "unprotected" "protected"
do
ynh_legacy_permissions_delete_all() {
for permission in "skipped" "unprotected" "protected"; do
if ynh_permission_exists --permission="legacy_${permission}_uris"; then
ynh_permission_delete --permission="legacy_${permission}_uris"
fi

View file

@ -56,10 +56,10 @@ YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
# children ready to answer.
#
# Requires YunoHost version 4.1.0 or higher.
ynh_add_fpm_config () {
ynh_add_fpm_config() {
# Declare an array to define the options of this helper.
local legacy_args=vtufpd
local -A args_array=( [v]=phpversion= [t]=use_template [u]=usage= [f]=footprint= [p]=package= [d]=dedicated_service )
local -A args_array=([v]=phpversion= [t]=use_template [u]=usage= [f]=footprint= [p]=package= [d]=dedicated_service)
local phpversion
local use_template
local usage
@ -86,8 +86,7 @@ ynh_add_fpm_config () {
local old_phpversion=$(ynh_app_setting_get --app=$app --key=phpversion)
# If the PHP version changed, remove the old fpm conf
if [ -n "$old_phpversion" ] && [ "$old_phpversion" != "$phpversion" ]
then
if [ -n "$old_phpversion" ] && [ "$old_phpversion" != "$phpversion" ]; then
local old_php_fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir)
local old_php_finalphpconf="$old_php_fpm_config_dir/pool.d/$app.conf"
@ -97,25 +96,21 @@ ynh_add_fpm_config () {
fi
# If the requested PHP version is not the default version for YunoHost
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]
then
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]; then
# If the argument --package is used, add the packages to ynh_install_php to install them from sury
if [ -n "$package" ]
then
if [ -n "$package" ]; then
local additionnal_packages="--package=$package"
else
local additionnal_packages=""
fi
# Install this specific version of PHP.
ynh_install_php --phpversion="$phpversion" "$additionnal_packages"
elif [ -n "$package" ]
then
elif [ -n "$package" ]; then
# Install the additionnal packages from the default repository
ynh_add_app_dependencies --package="$package"
ynh_install_app_dependencies "$package"
fi
if [ $dedicated_service -eq 1 ]
then
if [ $dedicated_service -eq 1 ]; then
local fpm_service="${app}-phpfpm"
local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm"
else
@ -132,12 +127,10 @@ ynh_add_fpm_config () {
ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion
# Migrate from mutual PHP service to dedicated one.
if [ $dedicated_service -eq 1 ]
then
if [ $dedicated_service -eq 1 ]; then
local old_fpm_config_dir="/etc/php/$phpversion/fpm"
# If a config file exist in the common pool, move it.
if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ]
then
if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ]; then
ynh_print_info --message="Migrate to a dedicated php-fpm service for $app."
# Create a backup of the old file before migration
ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf"
@ -148,8 +141,7 @@ ynh_add_fpm_config () {
fi
fi
if [ $use_template -eq 1 ]
then
if [ $use_template -eq 1 ]; then
# Usage 1, use the template in conf/php-fpm.conf
local phpfpm_path="$YNH_APP_BASEDIR/conf/php-fpm.conf"
# Make sure now that the template indeed exists
@ -181,49 +173,45 @@ pm = __PHP_PM__
pm.max_children = __PHP_MAX_CHILDREN__
pm.max_requests = 500
request_terminate_timeout = 1d
" > $phpfpm_path
" >$phpfpm_path
if [ "$php_pm" = "dynamic" ]
then
if [ "$php_pm" = "dynamic" ]; then
echo "
pm.start_servers = __PHP_START_SERVERS__
pm.min_spare_servers = __PHP_MIN_SPARE_SERVERS__
pm.max_spare_servers = __PHP_MAX_SPARE_SERVERS__
" >> $phpfpm_path
" >>$phpfpm_path
elif [ "$php_pm" = "ondemand" ]
then
elif [ "$php_pm" = "ondemand" ]; then
echo "
pm.process_idle_timeout = 10s
" >> $phpfpm_path
" >>$phpfpm_path
fi
# Concatene the extra config.
if [ -e $YNH_APP_BASEDIR/conf/extra_php-fpm.conf ]; then
cat $YNH_APP_BASEDIR/conf/extra_php-fpm.conf >> "$phpfpm_path"
cat $YNH_APP_BASEDIR/conf/extra_php-fpm.conf >>"$phpfpm_path"
fi
fi
local finalphpconf="$fpm_config_dir/pool.d/$app.conf"
ynh_add_config --template="$phpfpm_path" --destination="$finalphpconf"
if [ -e "$YNH_APP_BASEDIR/conf/php-fpm.ini" ]
then
if [ -e "$YNH_APP_BASEDIR/conf/php-fpm.ini" ]; then
ynh_print_warn --message="Packagers ! Please do not use a separate php ini file, merge your directives in the pool file instead."
ynh_add_config --template="$YNH_APP_BASEDIR/conf/php-fpm.ini" --destination="$fpm_config_dir/conf.d/20-$app.ini"
fi
if [ $dedicated_service -eq 1 ]
then
if [ $dedicated_service -eq 1 ]; then
# Create a dedicated php-fpm.conf for the service
local globalphpconf=$fpm_config_dir/php-fpm-$app.conf
echo "[global]
echo "[global]
pid = /run/php/php__PHPVERSION__-fpm-__APP__.pid
error_log = /var/log/php/fpm-php.__APP__.log
syslog.ident = php-fpm-__APP__
include = __FINALPHPCONF__
" > $YNH_APP_BASEDIR/conf/php-fpm-$app.conf
" >$YNH_APP_BASEDIR/conf/php-fpm-$app.conf
ynh_add_config --template="$YNH_APP_BASEDIR/conf/php-fpm-$app.conf" --destination="$globalphpconf"
@ -240,7 +228,7 @@ ExecReload=/bin/kill -USR2 \$MAINPID
[Install]
WantedBy=multi-user.target
" > $YNH_APP_BASEDIR/conf/$fpm_service
" >$YNH_APP_BASEDIR/conf/$fpm_service
# Create this dedicated PHP-FPM service
ynh_add_systemd_config --service=$fpm_service --template=$fpm_service
@ -252,8 +240,7 @@ WantedBy=multi-user.target
ynh_systemd_action --service_name=$fpm_service --action=restart
else
# Validate that the new php conf doesn't break php-fpm entirely
if ! php-fpm${phpversion} --test 2>/dev/null
then
if ! php-fpm${phpversion} --test 2>/dev/null; then
php-fpm${phpversion} --test || true
ynh_secure_remove --file="$finalphpconf"
ynh_die --message="The new configuration broke php-fpm?"
@ -267,7 +254,7 @@ WantedBy=multi-user.target
# usage: ynh_remove_fpm_config
#
# Requires YunoHost version 2.7.2 or higher.
ynh_remove_fpm_config () {
ynh_remove_fpm_config() {
local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir)
local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service)
local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service)
@ -279,20 +266,17 @@ ynh_remove_fpm_config () {
phpversion="${phpversion:-$YNH_DEFAULT_PHP_VERSION}"
# Assume default PHP files if not set
if [ -z "$fpm_config_dir" ]
then
if [ -z "$fpm_config_dir" ]; then
fpm_config_dir="/etc/php/$YNH_DEFAULT_PHP_VERSION/fpm"
fpm_service="php$YNH_DEFAULT_PHP_VERSION-fpm"
fi
ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf"
if [ -e $fpm_config_dir/conf.d/20-$app.ini ]
then
if [ -e $fpm_config_dir/conf.d/20-$app.ini ]; then
ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini"
fi
if [ $dedicated_service -eq 1 ]
then
if [ $dedicated_service -eq 1 ]; then
# Remove the dedicated service PHP-FPM service for the app
ynh_remove_systemd_config --service=$fpm_service
# Remove the global PHP-FPM conf
@ -304,8 +288,7 @@ ynh_remove_fpm_config () {
fi
# If the PHP version used is not the default version for YunoHost
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]
then
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]; then
# Remove this specific version of PHP
ynh_remove_php
fi
@ -320,47 +303,22 @@ ynh_remove_fpm_config () {
# | arg: -p, --package= - Additionnal PHP packages to install
#
# Requires YunoHost version 3.8.1 or higher.
ynh_install_php () {
ynh_install_php() {
# Declare an array to define the options of this helper.
local legacy_args=vp
local -A args_array=( [v]=phpversion= [p]=package= )
local -A args_array=([v]=phpversion= [p]=package=)
local phpversion
local package
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
package=${package:-}
# Store phpversion into the config of this app
ynh_app_setting_set $app phpversion $phpversion
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]
then
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]; then
ynh_die --message="Do not use ynh_install_php to install php$YNH_DEFAULT_PHP_VERSION"
fi
# Create the file if doesn't exist already
touch /etc/php/ynh_app_version
# Do not add twice the same line
if ! grep --quiet "$YNH_APP_INSTANCE_NAME:" "/etc/php/ynh_app_version"
then
# Store the ID of this app and the version of PHP requested for it
echo "$YNH_APP_INSTANCE_NAME:$phpversion" | tee --append "/etc/php/ynh_app_version"
fi
# Add an extra repository for those packages
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
# Install requested dependencies from this extra repository.
# Install PHP-FPM first, otherwise PHP will install apache as a dependency.
ynh_add_app_dependencies --package="php${phpversion}-fpm"
ynh_add_app_dependencies --package="php$phpversion php${phpversion}-common $package"
# Set the default PHP version back as the default version for php-cli.
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION
# Advertise service in admin panel
yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log"
ynh_install_app_dependencies "$package"
ynh_app_setting_set --app=$app --key=phpversion --value=$specific_php_version
}
# Remove the specific version of PHP used by the app.
@ -371,35 +329,7 @@ ynh_install_php () {
#
# Requires YunoHost version 3.8.1 or higher.
ynh_remove_php () {
# Get the version of PHP used by this app
local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion)
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] || [ -z "$phpversion" ]
then
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ]
then
ynh_print_err "Do not use ynh_remove_php to remove php$YNH_DEFAULT_PHP_VERSION !"
fi
return 0
fi
# Create the file if doesn't exist already
touch /etc/php/ynh_app_version
# Remove the line for this app
sed --in-place "/$YNH_APP_INSTANCE_NAME:$phpversion/d" "/etc/php/ynh_app_version"
# If no other app uses this version of PHP, remove it.
if ! grep --quiet "$phpversion" "/etc/php/ynh_app_version"
then
# Remove the service from the admin panel
if ynh_package_is_installed --package="php${phpversion}-fpm"; then
yunohost service remove php${phpversion}-fpm
fi
# Purge PHP dependencies for this version.
ynh_package_autopurge "php$phpversion php${phpversion}-fpm php${phpversion}-common"
fi
ynh_remove_app_dependencies
}
# Define the values to configure PHP-FPM
@ -421,10 +351,10 @@ ynh_remove_php () {
# high - High usage, frequently visited website.
#
# | arg: -p, --print - Print the result (intended for debug purpose only when packaging the app)
ynh_get_scalable_phpfpm () {
ynh_get_scalable_phpfpm() {
local legacy_args=ufp
# Declare an array to define the options of this helper.
local -A args_array=( [u]=usage= [f]=footprint= [p]=print )
local -A args_array=([u]=usage= [f]=footprint= [p]=print)
local usage
local footprint
local print
@ -435,38 +365,30 @@ ynh_get_scalable_phpfpm () {
usage=${usage,,}
print=${print:-0}
if [ "$footprint" = "low" ]
then
if [ "$footprint" = "low" ]; then
footprint=20
elif [ "$footprint" = "medium" ]
then
elif [ "$footprint" = "medium" ]; then
footprint=35
elif [ "$footprint" = "high" ]
then
elif [ "$footprint" = "high" ]; then
footprint=50
fi
# Define the factor to determine min_spare_servers
# to avoid having too few children ready to start for heavy apps
if [ $footprint -le 20 ]
then
if [ $footprint -le 20 ]; then
min_spare_servers_factor=8
elif [ $footprint -le 35 ]
then
elif [ $footprint -le 35 ]; then
min_spare_servers_factor=5
else
min_spare_servers_factor=3
fi
# Define the way the process manager handle child processes.
if [ "$usage" = "low" ]
then
if [ "$usage" = "low" ]; then
php_pm=ondemand
elif [ "$usage" = "medium" ]
then
elif [ "$usage" = "medium" ]; then
php_pm=dynamic
elif [ "$usage" = "high" ]
then
elif [ "$usage" = "high" ]; then
php_pm=static
else
ynh_die --message="Does not recognize '$usage' as an usage value."
@ -477,8 +399,7 @@ ynh_get_scalable_phpfpm () {
at_least_one() {
# Do not allow value below 1
if [ $1 -le 0 ]
then
if [ $1 -le 0 ]; then
echo 1
else
echo $1
@ -488,20 +409,18 @@ ynh_get_scalable_phpfpm () {
# Define pm.max_children
# The value of pm.max_children is the total amount of ram divide by 2 and divide again by the footprint of a pool for this app.
# So if PHP-FPM start the maximum of children, it won't exceed half of the ram.
php_max_children=$(( $max_ram / 2 / $footprint ))
php_max_children=$(($max_ram / 2 / $footprint))
# If process manager is set as static, use half less children.
# Used as static, there's always as many children as the value of pm.max_children
if [ "$php_pm" = "static" ]
then
php_max_children=$(( $php_max_children / 2 ))
if [ "$php_pm" = "static" ]; then
php_max_children=$(($php_max_children / 2))
fi
php_max_children=$(at_least_one $php_max_children)
# To not overload the proc, limit the number of children to 4 times the number of cores.
local core_number=$(nproc)
local max_proc=$(( $core_number * 4 ))
if [ $php_max_children -gt $max_proc ]
then
local max_proc=$(($core_number * 4))
if [ $php_max_children -gt $max_proc ]; then
php_max_children=$max_proc
fi
@ -511,16 +430,15 @@ ynh_get_scalable_phpfpm () {
php_max_children=$php_forced_max_children
fi
if [ "$php_pm" = "dynamic" ]
then
if [ "$php_pm" = "dynamic" ]; then
# Define pm.start_servers, pm.min_spare_servers and pm.max_spare_servers for a dynamic process manager
php_min_spare_servers=$(( $php_max_children / $min_spare_servers_factor ))
php_min_spare_servers=$(($php_max_children / $min_spare_servers_factor))
php_min_spare_servers=$(at_least_one $php_min_spare_servers)
php_max_spare_servers=$(( $php_max_children / 2 ))
php_max_spare_servers=$(($php_max_children / 2))
php_max_spare_servers=$(at_least_one $php_max_spare_servers)
php_start_servers=$(( $php_min_spare_servers + ( $php_max_spare_servers - $php_min_spare_servers ) /2 ))
php_start_servers=$(($php_min_spare_servers + ($php_max_spare_servers - $php_min_spare_servers) / 2))
php_start_servers=$(at_least_one $php_start_servers)
else
php_min_spare_servers=0
@ -528,30 +446,25 @@ ynh_get_scalable_phpfpm () {
php_start_servers=0
fi
if [ $print -eq 1 ]
then
ynh_debug --message="Footprint=${footprint}Mb by pool."
ynh_debug --message="Process manager=$php_pm"
ynh_debug --message="Max RAM=${max_ram}Mb"
if [ "$php_pm" != "static" ]
then
ynh_debug --message="\nMax estimated footprint=$(( $php_max_children * $footprint ))"
ynh_debug --message="Min estimated footprint=$(( $php_min_spare_servers * $footprint ))"
if [ $print -eq 1 ]; then
ynh_print_warn --message="Footprint=${footprint}Mb by pool."
ynh_print_warn --message="Process manager=$php_pm"
ynh_print_warn --message="Max RAM=${max_ram}Mb"
if [ "$php_pm" != "static" ]; then
ynh_print_warn --message="\nMax estimated footprint=$(($php_max_children * $footprint))"
ynh_print_warn --message="Min estimated footprint=$(($php_min_spare_servers * $footprint))"
fi
if [ "$php_pm" = "dynamic" ]
then
ynh_debug --message="Estimated average footprint=$(( $php_max_spare_servers * $footprint ))"
elif [ "$php_pm" = "static" ]
then
ynh_debug --message="Estimated footprint=$(( $php_max_children * $footprint ))"
if [ "$php_pm" = "dynamic" ]; then
ynh_print_warn --message="Estimated average footprint=$(($php_max_spare_servers * $footprint))"
elif [ "$php_pm" = "static" ]; then
ynh_print_warn --message="Estimated footprint=$(($php_max_children * $footprint))"
fi
ynh_debug --message="\nRaw php-fpm values:"
ynh_debug --message="pm.max_children = $php_max_children"
if [ "$php_pm" = "dynamic" ]
then
ynh_debug --message="pm.start_servers = $php_start_servers"
ynh_debug --message="pm.min_spare_servers = $php_min_spare_servers"
ynh_debug --message="pm.max_spare_servers = $php_max_spare_servers"
ynh_print_warn --message="\nRaw php-fpm values:"
ynh_print_warn --message="pm.max_children = $php_max_children"
if [ "$php_pm" = "dynamic" ]; then
ynh_print_warn --message="pm.start_servers = $php_start_servers"
ynh_print_warn --message="pm.min_spare_servers = $php_min_spare_servers"
ynh_print_warn --message="pm.max_spare_servers = $php_max_spare_servers"
fi
fi
}
@ -569,10 +482,10 @@ YNH_COMPOSER_VERSION=${YNH_COMPOSER_VERSION:-$YNH_DEFAULT_COMPOSER_VERSION}
# | arg: -c, --commands - Commands to execute.
#
# Requires YunoHost version 4.2 or higher.
ynh_composer_exec () {
ynh_composer_exec() {
# Declare an array to define the options of this helper.
local legacy_args=vwc
declare -Ar args_array=( [v]=phpversion= [w]=workdir= [c]=commands= )
declare -Ar args_array=([v]=phpversion= [w]=workdir= [c]=commands=)
local phpversion
local workdir
local commands
@ -595,10 +508,10 @@ ynh_composer_exec () {
# | arg: -c, --composerversion - Composer version to install
#
# Requires YunoHost version 4.2 or higher.
ynh_install_composer () {
ynh_install_composer() {
# Declare an array to define the options of this helper.
local legacy_args=vwac
declare -Ar args_array=( [v]=phpversion= [w]=workdir= [a]=install_args= [c]=composerversion=)
declare -Ar args_array=([v]=phpversion= [w]=workdir= [a]=install_args= [c]=composerversion=)
local phpversion
local workdir
local install_args
@ -612,7 +525,7 @@ ynh_install_composer () {
curl -sS https://getcomposer.org/installer \
| COMPOSER_HOME="$workdir/.composer" \
php${phpversion} -- --quiet --install-dir="$workdir" --version=$composerversion \
php${phpversion} -- --quiet --install-dir="$workdir" --version=$composerversion \
|| ynh_die --message="Unable to install Composer."
# install dependencies

View file

@ -46,8 +46,7 @@ ynh_psql_execute_as_root() {
ynh_handle_getopts_args "$@"
database="${database:-}"
if [ -n "$database" ]
then
if [ -n "$database" ]; then
database="--database=$database"
fi
@ -72,8 +71,7 @@ ynh_psql_execute_file_as_root() {
ynh_handle_getopts_args "$@"
database="${database:-}"
if [ -n "$database" ]
then
if [ -n "$database" ]; then
database="--database=$database"
fi
@ -175,8 +173,7 @@ ynh_psql_user_exists() {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT rolname FROM pg_roles WHERE rolname='$user';" | grep --quiet "$user"
then
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT rolname FROM pg_roles WHERE rolname='$user';" | grep --quiet "$user"; then
return 1
else
return 0
@ -198,8 +195,7 @@ ynh_psql_database_exists() {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT datname FROM pg_database WHERE datname='$database';" | grep --quiet "$database"
then
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT datname FROM pg_database WHERE datname='$database';" | grep --quiet "$database"; then
return 1
else
return 0
@ -269,16 +265,14 @@ ynh_psql_remove_db() {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ynh_psql_database_exists --database=$db_name
then # Check if the database exists
ynh_psql_drop_db $db_name # Remove the database
if ynh_psql_database_exists --database=$db_name; then # Check if the database exists
ynh_psql_drop_db $db_name # Remove the database
else
ynh_print_warn --message="Database $db_name not found"
fi
# Remove psql user if it exists
if ynh_psql_user_exists --user=$db_user
then
if ynh_psql_user_exists --user=$db_user; then
ynh_psql_drop_user $db_user
else
ynh_print_warn --message="User $db_user not found"
@ -310,8 +304,7 @@ ynh_psql_test_if_first_run() {
# If this is the very first time, we define the root password
# and configure a few things
if [ ! -f "$PSQL_ROOT_PWD_FILE" ]
then
if [ ! -f "$PSQL_ROOT_PWD_FILE" ]; then
local pg_hba=/etc/postgresql/$PSQL_VERSION/main/pg_hba.conf
local psql_root_password="$(ynh_string_random)"

View file

@ -10,7 +10,7 @@
ynh_app_setting_get() {
# Declare an array to define the options of this helper.
local legacy_args=ak
local -A args_array=( [a]=app= [k]=key= )
local -A args_array=([a]=app= [k]=key=)
local app
local key
# Manage arguments with getopts
@ -34,7 +34,7 @@ ynh_app_setting_get() {
ynh_app_setting_set() {
# Declare an array to define the options of this helper.
local legacy_args=akv
local -A args_array=( [a]=app= [k]=key= [v]=value= )
local -A args_array=([a]=app= [k]=key= [v]=value=)
local app
local key
local value
@ -58,7 +58,7 @@ ynh_app_setting_set() {
ynh_app_setting_delete() {
# Declare an array to define the options of this helper.
local legacy_args=ak
local -A args_array=( [a]=app= [k]=key= )
local -A args_array=([a]=app= [k]=key=)
local app
local key
# Manage arguments with getopts
@ -76,8 +76,7 @@ ynh_app_setting_delete() {
#
# [internal]
#
ynh_app_setting()
{
ynh_app_setting() {
set +o xtrace # set +x
ACTION="$1" APP="$2" KEY="$3" VALUE="${4:-}" python3 - <<EOF
import os, yaml, sys
@ -115,10 +114,10 @@ EOF
# example: ynh_webpath_available --domain=some.domain.tld --path_url=/coffee
#
# Requires YunoHost version 2.6.4 or higher.
ynh_webpath_available () {
ynh_webpath_available() {
# Declare an array to define the options of this helper.
local legacy_args=dp
local -A args_array=( [d]=domain= [p]=path_url= )
local -A args_array=([d]=domain= [p]=path_url=)
local domain
local path_url
# Manage arguments with getopts
@ -137,10 +136,10 @@ ynh_webpath_available () {
# example: ynh_webpath_register --app=wordpress --domain=some.domain.tld --path_url=/coffee
#
# Requires YunoHost version 2.6.4 or higher.
ynh_webpath_register () {
ynh_webpath_register() {
# Declare an array to define the options of this helper.
local legacy_args=adp
local -A args_array=( [a]=app= [d]=domain= [p]=path_url= )
local -A args_array=([a]=app= [d]=domain= [p]=path_url=)
local app
local domain
local path_url

View file

@ -12,13 +12,13 @@
ynh_string_random() {
# Declare an array to define the options of this helper.
local legacy_args=l
local -A args_array=( [l]=length= )
local -A args_array=([l]=length=)
local length
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
length=${length:-24}
dd if=/dev/urandom bs=1 count=1000 2> /dev/null \
dd if=/dev/urandom bs=1 count=1000 2>/dev/null \
| tr --complement --delete 'A-Za-z0-9' \
| sed --quiet 's/\(.\{'"$length"'\}\).*/\1/p'
}
@ -34,10 +34,10 @@ ynh_string_random() {
# sub-expressions can be used (see sed manual page for more information)
#
# Requires YunoHost version 2.6.4 or higher.
ynh_replace_string () {
ynh_replace_string() {
# Declare an array to define the options of this helper.
local legacy_args=mrf
local -A args_array=( [m]=match_string= [r]=replace_string= [f]=target_file= )
local -A args_array=([m]=match_string= [r]=replace_string= [f]=target_file=)
local match_string
local replace_string
local target_file
@ -65,10 +65,10 @@ ynh_replace_string () {
# characters, you can't use some regular expressions and sub-expressions.
#
# Requires YunoHost version 2.7.7 or higher.
ynh_replace_special_string () {
ynh_replace_special_string() {
# Declare an array to define the options of this helper.
local legacy_args=mrf
local -A args_array=( [m]=match_string= [r]=replace_string= [f]=target_file= )
local -A args_array=([m]=match_string= [r]=replace_string= [f]=target_file=)
local match_string
local replace_string
local target_file
@ -97,10 +97,10 @@ ynh_replace_special_string () {
# Underscorify the string (replace - and . by _)
#
# Requires YunoHost version 2.2.4 or higher.
ynh_sanitize_dbid () {
ynh_sanitize_dbid() {
# Declare an array to define the options of this helper.
local legacy_args=n
local -A args_array=( [n]=db_name= )
local -A args_array=([n]=db_name=)
local db_name
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -127,20 +127,20 @@ ynh_sanitize_dbid () {
# | arg: -p, --path_url= - URL path to normalize before using it
#
# Requires YunoHost version 2.6.4 or higher.
ynh_normalize_url_path () {
ynh_normalize_url_path() {
# Declare an array to define the options of this helper.
local legacy_args=p
local -A args_array=( [p]=path_url= )
local -A args_array=([p]=path_url=)
local path_url
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
test -n "$path_url" || ynh_die --message="ynh_normalize_url_path expect a URL path as first argument and received nothing."
if [ "${path_url:0:1}" != "/" ]; then # If the first character is not a /
path_url="/$path_url" # Add / at begin of path variable
if [ "${path_url:0:1}" != "/" ]; then # If the first character is not a /
path_url="/$path_url" # Add / at begin of path variable
fi
if [ "${path_url:${#path_url}-1}" == "/" ] && [ ${#path_url} -gt 1 ]; then # If the last character is a / and that not the only character.
path_url="${path_url:0:${#path_url}-1}" # Delete the last character
if [ "${path_url:${#path_url}-1}" == "/" ] && [ ${#path_url} -gt 1 ]; then # If the last character is a / and that not the only character.
path_url="${path_url:0:${#path_url}-1}" # Delete the last character
fi
echo $path_url
}

View file

@ -12,10 +12,10 @@
# format and how placeholders are replaced with actual variables.
#
# Requires YunoHost version 4.1.0 or higher.
ynh_add_systemd_config () {
ynh_add_systemd_config() {
# Declare an array to define the options of this helper.
local legacy_args=stv
local -A args_array=( [s]=service= [t]=template= [v]=others_var=)
local -A args_array=([s]=service= [t]=template= [v]=others_var=)
local service
local template
local others_var
@ -39,18 +39,17 @@ ynh_add_systemd_config () {
# | arg: -s, --service= - Service name (optionnal, $app by default)
#
# Requires YunoHost version 2.7.2 or higher.
ynh_remove_systemd_config () {
ynh_remove_systemd_config() {
# Declare an array to define the options of this helper.
local legacy_args=s
local -A args_array=( [s]=service= )
local -A args_array=([s]=service=)
local service
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local service="${service:-$app}"
local finalsystemdconf="/etc/systemd/system/$service.service"
if [ -e "$finalsystemdconf" ]
then
if [ -e "$finalsystemdconf" ]; then
ynh_systemd_action --service_name=$service --action=stop
systemctl disable $service --quiet
ynh_secure_remove --file="$finalsystemdconf"
@ -72,7 +71,7 @@ ynh_remove_systemd_config () {
ynh_systemd_action() {
# Declare an array to define the options of this helper.
local legacy_args=nalpte
local -A args_array=( [n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length= )
local -A args_array=([n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length=)
local service_name
local action
local line_match
@ -89,25 +88,22 @@ ynh_systemd_action() {
timeout=${timeout:-300}
# Manage case of service already stopped
if [ "$action" == "stop" ] && ! systemctl is-active --quiet $service_name
then
if [ "$action" == "stop" ] && ! systemctl is-active --quiet $service_name; then
return 0
fi
# Start to read the log
if [[ -n "$line_match" ]]
then
if [[ -n "$line_match" ]]; then
local templog="$(mktemp)"
# Following the starting of the app in its log
if [ "$log_path" == "systemd" ]
then
if [ "$log_path" == "systemd" ]; then
# Read the systemd journal
journalctl --unit=$service_name --follow --since=-0 --quiet > "$templog" &
journalctl --unit=$service_name --follow --since=-0 --quiet >"$templog" &
# Get the PID of the journalctl command
local pid_tail=$!
else
# Read the specified log file
tail --follow=name --retry --lines=0 "$log_path" > "$templog" 2>&1 &
tail --follow=name --retry --lines=0 "$log_path" >"$templog" 2>&1 &
# Get the PID of the tail command
local pid_tail=$!
fi
@ -119,13 +115,11 @@ ynh_systemd_action() {
fi
# If the service fails to perform the action
if ! systemctl $action $service_name
then
if ! systemctl $action $service_name; then
# Show syslog for this service
ynh_exec_err journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service_name
# If a log is specified for this service, show also the content of this log
if [ -e "$log_path" ]
then
if [ -e "$log_path" ]; then
ynh_exec_err tail --lines=$length "$log_path"
fi
ynh_clean_check_starting
@ -133,15 +127,12 @@ ynh_systemd_action() {
fi
# Start the timeout and try to find line_match
if [[ -n "${line_match:-}" ]]
then
if [[ -n "${line_match:-}" ]]; then
set +x
local i=0
for i in $(seq 1 $timeout)
do
for i in $(seq 1 $timeout); do
# Read the log until the sentence is found, that means the app finished to start. Or run until the timeout
if grep --extended-regexp --quiet "$line_match" "$templog"
then
if grep --extended-regexp --quiet "$line_match" "$templog"; then
ynh_print_info --message="The service $service_name has correctly executed the action ${action}."
break
fi
@ -154,13 +145,11 @@ ynh_systemd_action() {
if [ $i -ge 3 ]; then
echo "" >&2
fi
if [ $i -eq $timeout ]
then
if [ $i -eq $timeout ]; then
ynh_print_warn --message="The service $service_name didn't fully executed the action ${action} before the timeout."
ynh_print_warn --message="Please find here an extract of the end of the log of the service $service_name:"
ynh_exec_warn journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service_name
if [ -e "$log_path" ]
then
if [ -e "$log_path" ]; then
ynh_print_warn --message="\-\-\-"
ynh_exec_warn tail --lines=$length "$log_path"
fi
@ -174,14 +163,12 @@ ynh_systemd_action() {
# [internal]
#
# Requires YunoHost version 3.5.0 or higher.
ynh_clean_check_starting () {
if [ -n "${pid_tail:-}" ]
then
ynh_clean_check_starting() {
if [ -n "${pid_tail:-}" ]; then
# Stop the execution of tail.
kill -SIGTERM $pid_tail 2>&1
fi
if [ -n "${templog:-}" ]
then
if [ -n "${templog:-}" ]; then
ynh_secure_remove --file="$templog" 2>&1
fi
}

View file

@ -12,7 +12,7 @@
ynh_user_exists() {
# Declare an array to define the options of this helper.
local legacy_args=u
local -A args_array=( [u]=username= )
local -A args_array=([u]=username=)
local username
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -33,7 +33,7 @@ ynh_user_exists() {
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 -A args_array=([u]=username= [k]=key=)
local username
local key
# Manage arguments with getopts
@ -64,7 +64,7 @@ ynh_user_list() {
ynh_system_user_exists() {
# Declare an array to define the options of this helper.
local legacy_args=u
local -A args_array=( [u]=username= )
local -A args_array=([u]=username=)
local username
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -82,7 +82,7 @@ ynh_system_user_exists() {
ynh_system_group_exists() {
# Declare an array to define the options of this helper.
local legacy_args=g
local -A args_array=( [g]=group= )
local -A args_array=([g]=group=)
local group
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -108,10 +108,10 @@ ynh_system_group_exists() {
# ```
#
# Requires YunoHost version 2.6.4 or higher.
ynh_system_user_create () {
ynh_system_user_create() {
# Declare an array to define the options of this helper.
local legacy_args=uhs
local -A args_array=( [u]=username= [h]=home_dir= [s]=use_shell [g]=groups= )
local -A args_array=([u]=username= [h]=home_dir= [s]=use_shell [g]=groups=)
local username
local home_dir
local use_shell
@ -123,17 +123,15 @@ ynh_system_user_create () {
home_dir="${home_dir:-}"
groups="${groups:-}"
if ! ynh_system_user_exists "$username" # Check if the user exists on the system
then # If the user doesn't exist
if [ -n "$home_dir" ]
then # If a home dir is mentioned
if ! ynh_system_user_exists "$username"; then # Check if the user exists on the system
# If the user doesn't exist
if [ -n "$home_dir" ]; then # If a home dir is mentioned
local user_home_dir="--home-dir $home_dir"
else
local user_home_dir="--no-create-home"
fi
if [ $use_shell -eq 1 ]
then # If we want a shell for the user
local shell="" # Use default shell
if [ $use_shell -eq 1 ]; then # If we want a shell for the user
local shell="" # Use default shell
else
local shell="--shell /usr/sbin/nologin"
fi
@ -141,8 +139,7 @@ ynh_system_user_create () {
fi
local group
for group in $groups
do
for group in $groups; do
usermod -a -G "$group" "$username"
done
}
@ -153,25 +150,23 @@ ynh_system_user_create () {
# | arg: -u, --username= - Name of the system user that will be create
#
# Requires YunoHost version 2.6.4 or higher.
ynh_system_user_delete () {
ynh_system_user_delete() {
# Declare an array to define the options of this helper.
local legacy_args=u
local -A args_array=( [u]=username= )
local -A args_array=([u]=username=)
local username
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Check if the user exists on the system
if ynh_system_user_exists "$username"
then
if ynh_system_user_exists "$username"; then
deluser $username
else
ynh_print_warn --message="The user $username was not found"
fi
# Check if the group exists on the system
if ynh_system_group_exists "$username"
then
if ynh_system_group_exists "$username"; then
delgroup $username
fi
}

View file

@ -19,25 +19,25 @@ YNH_APP_BASEDIR=${YNH_APP_BASEDIR:-$(realpath ..)}
# 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 () {
ynh_exit_properly() {
local exit_code=$?
rm -rf "/var/cache/yunohost/download/"
if [ "$exit_code" -eq 0 ]; then
exit 0 # Exit without error if the script ended correctly
exit 0 # Exit without error if the script ended correctly
fi
trap '' EXIT # Ignore new exit signals
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
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.
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
@ -55,10 +55,10 @@ ynh_exit_properly () {
# 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
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
}
# Download, check integrity, uncompress and patch the source from app.src
@ -99,10 +99,10 @@ ynh_abort_if_errors () {
# - Extra files in `sources/extra_files/$src_id` will be copied to dest_dir
#
# Requires YunoHost version 2.6.4 or higher.
ynh_setup_source () {
ynh_setup_source() {
# Declare an array to define the options of this helper.
local legacy_args=dsk
local -A args_array=( [d]=dest_dir= [s]=source_id= [k]=keep= )
local -A args_array=([d]=dest_dir= [s]=source_id= [k]=keep=)
local dest_dir
local source_id
local keep
@ -133,15 +133,13 @@ ynh_setup_source () {
src_filename="${source_id}.${src_format}"
fi
# (Unused?) mecanism where one can have the file in a special local cache to not have to download it...
local local_src="/opt/yunohost-apps-src/${YNH_APP_ID}/${src_filename}"
mkdir -p /var/cache/yunohost/download/${YNH_APP_ID}/
src_filename="/var/cache/yunohost/download/${YNH_APP_ID}/${src_filename}"
if test -e "$local_src"
then
if test -e "$local_src"; then
cp $local_src $src_filename
else
[ -n "$src_url" ] || ynh_die "Couldn't parse SOURCE_URL from $src_file_path ?"
@ -162,15 +160,12 @@ ynh_setup_source () {
# Keep files to be backup/restored at the end of the helper
# Assuming $dest_dir already exists
rm -rf /var/cache/yunohost/files_to_keep_during_setup_source/
if [ -n "$keep" ] && [ -e "$dest_dir" ]
then
if [ -n "$keep" ] && [ -e "$dest_dir" ]; then
local keep_dir=/var/cache/yunohost/files_to_keep_during_setup_source/${YNH_APP_ID}
mkdir -p $keep_dir
local stuff_to_keep
for stuff_to_keep in $keep
do
if [ -e "$dest_dir/$stuff_to_keep" ]
then
for stuff_to_keep in $keep; do
if [ -e "$dest_dir/$stuff_to_keep" ]; then
mkdir --parents "$(dirname "$keep_dir/$stuff_to_keep")"
cp --archive "$dest_dir/$stuff_to_keep" "$keep_dir/$stuff_to_keep"
fi
@ -180,20 +175,16 @@ ynh_setup_source () {
# Extract source into the app dir
mkdir --parents "$dest_dir"
if [ -n "${final_path:-}" ] && [ "$dest_dir" == "$final_path" ]
then
if [ -n "${final_path:-}" ] && [ "$dest_dir" == "$final_path" ]; then
_ynh_apply_default_permissions $dest_dir
fi
if ! "$src_extract"
then
if ! "$src_extract"; then
mv $src_filename $dest_dir
elif [ "$src_format" = "zip" ]
then
elif [ "$src_format" = "zip" ]; then
# Zip format
# Using of a temp directory, because unzip doesn't manage --strip-components
if $src_in_subdir
then
if $src_in_subdir; then
local tmp_dir=$(mktemp --directory)
unzip -quo $src_filename -d "$tmp_dir"
cp --archive $tmp_dir/*/. "$dest_dir"
@ -204,18 +195,15 @@ ynh_setup_source () {
ynh_secure_remove --file="$src_filename"
else
local strip=""
if [ "$src_in_subdir" != "false" ]
then
if [ "$src_in_subdir" == "true" ]
then
if [ "$src_in_subdir" != "false" ]; then
if [ "$src_in_subdir" == "true" ]; then
local sub_dirs=1
else
local sub_dirs="$src_in_subdir"
fi
strip="--strip-components $sub_dirs"
fi
if [[ "$src_format" =~ ^tar.gz|tar.bz2|tar.xz$ ]]
then
if [[ "$src_format" =~ ^tar.gz|tar.bz2|tar.xz$ ]]; then
tar --extract --file=$src_filename --directory="$dest_dir" $strip
else
ynh_die --message="Archive format unrecognized."
@ -224,17 +212,16 @@ ynh_setup_source () {
fi
# Apply patches
if [ -d "$YNH_APP_BASEDIR/sources/patches/" ]
then
if [ -d "$YNH_APP_BASEDIR/sources/patches/" ]; then
local patches_folder=$(realpath $YNH_APP_BASEDIR/sources/patches/)
if (( $(find $patches_folder -type f -name "${source_id}-*.patch" 2> /dev/null | wc --lines) > "0" ))
then
(cd "$dest_dir"
for p in $patches_folder/${source_id}-*.patch
do
echo $p
patch --strip=1 < $p
done) || ynh_die --message="Unable to apply patches"
if (($(find $patches_folder -type f -name "${source_id}-*.patch" 2>/dev/null | wc --lines) > "0")); then
(
cd "$dest_dir"
for p in $patches_folder/${source_id}-*.patch; do
echo $p
patch --strip=1 <$p
done
) || ynh_die --message="Unable to apply patches"
fi
fi
@ -245,14 +232,11 @@ ynh_setup_source () {
# Keep files to be backup/restored at the end of the helper
# Assuming $dest_dir already exists
if [ -n "$keep" ]
then
if [ -n "$keep" ]; then
local keep_dir=/var/cache/yunohost/files_to_keep_during_setup_source/${YNH_APP_ID}
local stuff_to_keep
for stuff_to_keep in $keep
do
if [ -e "$keep_dir/$stuff_to_keep" ]
then
for stuff_to_keep in $keep; do
if [ -e "$keep_dir/$stuff_to_keep" ]; then
mkdir --parents "$(dirname "$dest_dir/$stuff_to_keep")"
cp --archive "$keep_dir/$stuff_to_keep" "$dest_dir/$stuff_to_keep"
fi
@ -276,7 +260,7 @@ ynh_setup_source () {
# `$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 () {
ynh_local_curl() {
# Define url of page to curl
local local_page=$(ynh_normalize_url_path $1)
local full_path=$path_url$local_page
@ -290,12 +274,10 @@ ynh_local_curl () {
# Concatenate all other arguments with '&' to prepare POST data
local POST_data=""
local arg=""
for arg in "${@:2}"
do
for arg in "${@:2}"; do
POST_data="${POST_data}${arg}&"
done
if [ -n "$POST_data" ]
then
if [ -n "$POST_data" ]; then
# Add --data arg and remove the last character, which is an unecessary '&'
POST_data="--data ${POST_data::-1}"
fi
@ -353,10 +335,10 @@ ynh_local_curl () {
# into the app settings when configuration is done.
#
# Requires YunoHost version 4.1.0 or higher.
ynh_add_config () {
ynh_add_config() {
# Declare an array to define the options of this helper.
local legacy_args=tdv
local -A args_array=( [t]=template= [d]=destination= )
local -A args_array=([t]=template= [d]=destination=)
local template
local destination
# Manage arguments with getopts
@ -414,17 +396,16 @@ ynh_add_config () {
# __VAR_2__ by $var_2
#
# Requires YunoHost version 4.1.0 or higher.
ynh_replace_vars () {
ynh_replace_vars() {
# Declare an array to define the options of this helper.
local legacy_args=f
local -A args_array=( [f]=file= )
local -A args_array=([f]=file=)
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Replace specific YunoHost variables
if test -n "${path_url:-}"
then
if test -n "${path_url:-}"; then
# path_url_slash_less is path_url, or a blank value if path_url is only '/'
local path_url_slash_less=${path_url%/}
ynh_replace_string --match_string="__PATH__/" --replace_string="$path_url_slash_less/" --target_file="$file"
@ -448,12 +429,11 @@ ynh_replace_vars () {
# Replace others variables
# List other unique (__ __) variables in $file
local uniques_vars=( $(grep -oP '__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__' $file | sort --unique | sed "s@__\([^.]*\)__@\L\1@g" ))
local uniques_vars=($(grep -oP '__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__' $file | sort --unique | sed "s@__\([^.]*\)__@\L\1@g"))
# Do the replacement
local delimit=@
for one_var in "${uniques_vars[@]}"
do
for one_var in "${uniques_vars[@]}"; do
# Validate that one_var is indeed defined
# -v checks if the variable is defined, for example:
# -v FOO tests if $FOO is defined
@ -509,7 +489,7 @@ ynh_replace_vars () {
ynh_read_var_in_file() {
# Declare an array to define the options of this helper.
local legacy_args=fka
local -A args_array=( [f]=file= [k]=key= [a]=after=)
local -A args_array=([f]=file= [k]=key= [a]=after=)
local file
local key
local after
@ -523,11 +503,9 @@ ynh_read_var_in_file() {
# Get the line number after which we search for the variable
local line_number=1
if [[ -n "$after" ]];
then
if [[ -n "$after" ]]; then
line_number=$(grep -n $after $file | cut -d: -f1)
if [[ -z "$line_number" ]];
then
if [[ -z "$line_number" ]]; then
set -o xtrace # set -x
return 1
fi
@ -545,7 +523,7 @@ ynh_read_var_in_file() {
if [[ "$ext" =~ ^ini|env$ ]]; then
comments="[;#]"
fi
if [[ "php" == "$ext" ]] || [[ "$ext" == "js" ]]; then
if [[ "php" == "$ext" ]] || [[ "$ext" == "js" ]]; then
comments="//"
fi
local list='\[\s*['$string']?\w+['$string']?\]'
@ -564,13 +542,13 @@ ynh_read_var_in_file() {
fi
# Remove comments if needed
local expression="$(echo "$expression_with_comment" | sed "s@$comments[^$string]*\$@@g" | sed "s@\s*[$endline]*\s*]*\$@@")"
local expression="$(echo "$expression_with_comment" | sed "s@${comments}[^$string]*\$@@g" | sed "s@\s*[$endline]*\s*]*\$@@")"
local first_char="${expression:0:1}"
if [[ "$first_char" == '"' ]] ; then
echo "$expression" | grep -m1 -o -P '"\K([^"](\\")?)*[^\\](?=")' | head -n1 | sed 's/\\"/"/g'
elif [[ "$first_char" == "'" ]] ; then
echo "$expression" | grep -m1 -o -P "'\K([^'](\\\\')?)*[^\\\\](?=')" | head -n1 | sed "s/\\\\'/'/g"
if [[ "$first_char" == '"' ]]; then
echo "$expression" | grep -m1 -o -P '"\K([^"](\\")?)*[^\\](?=")' | head -n1 | sed 's/\\"/"/g'
elif [[ "$first_char" == "'" ]]; then
echo "$expression" | grep -m1 -o -P "'\K([^'](\\\\')?)*[^\\\\](?=')" | head -n1 | sed "s/\\\\'/'/g"
else
echo "$expression"
fi
@ -588,7 +566,7 @@ ynh_read_var_in_file() {
ynh_write_var_in_file() {
# Declare an array to define the options of this helper.
local legacy_args=fkva
local -A args_array=( [f]=file= [k]=key= [v]=value= [a]=after=)
local -A args_array=([f]=file= [k]=key= [v]=value= [a]=after=)
local file
local key
local value
@ -603,11 +581,9 @@ ynh_write_var_in_file() {
# Get the line number after which we search for the variable
local line_number=1
if [[ -n "$after" ]];
then
if [[ -n "$after" ]]; then
line_number=$(grep -n $after $file | cut -d: -f1)
if [[ -z "$line_number" ]];
then
if [[ -z "$line_number" ]]; then
set -o xtrace # set -x
return 1
fi
@ -626,7 +602,7 @@ ynh_write_var_in_file() {
if [[ "$ext" =~ ^ini|env$ ]]; then
comments="[;#]"
fi
if [[ "php" == "$ext" ]] || [[ "$ext" == "js" ]]; then
if [[ "php" == "$ext" ]] || [[ "$ext" == "js" ]]; then
comments="//"
fi
local list='\[\s*['$string']?\w+['$string']?\]'
@ -644,28 +620,28 @@ ynh_write_var_in_file() {
fi
# Remove comments if needed
local expression="$(echo "$expression_with_comment" | sed "s@$comments[^$string]*\$@@g" | sed "s@\s*[$endline]*\s*]*\$@@")"
local expression="$(echo "$expression_with_comment" | sed "s@${comments}[^$string]*\$@@g" | sed "s@\s*[$endline]*\s*]*\$@@")"
endline=${expression_with_comment#"$expression"}
endline="$(echo "$endline" | sed 's/\\/\\\\/g')"
value="$(echo "$value" | sed 's/\\/\\\\/g')"
local first_char="${expression:0:1}"
delimiter=$'\001'
if [[ "$first_char" == '"' ]] ; then
if [[ "$first_char" == '"' ]]; then
# \ and sed is quite complex you need 2 \\ to get one in a sed
# So we need \\\\ to go through 2 sed
value="$(echo "$value" | sed 's/"/\\\\"/g')"
sed -ri "${range}s$delimiter"'(^'"${var_part}"'")([^"]|\\")*("[\s;,]*)(\s*'$comments'.*)?$'$delimiter'\1'"${value}"'"'"${endline}${delimiter}i" ${file}
elif [[ "$first_char" == "'" ]] ; then
elif [[ "$first_char" == "'" ]]; then
# \ and sed is quite complex you need 2 \\ to get one in a sed
# However double quotes implies to double \\ to
# So we need \\\\\\\\ to go through 2 sed and 1 double quotes str
value="$(echo "$value" | sed "s/'/\\\\\\\\'/g")"
sed -ri "${range}s$delimiter(^${var_part}')([^']|\\')*('"'[\s,;]*)(\s*'$comments'.*)?$'$delimiter'\1'"${value}'${endline}${delimiter}i" ${file}
else
if [[ "$value" == *"'"* ]] || [[ "$value" == *'"'* ]] || [[ "$ext" =~ ^php|py|json|js$ ]] ; then
if [[ "$value" == *"'"* ]] || [[ "$value" == *'"'* ]] || [[ "$ext" =~ ^php|py|json|js$ ]]; then
value='\"'"$(echo "$value" | sed 's/"/\\\\"/g')"'\"'
fi
if [[ "$ext" =~ ^yaml|yml$ ]] ; then
if [[ "$ext" =~ ^yaml|yml$ ]]; then
value=" $value"
fi
sed -ri "${range}s$delimiter(^${var_part}).*\$$delimiter\1${value}${endline}${delimiter}i" ${file}
@ -673,7 +649,6 @@ ynh_write_var_in_file() {
set -o xtrace # set -x
}
# Render templates with Jinja2
#
# [internal]
@ -691,7 +666,7 @@ ynh_render_template() {
# Taken from https://stackoverflow.com/a/35009576
python3 -c 'import os, sys, jinja2; sys.stdout.write(
jinja2.Template(sys.stdin.read()
).render(os.environ));' < $template_path > $output_path
).render(os.environ));' <$template_path >$output_path
}
# Fetch the Debian release codename
@ -700,7 +675,7 @@ ynh_render_template() {
# | ret: The Debian release codename (i.e. jessie, stretch, ...)
#
# Requires YunoHost version 2.7.12 or higher.
ynh_get_debian_release () {
ynh_get_debian_release() {
echo $(lsb_release --codename --short)
}
@ -724,46 +699,55 @@ properly with chmod/chown."
echo $TMP_DIR
}
_acceptable_path_to_delete() {
local file=$1
local forbidden_paths=$(ls -d / /* /{var,home,usr}/* /etc/{default,sudoers.d,yunohost,cron*})
# 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 () {
ynh_secure_remove() {
# Declare an array to define the options of this helper.
local legacy_args=f
local -A args_array=( [f]=file= )
local -A args_array=([f]=file=)
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
set +o xtrace # set +x
local forbidden_path=" \
/var/www \
/home/yunohost.app"
if [ $# -ge 2 ]
then
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
if [[ -z "$file" ]]; then
ynh_print_warn --message="ynh_secure_remove called with empty argument, ignoring."
elif [[ "$forbidden_path" =~ "$file" \
# Match all paths or subpaths in $forbidden_path
|| "$file" =~ ^/[[:alnum:]]+$ \
# Match all first level paths from / (Like /var, /root, etc...)
|| "${file:${#file}-1}" = "/" ]]
# Match if the path finishes by /. Because it seems there is an empty variable
then
ynh_print_warn --message="Not deleting '$file' because it is not an acceptable path to delete."
elif [ -e "$file" ]
then
rm --recursive "$file"
else
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
@ -776,26 +760,22 @@ ynh_secure_remove () {
# (Deprecated, use --output-as json and jq instead)
ynh_get_plain_key() {
local prefix="#"
local founded=0
local found=0
# We call this key_ so that it's not caught as
# an info to be redacted by the core
local key_=$1
shift
while read line
do
if [[ "$founded" == "1" ]]
then
while read line; do
if [[ "$found" == "1" ]]; then
[[ "$line" =~ ^${prefix}[^#] ]] && return
echo $line
elif [[ "$line" =~ ^${prefix}${key_}$ ]]
then
if [[ -n "${1:-}" ]]
then
elif [[ "$line" =~ ^${prefix}${key_}$ ]]; then
if [[ -n "${1:-}" ]]; then
prefix+="#"
key_=$1
shift
else
founded=1
found=1
fi
fi
done
@ -809,10 +789,10 @@ ynh_get_plain_key() {
# | ret: the value associate to that key
#
# Requires YunoHost version 3.5.0 or higher.
ynh_read_manifest () {
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 -A args_array=([m]=manifest= [k]=manifest_key=)
local manifest
local manifest_key
# Manage arguments with getopts
@ -839,20 +819,19 @@ ynh_read_manifest () {
# 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 () {
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 -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")
if [[ "$manifest" != "" ]] && [[ -e "$manifest" ]]; then
version_key_=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version")
else
version_key_=$YNH_APP_MANIFEST_VERSION
version_key_=$YNH_APP_MANIFEST_VERSION
fi
echo "${version_key_/~ynh*/}"
@ -869,10 +848,10 @@ ynh_app_upstream_version () {
# 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 () {
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 -A args_array=([m]=manifest=)
local manifest
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
@ -894,11 +873,10 @@ ynh_app_package_version () {
# sudo yunohost app upgrade <appname> --force
# ```
# Requires YunoHost version 3.5.0 or higher.
ynh_check_app_version_changed () {
ynh_check_app_version_changed() {
local return_value=${YNH_APP_UPGRADE_TYPE}
if [ "$return_value" == "UPGRADE_FULL" ] || [ "$return_value" == "UPGRADE_FORCED" ] || [ "$return_value" == "DOWNGRADE_FORCED" ]
then
if [ "$return_value" == "UPGRADE_FULL" ] || [ "$return_value" == "UPGRADE_FORCED" ] || [ "$return_value" == "DOWNGRADE_FORCED" ]; then
return_value="UPGRADE_APP"
fi
@ -927,7 +905,7 @@ ynh_check_app_version_changed () {
# 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= )
declare -Ar args_array=([c]=comparison= [v]=version=)
local version
local comparison
# Manage arguments with getopts
@ -936,8 +914,7 @@ ynh_compare_current_package_version() {
local current_version=$YNH_APP_CURRENT_VERSION
# Check the syntax of the versions
if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]]
then
if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]]; then
ynh_die --message="Invalid argument for version."
fi
@ -972,13 +949,11 @@ _ynh_apply_default_permissions() {
local ynh_requirement=$(jq -r '.requirements.yunohost' $YNH_APP_BASEDIR/manifest.json | tr -d '>= ')
if [ -z "$ynh_requirement" ] || [ "$ynh_requirement" == "null" ] || dpkg --compare-versions $ynh_requirement ge 4.2
then
if [ -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
if ynh_system_user_exists $app; then
chown $app:$app $target
fi
fi

View file

@ -6,13 +6,12 @@ YNH_CWD="${YNH_BACKUP_DIR%/}/conf/manually_modified_files"
mkdir -p "$YNH_CWD"
cd "$YNH_CWD"
yunohost tools shell -c "from yunohost.regenconf import manually_modified_files; print('\n'.join(manually_modified_files()))" > ./manually_modified_files_list
yunohost tools shell -c "from yunohost.regenconf import manually_modified_files; print('\n'.join(manually_modified_files()))" >./manually_modified_files_list
ynh_backup --src_path="./manually_modified_files_list"
for file in $(cat ./manually_modified_files_list)
do
for file in $(cat ./manually_modified_files_list); do
[[ -e $file ]] && ynh_backup --src_path="$file"
done
ynh_backup --src_path="/etc/ssowat/conf.json.persistent"

View file

@ -3,129 +3,128 @@
set -e
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
cd /usr/share/yunohost/templates/yunohost
cd /usr/share/yunohost/templates/yunohost
[[ -d /etc/yunohost ]] || mkdir -p /etc/yunohost
[[ -d /etc/yunohost ]] || mkdir -p /etc/yunohost
# set default current_host
[[ -f /etc/yunohost/current_host ]] \
|| echo "yunohost.org" > /etc/yunohost/current_host
# set default current_host
[[ -f /etc/yunohost/current_host ]] \
|| echo "yunohost.org" >/etc/yunohost/current_host
# copy default services and firewall
[[ -f /etc/yunohost/firewall.yml ]] \
|| cp firewall.yml /etc/yunohost/firewall.yml
# copy default services and firewall
[[ -f /etc/yunohost/firewall.yml ]] \
|| cp firewall.yml /etc/yunohost/firewall.yml
# allow users to access /media directory
[[ -d /etc/skel/media ]] \
|| (mkdir -p /media && ln -s /media /etc/skel/media)
# allow users to access /media directory
[[ -d /etc/skel/media ]] \
|| (mkdir -p /media && ln -s /media /etc/skel/media)
# Cert folders
mkdir -p /etc/yunohost/certs
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
# Cert folders
mkdir -p /etc/yunohost/certs
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
# App folders
mkdir -p /etc/yunohost/apps
chmod 700 /etc/yunohost/apps
mkdir -p /home/yunohost.app
chmod 755 /home/yunohost.app
# App folders
mkdir -p /etc/yunohost/apps
chmod 700 /etc/yunohost/apps
mkdir -p /home/yunohost.app
chmod 755 /home/yunohost.app
# Domain settings
mkdir -p /etc/yunohost/domains
chmod 700 /etc/yunohost/domains
# Domain settings
mkdir -p /etc/yunohost/domains
chmod 700 /etc/yunohost/domains
# Backup folders
mkdir -p /home/yunohost.backup/archives
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.backup/archives # This is later changed to admin:root once admin user exists
# Backup folders
mkdir -p /home/yunohost.backup/archives
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.backup/archives # This is later changed to admin:root once admin user exists
# Empty ssowat json persistent conf
echo "{}" > '/etc/ssowat/conf.json.persistent'
chmod 644 /etc/ssowat/conf.json.persistent
chown root:root /etc/ssowat/conf.json.persistent
# Empty ssowat json persistent conf
echo "{}" >'/etc/ssowat/conf.json.persistent'
chmod 644 /etc/ssowat/conf.json.persistent
chown root:root /etc/ssowat/conf.json.persistent
# Empty service conf
touch /etc/yunohost/services.yml
# Empty service conf
touch /etc/yunohost/services.yml
mkdir -p /var/cache/yunohost/repo
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
mkdir -p /var/cache/yunohost/repo
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
cp yunoprompt.service /etc/systemd/system/yunoprompt.service
cp dpkg-origins /etc/dpkg/origins/yunohost
cp yunoprompt.service /etc/systemd/system/yunoprompt.service
cp dpkg-origins /etc/dpkg/origins/yunohost
# Change dpkg vendor
# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
readlink -f /etc/dpkg/origins/default | grep -q debian \
&& rm -f /etc/dpkg/origins/default \
&& ln -s /etc/dpkg/origins/yunohost /etc/dpkg/origins/default
# Change dpkg vendor
# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
readlink -f /etc/dpkg/origins/default | grep -q debian \
&& rm -f /etc/dpkg/origins/default \
&& ln -s /etc/dpkg/origins/yunohost /etc/dpkg/origins/default
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/yunohost
cd /usr/share/yunohost/templates/yunohost
# Legacy code that can be removed once on bullseye
touch /etc/yunohost/services.yml
yunohost tools shell -c "from yunohost.service import _get_services, _save_services; _save_services(_get_services())"
# Legacy code that can be removed once on bullseye
touch /etc/yunohost/services.yml
yunohost tools shell -c "from yunohost.service import _get_services, _save_services; _save_services(_get_services())"
mkdir -p $pending_dir/etc/systemd/system
mkdir -p $pending_dir/etc/cron.d/
mkdir -p $pending_dir/etc/cron.daily/
mkdir -p $pending_dir/etc/systemd/system
mkdir -p $pending_dir/etc/cron.d/
mkdir -p $pending_dir/etc/cron.daily/
# add cron job for diagnosis to be ran at 7h and 19h + a random delay between
# 0 and 20min, meant to avoid every instances running their diagnosis at
# exactly the same time, which may overload the diagnosis server.
cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF
# add cron job for diagnosis to be ran at 7h and 19h + a random delay between
# 0 and 20min, meant to avoid every instances running their diagnosis at
# exactly the same time, which may overload the diagnosis server.
cat >$pending_dir/etc/cron.d/yunohost-diagnosis <<EOF
SHELL=/bin/bash
0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%1200)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably"
EOF
# Cron job that upgrade the app list everyday
cat > $pending_dir/etc/cron.daily/yunohost-fetch-apps-catalog << EOF
# Cron job that upgrade the app list everyday
cat >$pending_dir/etc/cron.daily/yunohost-fetch-apps-catalog <<EOF
#!/bin/bash
(sleep \$((RANDOM%3600)); yunohost tools update --apps > /dev/null) &
EOF
# Cron job that renew lets encrypt certificates if there's any that needs renewal
cat > $pending_dir/etc/cron.daily/yunohost-certificate-renew << EOF
# Cron job that renew lets encrypt certificates if there's any that needs renewal
cat >$pending_dir/etc/cron.daily/yunohost-certificate-renew <<EOF
#!/bin/bash
yunohost domain cert renew --email
EOF
# If we subscribed to a dyndns domain, add the corresponding cron
# - delay between 0 and 60 secs to spread the check over a 1 min window
# - do not run the command if some process already has the lock, to avoid queuing hundreds of commands...
if ls -l /etc/yunohost/dyndns/K*.private 2>/dev/null
then
cat > $pending_dir/etc/cron.d/yunohost-dyndns << EOF
# If we subscribed to a dyndns domain, add the corresponding cron
# - delay between 0 and 60 secs to spread the check over a 1 min window
# - do not run the command if some process already has the lock, to avoid queuing hundreds of commands...
if ls -l /etc/yunohost/dyndns/K*.private 2>/dev/null; then
cat >$pending_dir/etc/cron.d/yunohost-dyndns <<EOF
SHELL=/bin/bash
*/10 * * * * root : YunoHost DynDNS update; sleep \$((RANDOM\\%60)); test -e /var/run/moulinette_yunohost.lock || yunohost dyndns update >> /dev/null
EOF
fi
fi
# legacy stuff to avoid yunohost reporting etckeeper as manually modified
# (this make sure that the hash is null / file is flagged as to-delete)
mkdir -p $pending_dir/etc/etckeeper
touch $pending_dir/etc/etckeeper/etckeeper.conf
# legacy stuff to avoid yunohost reporting etckeeper as manually modified
# (this make sure that the hash is null / file is flagged as to-delete)
mkdir -p $pending_dir/etc/etckeeper
touch $pending_dir/etc/etckeeper/etckeeper.conf
# Skip ntp if inside a container (inspired from the conf of systemd-timesyncd)
mkdir -p ${pending_dir}/etc/systemd/system/ntp.service.d/
echo "
# Skip ntp if inside a container (inspired from the conf of systemd-timesyncd)
mkdir -p ${pending_dir}/etc/systemd/system/ntp.service.d/
echo "
[Unit]
ConditionCapability=CAP_SYS_TIME
ConditionVirtualization=!container
" > ${pending_dir}/etc/systemd/system/ntp.service.d/ynh-override.conf
" >${pending_dir}/etc/systemd/system/ntp.service.d/ynh-override.conf
# Make nftable conflict with yunohost-firewall
mkdir -p ${pending_dir}/etc/systemd/system/nftables.service.d/
cat > ${pending_dir}/etc/systemd/system/nftables.service.d/ynh-override.conf << EOF
# Make nftable conflict with yunohost-firewall
mkdir -p ${pending_dir}/etc/systemd/system/nftables.service.d/
cat >${pending_dir}/etc/systemd/system/nftables.service.d/ynh-override.conf <<EOF
[Unit]
# yunohost-firewall and nftables conflict with each other
Conflicts=yunohost-firewall.service
@ -133,107 +132,107 @@ ConditionFileIsExecutable=!/etc/init.d/yunohost-firewall
ConditionPathExists=!/etc/systemd/system/multi-user.target.wants/yunohost-firewall.service
EOF
# Don't suspend computer on LidSwitch
mkdir -p ${pending_dir}/etc/systemd/logind.conf.d/
cat > ${pending_dir}/etc/systemd/logind.conf.d/ynh-override.conf << EOF
# Don't suspend computer on LidSwitch
mkdir -p ${pending_dir}/etc/systemd/logind.conf.d/
cat >${pending_dir}/etc/systemd/logind.conf.d/ynh-override.conf <<EOF
[Login]
HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore
HandleLidSwitchExternalPower=ignore
EOF
cp yunoprompt.service ${pending_dir}/etc/systemd/system/yunoprompt.service
cp yunoprompt.service ${pending_dir}/etc/systemd/system/yunoprompt.service
if [[ "$(yunohost settings get 'security.experimental.enabled')" == "True" ]]
then
cp proc-hidepid.service ${pending_dir}/etc/systemd/system/proc-hidepid.service
else
touch ${pending_dir}/etc/systemd/system/proc-hidepid.service
fi
if [[ "$(yunohost settings get 'security.experimental.enabled')" == "True" ]]; then
cp proc-hidepid.service ${pending_dir}/etc/systemd/system/proc-hidepid.service
else
touch ${pending_dir}/etc/systemd/system/proc-hidepid.service
fi
cp dpkg-origins ${pending_dir}/etc/dpkg/origins/yunohost
mkdir -p ${pending_dir}/etc/dpkg/origins/
cp dpkg-origins ${pending_dir}/etc/dpkg/origins/yunohost
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
######################
# Enfore permissions #
######################
######################
# Enfore permissions #
######################
chmod 750 /home/admin
chmod 750 /home/yunohost.conf
chmod 750 /home/yunohost.backup
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.conf
chown admin:root /home/yunohost.backup
chown admin:root /home/yunohost.backup/archives
chmod 750 /home/admin
chmod 750 /home/yunohost.conf
chmod 750 /home/yunohost.backup
chmod 750 /home/yunohost.backup/archives
chown root:root /home/yunohost.conf
chown admin:root /home/yunohost.backup
chown admin:root /home/yunohost.backup/archives
# Certs
# We do this with find because there could be a lot of them...
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
# Certs
# We do this with find because there could be a lot of them...
chown -R root:ssl-cert /etc/yunohost/certs
chmod 750 /etc/yunohost/certs
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
find /etc/cron.*/yunohost-* -type f -exec chmod 755 {} \;
find /etc/cron.d/yunohost-* -type f -exec chmod 644 {} \;
find /etc/cron.*/yunohost-* -type f -exec chown root:root {} \;
find /etc/cron.*/yunohost-* -type f -exec chmod 755 {} \;
find /etc/cron.d/yunohost-* -type f -exec chmod 644 {} \;
find /etc/cron.*/yunohost-* -type f -exec chown root:root {} \;
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
chown root:root /var/cache/moulinette
chmod 700 /var/cache/moulinette
chown root:root /var/cache/yunohost
chmod 700 /var/cache/yunohost
chown root:root /var/cache/moulinette
chmod 700 /var/cache/moulinette
setfacl -m g:all_users:--- /var/www
setfacl -m g:all_users:--- /var/log/nginx
setfacl -m g:all_users:--- /etc/yunohost
setfacl -m g:all_users:--- /etc/ssowat
setfacl -m g:all_users:--- /var/www
setfacl -m g:all_users:--- /var/log/nginx
setfacl -m g:all_users:--- /etc/yunohost
setfacl -m g:all_users:--- /etc/ssowat
for USER in $(yunohost user list --quiet --output-as json | jq -r '.users | .[] | .username')
do
[ ! -e "/home/$USER" ] || setfacl -m g:all_users:--- /home/$USER
done
for USER in $(yunohost user list --quiet --output-as json | jq -r '.users | .[] | .username'); do
[ ! -e "/home/$USER" ] || setfacl -m g:all_users:--- /home/$USER
done
# Domain settings
mkdir -p /etc/yunohost/domains
# Domain settings
mkdir -p /etc/yunohost/domains
# Misc configuration / state files
chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
# Misc configuration / state files
chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
# Apps folder, custom hooks folder
[[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d)
[[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps)
[[ ! -e /etc/yunohost/domains ]] || (chown root /etc/yunohost/domains && chmod 700 /etc/yunohost/domains)
# Apps folder, custom hooks folder
[[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d)
[[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps)
[[ ! -e /etc/yunohost/domains ]] || (chown root /etc/yunohost/domains && chmod 700 /etc/yunohost/domains)
# Create ssh.app and sftp.app groups if they don't exist yet
grep -q '^ssh.app:' /etc/group || groupadd ssh.app
grep -q '^sftp.app:' /etc/group || groupadd sftp.app
# Create ssh.app and sftp.app groups if they don't exist yet
grep -q '^ssh.app:' /etc/group || groupadd ssh.app
grep -q '^sftp.app:' /etc/group || groupadd sftp.app
# Propagates changes in systemd service config overrides
[[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl restart ntp; }
[[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload
[[ ! "$regen_conf_files" =~ "login.conf.d/ynh-override.conf" ]] || systemctl daemon-reload
if [[ "$regen_conf_files" =~ "yunoprompt.service" ]]
then
systemctl daemon-reload
action=$([[ -e /etc/systemd/system/yunoprompt.service ]] && echo 'enable' || echo 'disable')
systemctl $action yunoprompt --quiet --now
fi
if [[ "$regen_conf_files" =~ "proc-hidepid.service" ]]
then
systemctl daemon-reload
action=$([[ -e /etc/systemd/system/proc-hidepid.service ]] && echo 'enable' || echo 'disable')
systemctl $action proc-hidepid --quiet --now
fi
# Propagates changes in systemd service config overrides
[[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || {
systemctl daemon-reload
systemctl restart ntp
}
[[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload
[[ ! "$regen_conf_files" =~ "login.conf.d/ynh-override.conf" ]] || systemctl daemon-reload
if [[ "$regen_conf_files" =~ "yunoprompt.service" ]]; then
systemctl daemon-reload
action=$([[ -e /etc/systemd/system/yunoprompt.service ]] && echo 'enable' || echo 'disable')
systemctl $action yunoprompt --quiet --now
fi
if [[ "$regen_conf_files" =~ "proc-hidepid.service" ]]; then
systemctl daemon-reload
action=$([[ -e /etc/systemd/system/proc-hidepid.service ]] && echo 'enable' || echo 'disable')
systemctl $action proc-hidepid --quiet --now
fi
# Change dpkg vendor
# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
readlink -f /etc/dpkg/origins/default | grep -q debian \
&& rm -f /etc/dpkg/origins/default \
&& ln -s /etc/dpkg/origins/yunohost /etc/dpkg/origins/default
# Change dpkg vendor
# see https://wiki.debian.org/Derivatives/Guidelines#Vendor
readlink -f /etc/dpkg/origins/default | grep -q debian \
&& rm -f /etc/dpkg/origins/default \
&& ln -s /etc/dpkg/origins/yunohost /etc/dpkg/origins/default
}
do_$1_regen ${@:2}

View file

@ -23,7 +23,7 @@ regen_local_ca() {
# (Update the serial so that it's specific to this very instance)
# N.B. : the weird RANDFILE thing comes from:
# https://stackoverflow.com/questions/94445/using-openssl-what-does-unable-to-write-random-state-mean
RANDFILE=.rnd openssl rand -hex 19 > serial
RANDFILE=.rnd openssl rand -hex 19 >serial
rm -f index.txt
touch index.txt
cp /usr/share/yunohost/templates/ssl/openssl.cnf openssl.ca.cnf
@ -50,73 +50,72 @@ regen_local_ca() {
do_init_regen() {
LOGFILE=/tmp/yunohost-ssl-init
echo "" > $LOGFILE
chown root:root $LOGFILE
chmod 640 $LOGFILE
LOGFILE=/tmp/yunohost-ssl-init
echo "" >$LOGFILE
chown root:root $LOGFILE
chmod 640 $LOGFILE
# Make sure this conf exists
mkdir -p ${ssl_dir}
cp /usr/share/yunohost/templates/ssl/openssl.cnf ${ssl_dir}/openssl.ca.cnf
# Make sure this conf exists
mkdir -p ${ssl_dir}
cp /usr/share/yunohost/templates/ssl/openssl.cnf ${ssl_dir}/openssl.ca.cnf
# create default certificates
if [[ ! -f "$ynh_ca" ]]; then
regen_local_ca yunohost.org >>$LOGFILE
fi
# create default certificates
if [[ ! -f "$ynh_ca" ]]; then
regen_local_ca yunohost.org >>$LOGFILE
fi
if [[ ! -f "$ynh_crt" ]]; then
echo -e "\n# Creating initial key and certificate \n" >>$LOGFILE
if [[ ! -f "$ynh_crt" ]]; then
echo -e "\n# Creating initial key and certificate \n" >>$LOGFILE
openssl req -new \
-config "$openssl_conf" \
-days 730 \
-out "${ssl_dir}/certs/yunohost_csr.pem" \
-keyout "${ssl_dir}/certs/yunohost_key.pem" \
-nodes -batch &>>$LOGFILE
openssl req -new \
-config "$openssl_conf" \
-days 730 \
-out "${ssl_dir}/certs/yunohost_csr.pem" \
-keyout "${ssl_dir}/certs/yunohost_key.pem" \
-nodes -batch &>>$LOGFILE
openssl ca \
-config "$openssl_conf" \
-days 730 \
-in "${ssl_dir}/certs/yunohost_csr.pem" \
-out "${ssl_dir}/certs/yunohost_crt.pem" \
-batch &>>$LOGFILE
openssl ca \
-config "$openssl_conf" \
-days 730 \
-in "${ssl_dir}/certs/yunohost_csr.pem" \
-out "${ssl_dir}/certs/yunohost_crt.pem" \
-batch &>>$LOGFILE
chmod 640 "${ssl_dir}/certs/yunohost_key.pem"
chmod 640 "${ssl_dir}/certs/yunohost_crt.pem"
chmod 640 "${ssl_dir}/certs/yunohost_key.pem"
chmod 640 "${ssl_dir}/certs/yunohost_crt.pem"
cp "${ssl_dir}/certs/yunohost_key.pem" "$ynh_key"
cp "${ssl_dir}/certs/yunohost_crt.pem" "$ynh_crt"
ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem
ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem
fi
cp "${ssl_dir}/certs/yunohost_key.pem" "$ynh_key"
cp "${ssl_dir}/certs/yunohost_crt.pem" "$ynh_crt"
ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem
ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem
fi
chown -R root:ssl-cert /etc/yunohost/certs/yunohost.org/
chmod o-rwx /etc/yunohost/certs/yunohost.org/
chown -R root:ssl-cert /etc/yunohost/certs/yunohost.org/
chmod o-rwx /etc/yunohost/certs/yunohost.org/
install -D -m 644 $openssl_conf "${ssl_dir}/openssl.cnf"
install -D -m 644 $openssl_conf "${ssl_dir}/openssl.cnf"
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/ssl
cd /usr/share/yunohost/templates/ssl
install -D -m 644 openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf"
install -D -m 644 openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
current_local_ca_domain=$(openssl x509 -in $ynh_ca -text | tr ',' '\n' | grep Issuer | awk '{print $4}')
main_domain=$(cat /etc/yunohost/current_host)
current_local_ca_domain=$(openssl x509 -in $ynh_ca -text | tr ',' '\n' | grep Issuer | awk '{print $4}')
main_domain=$(cat /etc/yunohost/current_host)
if [[ "$current_local_ca_domain" != "$main_domain" ]]
then
regen_local_ca $main_domain
# Idk how useful this is, but this was in the previous python code (domain.main_domain())
ln -sf /etc/yunohost/certs/$domain/crt.pem /etc/ssl/certs/yunohost_crt.pem
ln -sf /etc/yunohost/certs/$domain/key.pem /etc/ssl/private/yunohost_key.pem
fi
if [[ "$current_local_ca_domain" != "$main_domain" ]]; then
regen_local_ca $main_domain
# Idk how useful this is, but this was in the previous python code (domain.main_domain())
ln -sf /etc/yunohost/certs/$domain/crt.pem /etc/ssl/certs/yunohost_crt.pem
ln -sf /etc/yunohost/certs/$domain/key.pem /etc/ssl/private/yunohost_key.pem
fi
}
do_$1_regen ${@:2}

View file

@ -8,19 +8,19 @@ config="/usr/share/yunohost/templates/slapd/config.ldif"
db_init="/usr/share/yunohost/templates/slapd/db_init.ldif"
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
do_pre_regen ""
do_pre_regen ""
# Drop current existing slapd data
# Drop current existing slapd data
rm -rf /var/backups/*.ldapdb
rm -rf /var/backups/slapd-*
rm -rf /var/backups/*.ldapdb
rm -rf /var/backups/slapd-*
debconf-set-selections << EOF
debconf-set-selections <<EOF
slapd slapd/password1 password yunohost
slapd slapd/password2 password yunohost
slapd slapd/domain string yunohost.org
@ -33,170 +33,166 @@ slapd slapd/no_configuration boolean false
slapd slapd/purge_database boolean false
EOF
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure slapd -u
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure slapd -u
# Enforce permissions
chown -R openldap:openldap /etc/ldap/schema/
usermod -aG ssl-cert openldap
# Enforce permissions
chown -R openldap:openldap /etc/ldap/schema/
usermod -aG ssl-cert openldap
# (Re-)init data according to default ldap entries
echo ' Initializing LDAP with YunoHost DB structure'
# (Re-)init data according to default ldap entries
echo ' Initializing LDAP with YunoHost DB structure'
rm -rf /etc/ldap/slapd.d
mkdir -p /etc/ldap/slapd.d
slapadd -F /etc/ldap/slapd.d -b cn=config -l "$config" 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
chown -R openldap: /etc/ldap/slapd.d
rm -rf /etc/ldap/slapd.d
mkdir -p /etc/ldap/slapd.d
slapadd -F /etc/ldap/slapd.d -b cn=config -l "$config" 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
chown -R openldap: /etc/ldap/slapd.d
rm -rf /var/lib/ldap
mkdir -p /var/lib/ldap
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "$db_init" 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
chown -R openldap: /var/lib/ldap
rm -rf /var/lib/ldap
mkdir -p /var/lib/ldap
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "$db_init" 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
chown -R openldap: /var/lib/ldap
nscd -i group || true
nscd -i passwd || true
nscd -i group || true
nscd -i passwd || true
systemctl restart slapd
systemctl restart slapd
# We don't use mkhomedir_helper because 'admin' may not be recognized
# when this script is ran in a chroot (e.g. ISO install)
# We also refer to admin as uid 1007 for the same reason
if [ ! -d /home/admin ]
then
cp -r /etc/skel /home/admin
chown -R 1007:1007 /home/admin
fi
# We don't use mkhomedir_helper because 'admin' may not be recognized
# when this script is ran in a chroot (e.g. ISO install)
# We also refer to admin as uid 1007 for the same reason
if [ ! -d /home/admin ]; then
cp -r /etc/skel /home/admin
chown -R 1007:1007 /home/admin
fi
}
_regenerate_slapd_conf() {
# Validate the new slapd config
# To do so, we have to use the .ldif to generate the config directory
# so we use a temporary directory slapd_new.d
rm -Rf /etc/ldap/slapd_new.d
mkdir /etc/ldap/slapd_new.d
slapadd -b cn=config -l "$config" -F /etc/ldap/slapd_new.d/ 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
# Actual validation (-Q is for quiet, -u is for dry-run)
slaptest -Q -u -F /etc/ldap/slapd_new.d
# Validate the new slapd config
# To do so, we have to use the .ldif to generate the config directory
# so we use a temporary directory slapd_new.d
rm -Rf /etc/ldap/slapd_new.d
mkdir /etc/ldap/slapd_new.d
slapadd -b cn=config -l "$config" -F /etc/ldap/slapd_new.d/ 2>&1 \
| grep -v "none elapsed\|Closing DB" || true
# Actual validation (-Q is for quiet, -u is for dry-run)
slaptest -Q -u -F /etc/ldap/slapd_new.d
# "Commit" / apply the new config (meaning we delete the old one and replace
# it with the new one)
rm -Rf /etc/ldap/slapd.d
mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d
# "Commit" / apply the new config (meaning we delete the old one and replace
# it with the new one)
rm -Rf /etc/ldap/slapd.d
mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d
chown -R openldap:openldap /etc/ldap/slapd.d/
chown -R openldap:openldap /etc/ldap/slapd.d/
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
# remove temporary backup file
rm -f "$tmp_backup_dir_file"
# remove temporary backup file
rm -f "$tmp_backup_dir_file"
# Define if we need to migrate from hdb to mdb
curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}')
if [ -e /etc/ldap/slapd.conf ] && [ -n "$curr_backend" ] && \
[ $curr_backend != 'mdb' ]; then
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
mkdir -p "$backup_dir"
slapcat -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
echo "$backup_dir" > "$tmp_backup_dir_file"
fi
# Define if we need to migrate from hdb to mdb
curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}')
if [ -e /etc/ldap/slapd.conf ] && [ -n "$curr_backend" ] \
&& [ $curr_backend != 'mdb' ]; then
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
mkdir -p "$backup_dir"
slapcat -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
echo "$backup_dir" >"$tmp_backup_dir_file"
fi
# create needed directories
ldap_dir="${pending_dir}/etc/ldap"
schema_dir="${ldap_dir}/schema"
mkdir -p "$ldap_dir" "$schema_dir"
# create needed directories
ldap_dir="${pending_dir}/etc/ldap"
schema_dir="${ldap_dir}/schema"
mkdir -p "$ldap_dir" "$schema_dir"
# remove legacy configuration file
[ ! -f /etc/ldap/slapd-yuno.conf ] || touch "${ldap_dir}/slapd-yuno.conf"
[ ! -f /etc/ldap/slapd.conf ] || touch "${ldap_dir}/slapd.conf"
[ ! -f /etc/ldap/schema/yunohost.schema ] || touch "${schema_dir}/yunohost.schema"
# remove legacy configuration file
[ ! -f /etc/ldap/slapd-yuno.conf ] || touch "${ldap_dir}/slapd-yuno.conf"
[ ! -f /etc/ldap/slapd.conf ] || touch "${ldap_dir}/slapd.conf"
[ ! -f /etc/ldap/schema/yunohost.schema ] || touch "${schema_dir}/yunohost.schema"
cd /usr/share/yunohost/templates/slapd
cd /usr/share/yunohost/templates/slapd
# copy configuration files
cp -a ldap.conf "$ldap_dir"
cp -a sudo.ldif mailserver.ldif permission.ldif "$schema_dir"
# copy configuration files
cp -a ldap.conf "$ldap_dir"
cp -a sudo.ldif mailserver.ldif permission.ldif "$schema_dir"
mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/
cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf
mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/
cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf
install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd"
install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
# fix some permissions
echo "Enforce permissions on ldap/slapd directories and certs ..."
# penldap user should be in the ssl-cert group to let it access the certificate for TLS
usermod -aG ssl-cert openldap
chown -R openldap:openldap /etc/ldap/schema/
chown -R openldap:openldap /etc/ldap/slapd.d/
# fix some permissions
echo "Enforce permissions on ldap/slapd directories and certs ..."
# penldap user should be in the ssl-cert group to let it access the certificate for TLS
usermod -aG ssl-cert openldap
chown -R openldap:openldap /etc/ldap/schema/
chown -R openldap:openldap /etc/ldap/slapd.d/
# If we changed the systemd ynh-override conf
if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/slapd.service.d/ynh-override.conf$"
then
systemctl daemon-reload
systemctl restart slapd
sleep 3
fi
# If we changed the systemd ynh-override conf
if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/slapd.service.d/ynh-override.conf$"; then
systemctl daemon-reload
systemctl restart slapd
sleep 3
fi
# For some reason, old setups don't have the admins group defined...
if ! slapcat | grep -q 'cn=admins,ou=groups,dc=yunohost,dc=org'
then
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org <<< \
"dn: cn=admins,ou=groups,dc=yunohost,dc=org
# For some reason, old setups don't have the admins group defined...
if ! slapcat | grep -q 'cn=admins,ou=groups,dc=yunohost,dc=org'; then
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org <<< \
"dn: cn=admins,ou=groups,dc=yunohost,dc=org
cn: admins
gidNumber: 4001
memberUid: admin
objectClass: posixGroup
objectClass: top"
chown -R openldap: /var/lib/ldap
systemctl restart slapd
nscd -i group
fi
chown -R openldap: /var/lib/ldap
systemctl restart slapd
nscd -i group
fi
[ -z "$regen_conf_files" ] && exit 0
[ -z "$regen_conf_files" ] && exit 0
# regenerate LDAP config directory from slapd.conf
echo "Regenerate LDAP config directory from config.ldif"
_regenerate_slapd_conf
# regenerate LDAP config directory from slapd.conf
echo "Regenerate LDAP config directory from config.ldif"
_regenerate_slapd_conf
# If there's a backup, re-import its data
backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true)
if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then
# regenerate LDAP config directory and import database as root
echo "Import the database using slapadd"
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
chown -R openldap:openldap /var/lib/ldap 2>&1
fi
# If there's a backup, re-import its data
backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true)
if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then
# regenerate LDAP config directory and import database as root
echo "Import the database using slapadd"
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
chown -R openldap:openldap /var/lib/ldap 2>&1
fi
echo "Running slapdindex"
su openldap -s "/bin/bash" -c "/usr/sbin/slapindex"
echo "Running slapdindex"
su openldap -s "/bin/bash" -c "/usr/sbin/slapindex"
echo "Reloading slapd"
systemctl force-reload slapd
echo "Reloading slapd"
systemctl force-reload slapd
# on slow hardware/vm this regen conf would exit before the admin user that
# is stored in ldap is available because ldap seems to slow to restart
# so we'll wait either until we are able to log as admin or until a timeout
# is reached
# we need to do this because the next hooks executed after this one during
# postinstall requires to run as admin thus breaking postinstall on slow
# hardware which mean yunohost can't be correctly installed on those hardware
# and this sucks
# wait a maximum time of 5 minutes
# yes, force-reload behave like a restart
number_of_wait=0
while ! su admin -c '' && ((number_of_wait < 60))
do
sleep 5
((number_of_wait += 1))
done
# on slow hardware/vm this regen conf would exit before the admin user that
# is stored in ldap is available because ldap seems to slow to restart
# so we'll wait either until we are able to log as admin or until a timeout
# is reached
# we need to do this because the next hooks executed after this one during
# postinstall requires to run as admin thus breaking postinstall on slow
# hardware which mean yunohost can't be correctly installed on those hardware
# and this sucks
# wait a maximum time of 5 minutes
# yes, force-reload behave like a restart
number_of_wait=0
while ! su admin -c '' && ((number_of_wait < 60)); do
sleep 5
((number_of_wait += 1))
done
}
do_$1_regen ${@:2}

View file

@ -3,23 +3,23 @@
set -e
do_init_regen() {
do_pre_regen ""
systemctl restart nslcd
do_pre_regen ""
systemctl restart nslcd
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/nslcd
cd /usr/share/yunohost/templates/nslcd
install -D -m 644 nslcd.conf "${pending_dir}/etc/nslcd.conf"
install -D -m 644 nslcd.conf "${pending_dir}/etc/nslcd.conf"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| systemctl restart nslcd
[[ -z "$regen_conf_files" ]] \
|| systemctl restart nslcd
}
do_$1_regen ${@:2}

View file

@ -8,15 +8,14 @@ do_pre_regen() {
mkdir --parents "${pending_dir}/etc/apt/preferences.d"
packages_to_refuse_from_sury="php php-fpm php-mysql php-xml php-zip php-mbstring php-ldap php-gd php-curl php-bz2 php-json php-sqlite3 php-intl openssl libssl1.1 libssl-dev"
for package in $packages_to_refuse_from_sury
do
for package in $packages_to_refuse_from_sury; do
echo "
Package: $package
Pin: origin \"packages.sury.org\"
Pin-Priority: -1" >> "${pending_dir}/etc/apt/preferences.d/extra_php_version"
Pin-Priority: -1" >>"${pending_dir}/etc/apt/preferences.d/extra_php_version"
done
echo "
echo "
# PLEASE READ THIS WARNING AND DON'T EDIT THIS FILE
@ -43,15 +42,15 @@ Pin-Priority: -1
Package: bind9
Pin: release *
Pin-Priority: -1
" >> "${pending_dir}/etc/apt/preferences.d/ban_packages"
" >>"${pending_dir}/etc/apt/preferences.d/ban_packages"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
# Make sure php7.3 is the default version when using php in cli
update-alternatives --set php /usr/bin/php7.3
# Make sure php7.3 is the default version when using php in cli
update-alternatives --set php /usr/bin/php7.3
}
do_$1_regen ${@:2}

View file

@ -3,71 +3,71 @@
set -e
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/metronome
cd /usr/share/yunohost/templates/metronome
# create directories for pending conf
metronome_dir="${pending_dir}/etc/metronome"
metronome_conf_dir="${metronome_dir}/conf.d"
mkdir -p "$metronome_conf_dir"
# create directories for pending conf
metronome_dir="${pending_dir}/etc/metronome"
metronome_conf_dir="${metronome_dir}/conf.d"
mkdir -p "$metronome_conf_dir"
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# install main conf file
cat metronome.cfg.lua \
| sed "s/{{ main_domain }}/${main_domain}/g" \
> "${metronome_dir}/metronome.cfg.lua"
# install main conf file
cat metronome.cfg.lua \
| sed "s/{{ main_domain }}/${main_domain}/g" \
>"${metronome_dir}/metronome.cfg.lua"
# add domain conf files
for domain in $YNH_DOMAINS; do
cat domain.tpl.cfg.lua \
| sed "s/{{ domain }}/${domain}/g" \
> "${metronome_conf_dir}/${domain}.cfg.lua"
done
# add domain conf files
for domain in $YNH_DOMAINS; do
cat domain.tpl.cfg.lua \
| sed "s/{{ domain }}/${domain}/g" \
>"${metronome_conf_dir}/${domain}.cfg.lua"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/metronome/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.cfg\.lua$/ { print $1 }')
for file in $conf_files; do
domain=${file%.cfg.lua}
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${metronome_conf_dir}/${file}"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/metronome/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.cfg\.lua$/ { print $1 }')
for file in $conf_files; do
domain=${file%.cfg.lua}
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${metronome_conf_dir}/${file}"
done
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# FIXME : small optimization to do to avoid calling a yunohost command ...
# maybe another env variable like YNH_MAIN_DOMAINS idk
domain_list=$(yunohost domain list --exclude-subdomains --output-as plain --quiet)
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# create metronome directories for domains
for domain in $domain_list; do
mkdir -p "/var/lib/metronome/${domain//./%2e}/pep"
# http_upload directory must be writable by metronome and readable by nginx
mkdir -p "/var/xmpp-upload/${domain}/upload"
# sgid bit allows that file created in that dir will be owned by www-data
# despite the fact that metronome ain't in the www-data group
chmod g+s "/var/xmpp-upload/${domain}/upload"
done
# FIXME : small optimization to do to avoid calling a yunohost command ...
# maybe another env variable like YNH_MAIN_DOMAINS idk
domain_list=$(yunohost domain list --exclude-subdomains --output-as plain --quiet)
# fix some permissions
[ ! -e '/var/xmpp-upload' ] || chown -R metronome:www-data "/var/xmpp-upload/"
[ ! -e '/var/xmpp-upload' ] || chmod 750 "/var/xmpp-upload/"
# create metronome directories for domains
for domain in $domain_list; do
mkdir -p "/var/lib/metronome/${domain//./%2e}/pep"
# http_upload directory must be writable by metronome and readable by nginx
mkdir -p "/var/xmpp-upload/${domain}/upload"
# sgid bit allows that file created in that dir will be owned by www-data
# despite the fact that metronome ain't in the www-data group
chmod g+s "/var/xmpp-upload/${domain}/upload"
done
# metronome should be in ssl-cert group to let it access SSL certificates
usermod -aG ssl-cert metronome
chown -R metronome: /var/lib/metronome/
chown -R metronome: /etc/metronome/conf.d/
# fix some permissions
[ ! -e '/var/xmpp-upload' ] || chown -R metronome:www-data "/var/xmpp-upload/"
[ ! -e '/var/xmpp-upload' ] || chmod 750 "/var/xmpp-upload/"
[[ -z "$regen_conf_files" ]] \
|| systemctl restart metronome
# metronome should be in ssl-cert group to let it access SSL certificates
usermod -aG ssl-cert metronome
chown -R metronome: /var/lib/metronome/
chown -R metronome: /etc/metronome/conf.d/
[[ -z "$regen_conf_files" ]] \
|| systemctl restart metronome
}
do_$1_regen ${@:2}

View file

@ -5,148 +5,156 @@ set -e
. /usr/share/yunohost/helpers
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
cd /usr/share/yunohost/templates/nginx
cd /usr/share/yunohost/templates/nginx
nginx_dir="/etc/nginx"
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
nginx_dir="/etc/nginx"
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
# install plain conf files
cp plain/* "$nginx_conf_dir"
# install plain conf files
cp plain/* "$nginx_conf_dir"
# probably run with init: just disable default site, restart NGINX and exit
rm -f "${nginx_dir}/sites-enabled/default"
# probably run with init: just disable default site, restart NGINX and exit
rm -f "${nginx_dir}/sites-enabled/default"
export compatibility="intermediate"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
ynh_render_template "yunohost_api.conf.inc" "${nginx_conf_dir}/yunohost_api.conf.inc"
export compatibility="intermediate"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
ynh_render_template "yunohost_api.conf.inc" "${nginx_conf_dir}/yunohost_api.conf.inc"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
# Restart nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null || { nginx -t; exit 1; }
systemctl restart nginx || { journalctl --no-pager --lines=10 -u nginx >&2; exit 1; }
# Restart nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null || {
nginx -t
exit 1
}
systemctl restart nginx || {
journalctl --no-pager --lines=10 -u nginx >&2
exit 1
}
exit 0
exit 0
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/nginx
cd /usr/share/yunohost/templates/nginx
nginx_dir="${pending_dir}/etc/nginx"
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
nginx_dir="${pending_dir}/etc/nginx"
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
# install / update plain conf files
cp plain/* "$nginx_conf_dir"
# remove the panel overlay if this is specified in settings
panel_overlay=$(yunohost settings get 'ssowat.panel_overlay.enabled')
if [ "$panel_overlay" == "false" ] || [ "$panel_overlay" == "False" ]
then
echo "#" > "${nginx_conf_dir}/yunohost_panel.conf.inc"
fi
# install / update plain conf files
cp plain/* "$nginx_conf_dir"
# remove the panel overlay if this is specified in settings
panel_overlay=$(yunohost settings get 'ssowat.panel_overlay.enabled')
if [ "$panel_overlay" == "false" ] || [ "$panel_overlay" == "False" ]; then
echo "#" >"${nginx_conf_dir}/yunohost_panel.conf.inc"
fi
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
# Support different strategy for security configurations
export redirect_to_https="$(yunohost settings get 'security.nginx.redirect_to_https')"
export compatibility="$(yunohost settings get 'security.nginx.compatibility')"
export experimental="$(yunohost settings get 'security.experimental.enabled')"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
# Support different strategy for security configurations
export redirect_to_https="$(yunohost settings get 'security.nginx.redirect_to_https')"
export compatibility="$(yunohost settings get 'security.nginx.compatibility')"
export experimental="$(yunohost settings get 'security.experimental.enabled')"
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
cert_status=$(yunohost domain cert status --json)
cert_status=$(yunohost domain cert status --json)
# add domain conf files
for domain in $YNH_DOMAINS; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
mkdir -p "$domain_conf_dir"
mail_autoconfig_dir="${pending_dir}/var/www/.well-known/${domain}/autoconfig/mail/"
mkdir -p "$mail_autoconfig_dir"
# add domain conf files
for domain in $YNH_DOMAINS; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
mkdir -p "$domain_conf_dir"
mail_autoconfig_dir="${pending_dir}/var/www/.well-known/${domain}/autoconfig/mail/"
mkdir -p "$mail_autoconfig_dir"
# NGINX server configuration
export domain
export domain_cert_ca=$(echo $cert_status \
| jq ".certificates.\"$domain\".CA_type" \
| tr -d '"')
# NGINX server configuration
export domain
export domain_cert_ca=$(echo $cert_status \
| jq ".certificates.\"$domain\".CA_type" \
| tr -d '"')
ynh_render_template "server.tpl.conf" "${nginx_conf_dir}/${domain}.conf"
ynh_render_template "autoconfig.tpl.xml" "${mail_autoconfig_dir}/config-v1.1.xml"
ynh_render_template "server.tpl.conf" "${nginx_conf_dir}/${domain}.conf"
ynh_render_template "autoconfig.tpl.xml" "${mail_autoconfig_dir}/config-v1.1.xml"
touch "${domain_conf_dir}/yunohost_local.conf" # Clean legacy conf files
touch "${domain_conf_dir}/yunohost_local.conf" # Clean legacy conf files
done
done
export webadmin_allowlist_enabled=$(yunohost settings get security.webadmin.allowlist.enabled)
if [ "$webadmin_allowlist_enabled" == "True" ]
then
export webadmin_allowlist=$(yunohost settings get security.webadmin.allowlist)
fi
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
ynh_render_template "yunohost_api.conf.inc" "${nginx_conf_dir}/yunohost_api.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
export webadmin_allowlist_enabled=$(yunohost settings get security.webadmin.allowlist.enabled)
if [ "$webadmin_allowlist_enabled" == "True" ]; then
export webadmin_allowlist=$(yunohost settings get security.webadmin.allowlist)
fi
ynh_render_template "yunohost_admin.conf.inc" "${nginx_conf_dir}/yunohost_admin.conf.inc"
ynh_render_template "yunohost_api.conf.inc" "${nginx_conf_dir}/yunohost_api.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
# remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }')
for file in $conf_files; do
domain=${file%.conf}
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${nginx_conf_dir}/${file}"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }')
for file in $conf_files; do
domain=${file%.conf}
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${nginx_conf_dir}/${file}"
done
# remove old mail-autoconfig files
autoconfig_files=$(ls -1 /var/www/.well-known/*/autoconfig/mail/config-v1.1.xml 2>/dev/null || true)
for file in $autoconfig_files; do
domain=$(basename $(readlink -f $(dirname $file)/../..))
[[ $YNH_DOMAINS =~ $domain ]] \
|| (mkdir -p "$(dirname ${pending_dir}/${file})" && touch "${pending_dir}/${file}")
done
# remove old mail-autoconfig files
autoconfig_files=$(ls -1 /var/www/.well-known/*/autoconfig/mail/config-v1.1.xml 2>/dev/null || true)
for file in $autoconfig_files; do
domain=$(basename $(readlink -f $(dirname $file)/../..))
[[ $YNH_DOMAINS =~ $domain ]] \
|| (mkdir -p "$(dirname ${pending_dir}/${file})" && touch "${pending_dir}/${file}")
done
# disable default site
mkdir -p "${nginx_dir}/sites-enabled"
touch "${nginx_dir}/sites-enabled/default"
# disable default site
mkdir -p "${nginx_dir}/sites-enabled"
touch "${nginx_dir}/sites-enabled/default"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0
[ -z "$regen_conf_files" ] && exit 0
# create NGINX conf directories for domains
for domain in $YNH_DOMAINS; do
mkdir -p "/etc/nginx/conf.d/${domain}.d"
done
# create NGINX conf directories for domains
for domain in $YNH_DOMAINS; do
mkdir -p "/etc/nginx/conf.d/${domain}.d"
done
# Get rid of legacy lets encrypt snippets
for domain in $YNH_DOMAINS; do
# If the legacy letsencrypt / acme-challenge domain-specific snippet is still there
if [ -e /etc/nginx/conf.d/${domain}.d/000-acmechallenge.conf ]
then
# And if we're effectively including the new domain-independant snippet now
if grep -q "include /etc/nginx/conf.d/acme-challenge.conf.inc;" /etc/nginx/conf.d/${domain}.conf
then
# Delete the old domain-specific snippet
rm /etc/nginx/conf.d/${domain}.d/000-acmechallenge.conf
fi
fi
done
# Get rid of legacy lets encrypt snippets
for domain in $YNH_DOMAINS; do
# If the legacy letsencrypt / acme-challenge domain-specific snippet is still there
if [ -e /etc/nginx/conf.d/${domain}.d/000-acmechallenge.conf ]; then
# And if we're effectively including the new domain-independant snippet now
if grep -q "include /etc/nginx/conf.d/acme-challenge.conf.inc;" /etc/nginx/conf.d/${domain}.conf; then
# Delete the old domain-specific snippet
rm /etc/nginx/conf.d/${domain}.d/000-acmechallenge.conf
fi
fi
done
# Reload nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null || { nginx -t; exit 1; }
pgrep nginx && systemctl reload nginx || { journalctl --no-pager --lines=10 -u nginx >&2; exit 1; }
# Reload nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null || {
nginx -t
exit 1
}
pgrep nginx && systemctl reload nginx || {
journalctl --no-pager --lines=10 -u nginx >&2
exit 1
}
}
do_$1_regen ${@:2}

View file

@ -5,78 +5,76 @@ set -e
. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/postfix
cd /usr/share/yunohost/templates/postfix
postfix_dir="${pending_dir}/etc/postfix"
mkdir -p "$postfix_dir"
postfix_dir="${pending_dir}/etc/postfix"
mkdir -p "$postfix_dir"
default_dir="${pending_dir}/etc/default/"
mkdir -p "$default_dir"
default_dir="${pending_dir}/etc/default/"
mkdir -p "$default_dir"
# install plain conf files
cp plain/* "$postfix_dir"
# install plain conf files
cp plain/* "$postfix_dir"
# prepare main.cf conf file
main_domain=$(cat /etc/yunohost/current_host)
# prepare main.cf conf file
main_domain=$(cat /etc/yunohost/current_host)
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.postfix.compatibility')"
# Add possibility to specify a relay
# Could be useful with some isp with no 25 port open or more complex setup
export relay_port=""
export relay_user=""
export relay_host="$(yunohost settings get 'smtp.relay.host')"
if [ -n "${relay_host}" ]
then
relay_port="$(yunohost settings get 'smtp.relay.port')"
relay_user="$(yunohost settings get 'smtp.relay.user')"
relay_password="$(yunohost settings get 'smtp.relay.password')"
# Avoid to display "Relay account paswword" to other users
touch ${postfix_dir}/sasl_passwd
chmod 750 ${postfix_dir}/sasl_passwd
# Avoid "postmap: warning: removing zero-length database file"
chown postfix ${pending_dir}/etc/postfix
chown postfix ${pending_dir}/etc/postfix/sasl_passwd
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.postfix.compatibility')"
cat <<< "[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" > ${postfix_dir}/sasl_passwd
postmap ${postfix_dir}/sasl_passwd
fi
export main_domain
export domain_list="$YNH_DOMAINS"
ynh_render_template "main.cf" "${postfix_dir}/main.cf"
# Add possibility to specify a relay
# Could be useful with some isp with no 25 port open or more complex setup
export relay_port=""
export relay_user=""
export relay_host="$(yunohost settings get 'smtp.relay.host')"
if [ -n "${relay_host}" ]; then
relay_port="$(yunohost settings get 'smtp.relay.port')"
relay_user="$(yunohost settings get 'smtp.relay.user')"
relay_password="$(yunohost settings get 'smtp.relay.password')"
cat postsrsd \
| sed "s/{{ main_domain }}/${main_domain}/g" \
| sed "s/{{ domain_list }}/${YNH_DOMAINS}/g" \
> "${default_dir}/postsrsd"
# Avoid to display "Relay account paswword" to other users
touch ${postfix_dir}/sasl_passwd
chmod 750 ${postfix_dir}/sasl_passwd
# Avoid "postmap: warning: removing zero-length database file"
chown postfix ${pending_dir}/etc/postfix
chown postfix ${pending_dir}/etc/postfix/sasl_passwd
# adapt it for IPv4-only hosts
ipv6="$(yunohost settings get 'smtp.allow_ipv6')"
if [ "$ipv6" == "False" ] || [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \
"${postfix_dir}/main.cf"
sed -i \
's/inet_interfaces = all/&\ninet_protocols = ipv4/' \
"${postfix_dir}/main.cf"
fi
cat <<<"[${relay_host}]:${relay_port} ${relay_user}:${relay_password}" >${postfix_dir}/sasl_passwd
postmap ${postfix_dir}/sasl_passwd
fi
export main_domain
export domain_list="$YNH_DOMAINS"
ynh_render_template "main.cf" "${postfix_dir}/main.cf"
cat postsrsd \
| sed "s/{{ main_domain }}/${main_domain}/g" \
| sed "s/{{ domain_list }}/${YNH_DOMAINS}/g" \
>"${default_dir}/postsrsd"
# adapt it for IPv4-only hosts
ipv6="$(yunohost settings get 'smtp.allow_ipv6')"
if [ "$ipv6" == "False" ] || [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \
"${postfix_dir}/main.cf"
sed -i \
's/inet_interfaces = all/&\ninet_protocols = ipv4/' \
"${postfix_dir}/main.cf"
fi
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
if [ -e /etc/postfix/sasl_passwd ]
then
chmod 750 /etc/postfix/sasl_passwd*
chown postfix:root /etc/postfix/sasl_passwd*
fi
if [ -e /etc/postfix/sasl_passwd ]; then
chmod 750 /etc/postfix/sasl_passwd*
chown postfix:root /etc/postfix/sasl_passwd*
fi
[[ -z "$regen_conf_files" ]] \
|| { systemctl restart postfix && systemctl restart postsrsd; }
[[ -z "$regen_conf_files" ]] \
|| { systemctl restart postfix && systemctl restart postsrsd; }
}

View file

@ -5,62 +5,62 @@ set -e
. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/dovecot
cd /usr/share/yunohost/templates/dovecot
dovecot_dir="${pending_dir}/etc/dovecot"
mkdir -p "${dovecot_dir}/global_script"
dovecot_dir="${pending_dir}/etc/dovecot"
mkdir -p "${dovecot_dir}/global_script"
# copy simple conf files
cp dovecot-ldap.conf "${dovecot_dir}/dovecot-ldap.conf"
cp dovecot.sieve "${dovecot_dir}/global_script/dovecot.sieve"
# copy simple conf files
cp dovecot-ldap.conf "${dovecot_dir}/dovecot-ldap.conf"
cp dovecot.sieve "${dovecot_dir}/global_script/dovecot.sieve"
export pop3_enabled="$(yunohost settings get 'pop3.enabled')"
export main_domain=$(cat /etc/yunohost/current_host)
export pop3_enabled="$(yunohost settings get 'pop3.enabled')"
export main_domain=$(cat /etc/yunohost/current_host)
ynh_render_template "dovecot.conf" "${dovecot_dir}/dovecot.conf"
ynh_render_template "dovecot.conf" "${dovecot_dir}/dovecot.conf"
# adapt it for IPv4-only hosts
if [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/^\(listen =\).*/\1 */' \
"${dovecot_dir}/dovecot.conf"
fi
# adapt it for IPv4-only hosts
if [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/^\(listen =\).*/\1 */' \
"${dovecot_dir}/dovecot.conf"
fi
mkdir -p "${dovecot_dir}/yunohost.d"
cp pre-ext.conf "${dovecot_dir}/yunohost.d"
cp post-ext.conf "${dovecot_dir}/yunohost.d"
mkdir -p "${dovecot_dir}/yunohost.d"
cp pre-ext.conf "${dovecot_dir}/yunohost.d"
cp post-ext.conf "${dovecot_dir}/yunohost.d"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
mkdir -p "/etc/dovecot/yunohost.d/pre-ext.d"
mkdir -p "/etc/dovecot/yunohost.d/post-ext.d"
mkdir -p "/etc/dovecot/yunohost.d/pre-ext.d"
mkdir -p "/etc/dovecot/yunohost.d/post-ext.d"
# create vmail user
id vmail > /dev/null 2>&1 \
|| adduser --system --ingroup mail --uid 500 vmail --home /var/vmail --no-create-home
# create vmail user
id vmail >/dev/null 2>&1 \
|| adduser --system --ingroup mail --uid 500 vmail --home /var/vmail --no-create-home
# Delete legacy home for vmail that existed in the past but was empty, poluting /home/
[ ! -e /home/vmail ] || rmdir --ignore-fail-on-non-empty /home/vmail
# Delete legacy home for vmail that existed in the past but was empty, poluting /home/
[ ! -e /home/vmail ] || rmdir --ignore-fail-on-non-empty /home/vmail
# fix permissions
chown -R vmail:mail /etc/dovecot/global_script
chmod 770 /etc/dovecot/global_script
chown root:mail /var/mail
chmod 1775 /var/mail
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ dovecot\.sieve ]] && {
sievec /etc/dovecot/global_script/dovecot.sieve
# fix permissions
chown -R vmail:mail /etc/dovecot/global_script
}
chmod 770 /etc/dovecot/global_script
chown root:mail /var/mail
chmod 1775 /var/mail
systemctl restart dovecot
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ dovecot\.sieve ]] && {
sievec /etc/dovecot/global_script/dovecot.sieve
chown -R vmail:mail /etc/dovecot/global_script
}
systemctl restart dovecot
}
do_$1_regen ${@:2}

View file

@ -3,60 +3,60 @@
set -e
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/rspamd
cd /usr/share/yunohost/templates/rspamd
install -D -m 644 metrics.local.conf \
"${pending_dir}/etc/rspamd/local.d/metrics.conf"
install -D -m 644 dkim_signing.conf \
"${pending_dir}/etc/rspamd/local.d/dkim_signing.conf"
install -D -m 644 rspamd.sieve \
"${pending_dir}/etc/dovecot/global_script/rspamd.sieve"
install -D -m 644 metrics.local.conf \
"${pending_dir}/etc/rspamd/local.d/metrics.conf"
install -D -m 644 dkim_signing.conf \
"${pending_dir}/etc/rspamd/local.d/dkim_signing.conf"
install -D -m 644 rspamd.sieve \
"${pending_dir}/etc/dovecot/global_script/rspamd.sieve"
}
do_post_regen() {
##
## DKIM key generation
##
##
## DKIM key generation
##
# create DKIM directory with proper permission
mkdir -p /etc/dkim
chown _rspamd /etc/dkim
# create DKIM directory with proper permission
mkdir -p /etc/dkim
chown _rspamd /etc/dkim
# create DKIM key for domains
for domain in $YNH_DOMAINS; do
domain_key="/etc/dkim/${domain}.mail.key"
[ ! -f "$domain_key" ] && {
# We use a 1024 bit size because nsupdate doesn't seem to be able to
# handle 2048...
opendkim-genkey --domain="$domain" \
--selector=mail --directory=/etc/dkim -b 1024
mv /etc/dkim/mail.private "$domain_key"
mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt"
# create DKIM key for domains
for domain in $YNH_DOMAINS; do
domain_key="/etc/dkim/${domain}.mail.key"
[ ! -f "$domain_key" ] && {
# We use a 1024 bit size because nsupdate doesn't seem to be able to
# handle 2048...
opendkim-genkey --domain="$domain" \
--selector=mail --directory=/etc/dkim -b 1024
mv /etc/dkim/mail.private "$domain_key"
mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt"
}
done
# fix DKIM keys permissions
chown _rspamd /etc/dkim/*.mail.key
chmod 400 /etc/dkim/*.mail.key
[ ! -e /var/log/rspamd ] || chown -R _rspamd:_rspamd /var/log/rspamd
regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ rspamd\.sieve ]] && {
sievec /etc/dovecot/global_script/rspamd.sieve
chown -R vmail:mail /etc/dovecot/global_script
systemctl restart dovecot
}
done
# fix DKIM keys permissions
chown _rspamd /etc/dkim/*.mail.key
chmod 400 /etc/dkim/*.mail.key
[ ! -e /var/log/rspamd ] || chown -R _rspamd:_rspamd /var/log/rspamd
regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ rspamd\.sieve ]] && {
sievec /etc/dovecot/global_script/rspamd.sieve
chown -R vmail:mail /etc/dovecot/global_script
systemctl restart dovecot
}
# Restart rspamd due to the upgrade
# https://rspamd.com/announce/2016/08/01/rspamd-1.3.1.html
systemctl -q restart rspamd.service
# Restart rspamd due to the upgrade
# https://rspamd.com/announce/2016/08/01/rspamd-1.3.1.html
systemctl -q restart rspamd.service
}
do_$1_regen ${@:2}

View file

@ -4,69 +4,65 @@ set -e
. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/mysql
cd /usr/share/yunohost/templates/mysql
install -D -m 644 my.cnf "${pending_dir}/etc/mysql/my.cnf"
install -D -m 644 my.cnf "${pending_dir}/etc/mysql/my.cnf"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
if [[ ! -d /var/lib/mysql/mysql ]]
then
# dpkg-reconfigure will initialize mysql (if it ain't already)
# It enabled auth_socket for root, so no need to define any root password...
# c.f. : cat /var/lib/dpkg/info/mariadb-server-10.3.postinst | grep install_db -C3
MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1
if [[ ! -d /var/lib/mysql/mysql ]]; then
# dpkg-reconfigure will initialize mysql (if it ain't already)
# It enabled auth_socket for root, so no need to define any root password...
# c.f. : cat /var/lib/dpkg/info/mariadb-server-10.3.postinst | grep install_db -C3
MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')"
dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1
systemctl -q is-active mariadb.service \
|| systemctl start mariadb
systemctl -q is-active mariadb.service \
|| systemctl start mariadb
sleep 5
sleep 5
echo "" | mysql && echo "Can't connect to mysql using unix_socket auth ... something went wrong during initial configuration of mysql !?" >&2
fi
echo "" | mysql && echo "Can't connect to mysql using unix_socket auth ... something went wrong during initial configuration of mysql !?" >&2
fi
# Legacy code to get rid of /etc/yunohost/mysql ...
# Nowadays, we can simply run mysql while being run as root of unix_socket/auth_socket is enabled...
if [ -f /etc/yunohost/mysql ]; then
# Legacy code to get rid of /etc/yunohost/mysql ...
# Nowadays, we can simply run mysql while being run as root of unix_socket/auth_socket is enabled...
if [ -f /etc/yunohost/mysql ]; then
# This is a trick to check if we're able to use mysql without password
# Expect instances installed in stretch to already have unix_socket
#configured, but not old instances from the jessie/wheezy era
if ! echo "" | mysql 2>/dev/null
then
password="$(cat /etc/yunohost/mysql)"
# Enable plugin unix_socket for root on localhost
mysql -u root -p"$password" <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED WITH unix_socket WITH GRANT OPTION;"
fi
# This is a trick to check if we're able to use mysql without password
# Expect instances installed in stretch to already have unix_socket
#configured, but not old instances from the jessie/wheezy era
if ! echo "" | mysql 2>/dev/null; then
password="$(cat /etc/yunohost/mysql)"
# Enable plugin unix_socket for root on localhost
mysql -u root -p"$password" <<<"GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED WITH unix_socket WITH GRANT OPTION;"
fi
# If now we're able to login without password, drop the mysql password
if echo "" | mysql 2>/dev/null
then
rm /etc/yunohost/mysql
else
echo "Can't connect to mysql using unix_socket auth ... something went wrong while trying to get rid of mysql password !?" >&2
fi
fi
# If now we're able to login without password, drop the mysql password
if echo "" | mysql 2>/dev/null; then
rm /etc/yunohost/mysql
else
echo "Can't connect to mysql using unix_socket auth ... something went wrong while trying to get rid of mysql password !?" >&2
fi
fi
# mysql is supposed to be an alias to mariadb... but in some weird case is not
# c.f. https://forum.yunohost.org/t/mysql-ne-fonctionne-pas/11661
# Playing with enable/disable allows to recreate the proper symlinks.
if [ ! -e /etc/systemd/system/mysql.service ]
then
systemctl stop mysql -q
systemctl disable mysql -q
systemctl disable mariadb -q
systemctl enable mariadb -q
systemctl is-active mariadb -q || systemctl start mariadb
fi
# mysql is supposed to be an alias to mariadb... but in some weird case is not
# c.f. https://forum.yunohost.org/t/mysql-ne-fonctionne-pas/11661
# Playing with enable/disable allows to recreate the proper symlinks.
if [ ! -e /etc/systemd/system/mysql.service ]; then
systemctl stop mysql -q
systemctl disable mysql -q
systemctl disable mariadb -q
systemctl enable mariadb -q
systemctl is-active mariadb -q || systemctl start mariadb
fi
[[ -z "$regen_conf_files" ]] \
|| systemctl restart mysql
[[ -z "$regen_conf_files" ]] \
|| systemctl restart mysql
}
do_$1_regen ${@:2}

View file

@ -1,13 +1,13 @@
#!/bin/bash
do_pre_regen() {
:
:
}
do_post_regen() {
# Enforce these damn permissions because for some reason in some weird cases
# they are spontaneously replaced by root:root -_-
chown -R redis:adm /var/log/redis
# Enforce these damn permissions because for some reason in some weird cases
# they are spontaneously replaced by root:root -_-
chown -R redis:adm /var/log/redis
}
do_$1_regen ${@:2}

View file

@ -3,62 +3,52 @@
set -e
_generate_config() {
echo "domains:"
echo " - yunohost.local"
for domain in $YNH_DOMAINS
do
# Only keep .local domains (don't keep
[[ "$domain" =~ [^.]+\.[^.]+\.local$ ]] && echo "Subdomain $domain cannot be handled by Bonjour/Zeroconf/mDNS" >&2
[[ "$domain" =~ ^[^.]+\.local$ ]] || continue
echo " - $domain"
done
echo "interfaces:"
local_network_interfaces="$(ip --brief a | grep ' 10\.\| 192\.168\.' | awk '{print $1}')"
for interface in $local_network_interfaces
do
echo " - $interface"
done
echo "domains:"
echo " - yunohost.local"
for domain in $YNH_DOMAINS; do
# Only keep .local domains (don't keep
[[ "$domain" =~ [^.]+\.[^.]+\.local$ ]] && echo "Subdomain $domain cannot be handled by Bonjour/Zeroconf/mDNS" >&2
[[ "$domain" =~ ^[^.]+\.local$ ]] || continue
echo " - $domain"
done
}
do_init_regen() {
do_pre_regen
do_post_regen /etc/systemd/system/yunomdns.service
systemctl enable yunomdns
do_pre_regen
do_post_regen /etc/systemd/system/yunomdns.service
systemctl enable yunomdns
}
do_pre_regen() {
pending_dir="$1"
pending_dir="$1"
cd /usr/share/yunohost/templates/mdns
mkdir -p ${pending_dir}/etc/systemd/system/
cp yunomdns.service ${pending_dir}/etc/systemd/system/
cd /usr/share/yunohost/templates/mdns
mkdir -p ${pending_dir}/etc/systemd/system/
cp yunomdns.service ${pending_dir}/etc/systemd/system/
getent passwd mdns &>/dev/null || useradd --no-create-home --shell /usr/sbin/nologin --system --user-group mdns
getent passwd mdns &>/dev/null || useradd --no-create-home --shell /usr/sbin/nologin --system --user-group mdns
mkdir -p ${pending_dir}/etc/yunohost
_generate_config > ${pending_dir}/etc/yunohost/mdns.yml
mkdir -p ${pending_dir}/etc/yunohost
_generate_config >${pending_dir}/etc/yunohost/mdns.yml
}
do_post_regen() {
regen_conf_files="$1"
regen_conf_files="$1"
chown mdns:mdns /etc/yunohost/mdns.yml
chown mdns:mdns /etc/yunohost/mdns.yml
# If we changed the systemd ynh-override conf
if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/yunomdns.service$"
then
systemctl daemon-reload
fi
# If we changed the systemd ynh-override conf
if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/yunomdns.service$"; then
systemctl daemon-reload
fi
# Legacy stuff to enable the new yunomdns service on legacy systems
if [[ -e /etc/avahi/avahi-daemon.conf ]] && grep -q 'yunohost' /etc/avahi/avahi-daemon.conf
then
systemctl enable yunomdns
fi
# Legacy stuff to enable the new yunomdns service on legacy systems
if [[ -e /etc/avahi/avahi-daemon.conf ]] && grep -q 'yunohost' /etc/avahi/avahi-daemon.conf; then
systemctl enable yunomdns
fi
[[ -z "$regen_conf_files" ]] \
|| systemctl restart yunomdns
[[ -z "$regen_conf_files" ]] \
|| systemctl restart yunomdns
}
do_$1_regen ${@:2}

View file

@ -4,80 +4,80 @@ set -e
. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/dnsmasq
cd /usr/share/yunohost/templates/dnsmasq
# create directory for pending conf
dnsmasq_dir="${pending_dir}/etc/dnsmasq.d"
mkdir -p "$dnsmasq_dir"
etcdefault_dir="${pending_dir}/etc/default"
mkdir -p "$etcdefault_dir"
# create directory for pending conf
dnsmasq_dir="${pending_dir}/etc/dnsmasq.d"
mkdir -p "$dnsmasq_dir"
etcdefault_dir="${pending_dir}/etc/default"
mkdir -p "$etcdefault_dir"
# add general conf files
cp plain/etcdefault ${pending_dir}/etc/default/dnsmasq
cp plain/dnsmasq.conf ${pending_dir}/etc/dnsmasq.conf
# add general conf files
cp plain/etcdefault ${pending_dir}/etc/default/dnsmasq
cp plain/dnsmasq.conf ${pending_dir}/etc/dnsmasq.conf
# add resolver file
cat plain/resolv.dnsmasq.conf | grep "^nameserver" | shuf > ${pending_dir}/etc/resolv.dnsmasq.conf
# add resolver file
cat plain/resolv.dnsmasq.conf | grep "^nameserver" | shuf >${pending_dir}/etc/resolv.dnsmasq.conf
# retrieve variables
ipv4=$(curl -s -4 https://ip.yunohost.org 2>/dev/null || true)
ynh_validate_ip4 "$ipv4" || ipv4='127.0.0.1'
ipv6=$(curl -s -6 https://ip6.yunohost.org 2>/dev/null || true)
ynh_validate_ip6 "$ipv6" || ipv6=''
# retrieve variables
ipv4=$(curl -s -4 https://ip.yunohost.org 2>/dev/null || true)
ynh_validate_ip4 "$ipv4" || ipv4='127.0.0.1'
ipv6=$(curl -s -6 https://ip6.yunohost.org 2>/dev/null || true)
ynh_validate_ip6 "$ipv6" || ipv6=''
export ipv4
export ipv6
export ipv4
export ipv6
# add domain conf files
for domain in $YNH_DOMAINS; do
export domain
ynh_render_template "domain.tpl" "${dnsmasq_dir}/${domain}"
done
# add domain conf files
for domain in $YNH_DOMAINS; do
[[ ! $domain =~ \.local$ ]] || continue
export domain
ynh_render_template "domain.tpl" "${dnsmasq_dir}/${domain}"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/dnsmasq.d \
| awk '/^[^\.]+\.[^\.]+.*$/ { print $1 }')
for domain in $conf_files; do
[[ $YNH_DOMAINS =~ $domain ]] \
|| touch "${dnsmasq_dir}/${domain}"
done
# remove old domain conf files
conf_files=$(ls -1 /etc/dnsmasq.d \
| awk '/^[^\.]+\.[^\.]+.*$/ { print $1 }')
for domain in $conf_files; do
if [[ ! $YNH_DOMAINS =~ $domain ]] && [[ ! $domain =~ \.local$ ]]
then
touch "${dnsmasq_dir}/${domain}"
fi
done
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
# Fuck it, those domain/search entries from dhclient are usually annoying
# lying shit from the ISP trying to MiTM
if grep -q -E "^ *(domain|search)" /run/resolvconf/resolv.conf
then
if grep -q -E "^ *(domain|search)" /run/resolvconf/interface/*.dhclient 2>/dev/null
then
sed -E "s/^(domain|search)/#\1/g" -i /run/resolvconf/interface/*.dhclient
fi
# Fuck it, those domain/search entries from dhclient are usually annoying
# lying shit from the ISP trying to MiTM
if grep -q -E "^ *(domain|search)" /run/resolvconf/resolv.conf; then
if grep -q -E "^ *(domain|search)" /run/resolvconf/interface/*.dhclient 2>/dev/null; then
sed -E "s/^(domain|search)/#\1/g" -i /run/resolvconf/interface/*.dhclient
fi
grep -q '^supersede domain-name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-name "";' >> /etc/dhcp/dhclient.conf
grep -q '^supersede domain-search "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-search "";' >> /etc/dhcp/dhclient.conf
grep -q '^supersede name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede name "";' >> /etc/dhcp/dhclient.conf
systemctl restart resolvconf
fi
grep -q '^supersede domain-name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-name "";' >>/etc/dhcp/dhclient.conf
grep -q '^supersede domain-search "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-search "";' >>/etc/dhcp/dhclient.conf
grep -q '^supersede name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede name "";' >>/etc/dhcp/dhclient.conf
systemctl restart resolvconf
fi
# Some stupid things like rabbitmq-server used by onlyoffice won't work if
# the *short* hostname doesn't exists in /etc/hosts -_-
short_hostname=$(hostname -s)
grep -q "127.0.0.1.*$short_hostname" /etc/hosts || echo -e "\n127.0.0.1\t$short_hostname" >>/etc/hosts
# Some stupid things like rabbitmq-server used by onlyoffice won't work if
# the *short* hostname doesn't exists in /etc/hosts -_-
short_hostname=$(hostname -s)
grep -q "127.0.0.1.*$short_hostname" /etc/hosts || echo -e "\n127.0.0.1\t$short_hostname" >>/etc/hosts
[[ -n "$regen_conf_files" ]] || return
[[ -n "$regen_conf_files" ]] || return
# Remove / disable services likely to conflict with dnsmasq
for SERVICE in systemd-resolved bind9
do
systemctl is-enabled $SERVICE &>/dev/null && systemctl disable $SERVICE 2>/dev/null
systemctl is-active $SERVICE &>/dev/null && systemctl stop $SERVICE
done
# Remove / disable services likely to conflict with dnsmasq
for SERVICE in systemd-resolved bind9; do
systemctl is-enabled $SERVICE &>/dev/null && systemctl disable $SERVICE 2>/dev/null
systemctl is-active $SERVICE &>/dev/null && systemctl stop $SERVICE
done
systemctl restart dnsmasq
systemctl restart dnsmasq
}
do_$1_regen ${@:2}

View file

@ -3,23 +3,23 @@
set -e
do_init_regen() {
do_pre_regen ""
systemctl restart unscd
do_pre_regen ""
systemctl restart unscd
}
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/nsswitch
cd /usr/share/yunohost/templates/nsswitch
install -D -m 644 nsswitch.conf "${pending_dir}/etc/nsswitch.conf"
install -D -m 644 nsswitch.conf "${pending_dir}/etc/nsswitch.conf"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| systemctl restart unscd
[[ -z "$regen_conf_files" ]] \
|| systemctl restart unscd
}
do_$1_regen ${@:2}

View file

@ -5,26 +5,26 @@ set -e
. /usr/share/yunohost/helpers
do_pre_regen() {
pending_dir=$1
pending_dir=$1
cd /usr/share/yunohost/templates/fail2ban
cd /usr/share/yunohost/templates/fail2ban
fail2ban_dir="${pending_dir}/etc/fail2ban"
mkdir -p "${fail2ban_dir}/filter.d"
mkdir -p "${fail2ban_dir}/jail.d"
fail2ban_dir="${pending_dir}/etc/fail2ban"
mkdir -p "${fail2ban_dir}/filter.d"
mkdir -p "${fail2ban_dir}/jail.d"
cp yunohost.conf "${fail2ban_dir}/filter.d/yunohost.conf"
cp jail.conf "${fail2ban_dir}/jail.conf"
cp yunohost.conf "${fail2ban_dir}/filter.d/yunohost.conf"
cp jail.conf "${fail2ban_dir}/jail.conf"
export ssh_port="$(yunohost settings get 'security.ssh.port')"
ynh_render_template "yunohost-jails.conf" "${fail2ban_dir}/jail.d/yunohost-jails.conf"
export ssh_port="$(yunohost settings get 'security.ssh.port')"
ynh_render_template "yunohost-jails.conf" "${fail2ban_dir}/jail.d/yunohost-jails.conf"
}
do_post_regen() {
regen_conf_files=$1
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| systemctl reload fail2ban
[[ -z "$regen_conf_files" ]] \
|| systemctl reload fail2ban
}
do_$1_regen ${@:2}

View file

@ -8,13 +8,16 @@ from publicsuffix import PublicSuffixList
from moulinette.utils.process import check_output
from yunohost.utils.dns import dig, YNH_DYNDNS_DOMAINS
from yunohost.utils.dns import (
dig,
YNH_DYNDNS_DOMAINS,
is_yunohost_dyndns_domain,
is_special_use_tld,
)
from yunohost.diagnosis import Diagnoser
from yunohost.domain import domain_list, _get_maindomain
from yunohost.dns import _build_dns_conf, _get_dns_zone_for_domain
SPECIAL_USE_TLDS = ["local", "localhost", "onion", "test"]
class DNSRecordsDiagnoser(Diagnoser):
@ -26,23 +29,20 @@ class DNSRecordsDiagnoser(Diagnoser):
main_domain = _get_maindomain()
all_domains = domain_list(exclude_subdomains=True)["domains"]
for domain in all_domains:
major_domains = domain_list(exclude_subdomains=True)["domains"]
for domain in major_domains:
self.logger_debug("Diagnosing DNS conf for %s" % domain)
is_specialusedomain = any(
domain.endswith("." + tld) for tld in SPECIAL_USE_TLDS
)
for report in self.check_domain(
domain,
domain == main_domain,
is_specialusedomain=is_specialusedomain,
):
yield report
# Check if a domain buy by the user will expire soon
psl = PublicSuffixList()
domains_from_registrar = [
psl.get_public_suffix(domain) for domain in all_domains
psl.get_public_suffix(domain) for domain in major_domains
]
domains_from_registrar = [
domain for domain in domains_from_registrar if "." in domain
@ -53,7 +53,16 @@ class DNSRecordsDiagnoser(Diagnoser):
for report in self.check_expiration_date(domains_from_registrar):
yield report
def check_domain(self, domain, is_main_domain, is_specialusedomain):
def check_domain(self, domain, is_main_domain):
if is_special_use_tld(domain):
categories = []
yield dict(
meta={"domain": domain},
data={},
status="INFO",
summary="diagnosis_dns_specialusedomain",
)
base_dns_zone = _get_dns_zone_for_domain(domain)
basename = domain.replace(base_dns_zone, "").rstrip(".") or "@"
@ -64,15 +73,6 @@ class DNSRecordsDiagnoser(Diagnoser):
categories = ["basic", "mail", "xmpp", "extra"]
if is_specialusedomain:
categories = []
yield dict(
meta={"domain": domain},
data={},
status="INFO",
summary="diagnosis_dns_specialusedomain",
)
for category in categories:
records = expected_configuration[category]
@ -84,7 +84,8 @@ class DNSRecordsDiagnoser(Diagnoser):
id_ = r["type"] + ":" + r["name"]
fqdn = r["name"] + "." + base_dns_zone if r["name"] != "@" else domain
# Ugly hack to not check mail records for subdomains stuff, otherwise will end up in a shitstorm of errors for people with many subdomains...
# Ugly hack to not check mail records for subdomains stuff,
# otherwise will end up in a shitstorm of errors for people with many subdomains...
# Should find a cleaner solution in the suggested conf...
if r["type"] in ["MX", "TXT"] and fqdn not in [
domain,
@ -131,6 +132,12 @@ class DNSRecordsDiagnoser(Diagnoser):
status = "SUCCESS"
summary = "diagnosis_dns_good_conf"
# If status is okay and there's actually no expected records
# (e.g. XMPP disabled)
# then let's not yield any diagnosis line
if not records and "status" == "SUCCESS":
continue
output = dict(
meta={"domain": domain, "category": category},
data=results,
@ -140,10 +147,7 @@ class DNSRecordsDiagnoser(Diagnoser):
if discrepancies:
# For ynh-managed domains (nohost.me etc...), tell people to try to "yunohost dyndns update --force"
if any(
domain.endswith(ynh_dyndns_domain)
for ynh_dyndns_domain in YNH_DYNDNS_DOMAINS
):
if is_yunohost_dyndns_domain(domain):
output["details"] = ["diagnosis_dns_try_dyndns_update_force"]
# Otherwise point to the documentation
else:

View file

@ -8,6 +8,7 @@ from moulinette.utils.filesystem import read_file
from yunohost.diagnosis import Diagnoser
from yunohost.domain import domain_list
from yunohost.utils.dns import is_special_use_tld
DIAGNOSIS_SERVER = "diagnosis.yunohost.org"
@ -34,11 +35,11 @@ class WebDiagnoser(Diagnoser):
summary="diagnosis_http_nginx_conf_not_up_to_date",
details=["diagnosis_http_nginx_conf_not_up_to_date_details"],
)
elif domain.endswith(".local"):
elif is_special_use_tld(domain):
yield dict(
meta={"domain": domain},
status="INFO",
summary="diagnosis_http_localdomain",
summary="diagnosis_http_special_use_tld",
)
else:
domains_to_check.append(domain)

View file

@ -76,7 +76,7 @@ class AppDiagnoser(Diagnoser):
for deprecated_helper in deprecated_helpers:
if (
os.system(
f"grep -hr '{deprecated_helper}' {app['setting_path']}/scripts/ | grep -v -q '^\s*#'"
f"grep -hr '{deprecated_helper}' {app['setting_path']}/scripts/ | grep -v -q '^\\s*#'"
)
== 0
):

View file

@ -1,7 +1,7 @@
#!/bin/bash
user=$1
readonly MEDIA_GROUP=multimedia
readonly MEDIA_DIRECTORY=/home/yunohost.multimedia

View file

@ -14,11 +14,11 @@ die() {
# Restore saved configuration and database
[[ $state -ge 1 ]] \
&& (rm -rf /etc/ldap/slapd.d &&
mv "${TMPDIR}/slapd.d" /etc/ldap/slapd.d)
&& (rm -rf /etc/ldap/slapd.d \
&& mv "${TMPDIR}/slapd.d" /etc/ldap/slapd.d)
[[ $state -ge 2 ]] \
&& (rm -rf /var/lib/ldap &&
mv "${TMPDIR}/ldap" /var/lib/ldap)
&& (rm -rf /var/lib/ldap \
&& mv "${TMPDIR}/ldap" /var/lib/ldap)
chown -R openldap: /etc/ldap/slapd.d /var/lib/ldap
systemctl start slapd
@ -38,7 +38,7 @@ cp -a "${backup_dir}/ldap.conf" /etc/ldap/ldap.conf
|| cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf
slapadd -F /etc/ldap/slapd.d -b cn=config \
-l "${backup_dir}/cn=config.master.ldif" \
|| die 1 "Unable to restore LDAP configuration"
|| die 1 "Unable to restore LDAP configuration"
chown -R openldap: /etc/ldap/slapd.d
# Restore the database
@ -46,7 +46,7 @@ mv /var/lib/ldap "$TMPDIR"
mkdir -p /var/lib/ldap
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \
-l "${backup_dir}/dc=yunohost-dc=org.ldif" \
|| die 2 "Unable to restore LDAP database"
|| die 2 "Unable to restore LDAP database"
chown -R openldap: /var/lib/ldap
systemctl start slapd

View file

@ -5,8 +5,7 @@ ynh_abort_if_errors
YNH_CWD="${YNH_BACKUP_DIR%/}/conf/manually_modified_files"
cd "$YNH_CWD"
for file in $(cat ./manually_modified_files_list)
do
for file in $(cat ./manually_modified_files_list); do
ynh_restore_file --origin_path="$file" --not_mandatory
done

View file

@ -78,6 +78,20 @@ service quota-warning {
}
}
service stats {
unix_listener stats-reader {
user = vmail
group = mail
mode = 0660
}
unix_listener stats-writer {
user = vmail
group = mail
mode = 0660
}
}
plugin {
sieve = /var/mail/sievescript/%n/.dovecot.sieve
sieve_dir = /var/mail/sievescript/%n/scripts/

View file

@ -6,6 +6,7 @@ After=network.target
User=mdns
Group=mdns
Type=simple
Environment=PYTHONUNBUFFERED=1
ExecStart=/usr/bin/yunomdns
StandardOutput=syslog

45
debian/changelog vendored
View file

@ -1,3 +1,48 @@
yunohost (4.3.1.3) testing; urgency=low
- [fix] app: repo url branch names may contain dots (38cff4a9)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Oct 2021 18:31:09 +0200
yunohost (4.3.1.2) testing; urgency=low
- [fix] apps: upgrade was broken because of typo ([#1350](https://github.com/YunoHost/yunohost/pull/1350))
- [enh] apps: in app_info, return a new is_webapp info meant to be used by API/webadmin (4cd5e9b6)
- [fix] configpanel: handle case where file question didnt get modified from webadmin, in which case self.value contains a path (54d901ad)
- [fix] configpanel: bind_key -> bind_key_ to prevent yunohost from redacting key names which leads to broken log metadata.yml somehow (941cc294)
- [enh] questions: Add visible attribute support in cli (74256845)
- [enh] helpers: Simplify apt/php dependencies helpers ([#1018](https://github.com/YunoHost/yunohost/pull/1018))
- [enh] helpers: In logrotate helper, enforce decent permissions on log file if app user exists ([#1352](https://github.com/YunoHost/yunohost/pull/1352))
Thanks to all contributors <3 ! (Éric Gaspar, Kay0u, ljf)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 07 Oct 2021 10:42:06 +0200
yunohost (4.3.1.1) testing; urgency=low
- [enh] app helpers: Update n version ([#1347](https://github.com/YunoHost/yunohost/pull/1347))
- [enh] Misc app.py refactoring + Prevent change_url from being used to move a fulldomain app to a subpath ([#1346](https://github.com/YunoHost/yunohost/pull/1346))
- [i18n] Translations updated for French, Galician, Portuguese, Ukrainian
Thanks to all contributors <3 ! (Éric Gaspar, José M, mifegui, ppr, Tymofii-Lytvynenko)
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 04 Oct 2021 01:33:22 +0200
yunohost (4.3.1) testing; urgency=low
- [fix] diagnosis: new app diagnosis grep reporing comments as issues ([#1333](https://github.com/YunoHost/yunohost/pull/1333))
- [enh] configpanel: Bind function for hotspot (79126809)
- [enh] cli: Rework/improve prompt mecanic ([#1338](https://github.com/YunoHost/yunohost/pull/1338))
- [fix] dyndns update broke because of buggy dns record names (da1b9089)
- [enh] dns: general improvement for special-use TLD / ynh dyndns domains (17aafe6f)
- [fix] yunomdns: various fixes/improvements ([#1335](https://github.com/YunoHost/yunohost/pull/1335))
- [fix] certs: Adapt ready_for_ACME check to the new dnsrecord result format... (d75c1a61)
- [i18n] Translations updated for French
Thanks to all contributors <3 ! (Éric Gaspar, Félix Piédallu, Kayou, ljf, tituspijean)
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 29 Sep 2021 22:22:42 +0200
yunohost (4.3.0) testing; urgency=low
- [users] Import/export users from/to CSV ([#1089](https://github.com/YunoHost/yunohost/pull/1089))

2
debian/control vendored
View file

@ -10,7 +10,7 @@ Package: yunohost
Essential: yes
Architecture: all
Depends: ${python3:Depends}, ${misc:Depends}
, moulinette (>= 4.2), ssowat (>= 4.0)
, moulinette (>= 4.3), ssowat (>= 4.3)
, python3-psutil, python3-requests, python3-dnspython, python3-openssl
, python3-miniupnpc, python3-dbus, python3-jinja2
, python3-toml, python3-packaging, python3-publicsuffix,

59
debian/postinst vendored
View file

@ -3,36 +3,35 @@
set -e
do_configure() {
rm -rf /var/cache/moulinette/*
rm -rf /var/cache/moulinette/*
mkdir -p /usr/share/moulinette/actionsmap/
ln -sf /usr/share/yunohost/actionsmap/yunohost.yml /usr/share/moulinette/actionsmap/yunohost.yml
mkdir -p /usr/share/moulinette/actionsmap/
ln -sf /usr/share/yunohost/actionsmap/yunohost.yml /usr/share/moulinette/actionsmap/yunohost.yml
if [ ! -f /etc/yunohost/installed ]; then
# If apps/ is not empty, we're probably already installed in the past and
# something funky happened ...
if [ -d /etc/yunohost/apps/ ] && ls /etc/yunohost/apps/* >/dev/null 2>&1
then
echo "Sounds like /etc/yunohost/installed mysteriously disappeared ... You should probably contact the Yunohost support ..."
else
bash /usr/share/yunohost/hooks/conf_regen/01-yunohost init
bash /usr/share/yunohost/hooks/conf_regen/02-ssl init
bash /usr/share/yunohost/hooks/conf_regen/09-nslcd init
bash /usr/share/yunohost/hooks/conf_regen/46-nsswitch init
bash /usr/share/yunohost/hooks/conf_regen/06-slapd init
bash /usr/share/yunohost/hooks/conf_regen/15-nginx init
bash /usr/share/yunohost/hooks/conf_regen/37-mdns init
fi
else
echo "Regenerating configuration, this might take a while..."
yunohost tools regen-conf --output-as none
if [ ! -f /etc/yunohost/installed ]; then
# If apps/ is not empty, we're probably already installed in the past and
# something funky happened ...
if [ -d /etc/yunohost/apps/ ] && ls /etc/yunohost/apps/* >/dev/null 2>&1; then
echo "Sounds like /etc/yunohost/installed mysteriously disappeared ... You should probably contact the Yunohost support ..."
else
bash /usr/share/yunohost/hooks/conf_regen/01-yunohost init
bash /usr/share/yunohost/hooks/conf_regen/02-ssl init
bash /usr/share/yunohost/hooks/conf_regen/09-nslcd init
bash /usr/share/yunohost/hooks/conf_regen/46-nsswitch init
bash /usr/share/yunohost/hooks/conf_regen/06-slapd init
bash /usr/share/yunohost/hooks/conf_regen/15-nginx init
bash /usr/share/yunohost/hooks/conf_regen/37-mdns init
fi
else
echo "Regenerating configuration, this might take a while..."
yunohost tools regen-conf --output-as none
echo "Launching migrations..."
yunohost tools migrations run --auto
echo "Launching migrations..."
yunohost tools migrations run --auto
echo "Re-diagnosing server health..."
yunohost diagnosis run --force
fi
echo "Re-diagnosing server health..."
yunohost diagnosis run --force
fi
}
@ -50,13 +49,13 @@ do_configure() {
case "$1" in
configure)
do_configure
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
;;
abort-upgrade | abort-remove | abort-deconfigure) ;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
;;
esac
#DEBHELPER#

6
debian/postrm vendored
View file

@ -6,12 +6,12 @@
set -e
if [ "$1" = "purge" ]; then
update-rc.d yunohost-firewall remove >/dev/null
rm -f /etc/yunohost/installed
update-rc.d yunohost-firewall remove >/dev/null
rm -f /etc/yunohost/installed
fi
if [ "$1" = "remove" ]; then
rm -f /etc/yunohost/installed
rm -f /etc/yunohost/installed
fi
# Reset dpkg vendor to debian

View file

@ -13,10 +13,8 @@
"app_already_installed": "{app} is already installed",
"app_already_installed_cant_change_url": "This app is already installed. The URL cannot be changed just by this function. Check in `app changeurl` if it's available.",
"app_already_up_to_date": "{app} is already up-to-date",
"app_argument_choice_invalid": "Use one of these choices '{choices}' for the argument '{name}' instead of '{value}'",
"app_argument_choice_invalid": "Pick a valid value for argument '{name}': '{value}' is not among the available choices ({choices})",
"app_argument_invalid": "Pick a valid value for the argument '{name}': {error}",
"app_argument_password_help_keep": "Press Enter to keep the current value",
"app_argument_password_help_optional": "Type one space to empty the password",
"app_argument_password_no_default": "Error while parsing password argument '{name}': password argument can't have a default value for security reason",
"app_argument_required": "Argument '{name}' is required",
"app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain}{path}'), nothing to do.",
@ -38,7 +36,6 @@
"app_manifest_install_ask_is_public": "Should this app be exposed to anonymous visitors?",
"app_manifest_install_ask_password": "Choose an administration password for this app",
"app_manifest_install_ask_path": "Choose the URL path (after the domain) where this app should be installed",
"app_manifest_invalid": "Something is wrong with the app manifest: {error}",
"app_not_correctly_installed": "{app} seems to be incorrectly installed",
"app_not_installed": "Could not find {app} in the list of installed apps: {all_apps}",
"app_not_properly_removed": "{app} has not been properly removed",
@ -194,7 +191,7 @@
"diagnosis_dns_good_conf": "DNS records are correctly configured for domain {domain} (category {category})",
"diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.<br>Type: <code>{type}</code><br>Name: <code>{name}</code><br>Value: <code>{value}</code>",
"diagnosis_dns_point_to_doc": "Please check the documentation at <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> if you need help about configuring DNS records.",
"diagnosis_dns_specialusedomain": "Domain {domain} is based on a special-use top-level domain (TLD) and is therefore not expected to have actual DNS records.",
"diagnosis_dns_specialusedomain": "Domain {domain} is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.",
"diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by YunoHost. If that's not the case, you can try to force an update using <cmd>yunohost dyndns update --force</cmd>.",
"diagnosis_domain_expiration_error": "Some domains will expire VERY SOON!",
"diagnosis_domain_expiration_not_found": "Unable to check the expiration date for some domains",
@ -203,7 +200,7 @@
"diagnosis_domain_expiration_warning": "Some domains will expire soon!",
"diagnosis_domain_expires_in": "{domain} expires in {days} days.",
"diagnosis_domain_not_found_details": "The domain {domain} doesn't exist in WHOIS database or is expired!",
"diagnosis_everything_ok": "Everything looks good for {category}!",
"diagnosis_everything_ok": "Everything looks OK for {category}!",
"diagnosis_failed": "Failed to fetch diagnosis result for category '{category}': {error}",
"diagnosis_failed_for_category": "Diagnosis failed for category '{category}': {error}",
"diagnosis_found_errors": "Found {errors} significant issue(s) related to {category}!",
@ -216,7 +213,7 @@
"diagnosis_http_could_not_diagnose_details": "Error: {error}",
"diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.",
"diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?) when using the domain name or global IP. You may be able to improve the situation by having a look at <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>",
"diagnosis_http_localdomain": "Domain {domain}, with a .local TLD, is not expected to be exposed outside the local network.",
"diagnosis_http_special_use_tld": "Domain {domain} is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to be exposed outside the local network.",
"diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.",
"diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> and if you're ok, apply the changes with <cmd>yunohost tools regen-conf nginx --force</cmd>.",
"diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.",
@ -310,6 +307,7 @@
"domain_deleted": "Domain deleted",
"domain_deletion_failed": "Unable to delete domain {domain}: {error}",
"domain_dns_conf_is_just_a_recommendation": "This command shows you the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.",
"domain_dns_conf_special_use_tld": "This domain is based on a special-use top-level domain (TLD) such as .local or .test and is therefore not expected to have actual DNS records.",
"domain_dyndns_already_subscribed": "You have already subscribed to a DynDNS domain",
"domain_dyndns_root_unknown": "Unknown DynDNS root domain",
"domain_exists": "The domain already exists",
@ -366,7 +364,6 @@
"extracting": "Extracting...",
"field_invalid": "Invalid field '{}'",
"file_does_not_exist": "The file {path} does not exist.",
"file_extension_not_accepted": "Refusing file '{path}' because its extension is not among the accepted extensions: {accept}",
"firewall_reload_failed": "Could not reload the firewall",
"firewall_reloaded": "Firewall reloaded",
"firewall_rules_cmd_failed": "Some firewall rule commands have failed. More info in log.",
@ -555,6 +552,7 @@
"migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations run`.",
"not_enough_disk_space": "Not enough free space on '{path}'",
"operation_interrupted": "The operation was manually interrupted?",
"other_available_options": "... and {n} other available options not shown",
"packages_upgrade_failed": "Could not upgrade all the packages",
"password_listed": "This password is among the most used passwords in the world. Please choose something more unique.",
"password_too_simple_1": "The password needs to be at least 8 characters long",
@ -670,7 +668,7 @@
"service_stop_failed": "Unable to stop the service '{service}'\n\nRecent service logs:{logs}",
"service_stopped": "Service '{service}' stopped",
"service_unknown": "Unknown service '{service}'",
"show_tile_cant_be_enabled_for_regex": "You cannot enable 'show_tile' right no, because the URL for the permission '{permission}' is a regex",
"show_tile_cant_be_enabled_for_regex": "You cannot enable 'show_tile' right now, because the URL for the permission '{permission}' is a regex",
"show_tile_cant_be_enabled_for_url_not_defined": "You cannot enable 'show_tile' right now, because you must first define an URL for the permission '{permission}'",
"ssowat_conf_generated": "SSOwat configuration regenerated",
"ssowat_conf_updated": "SSOwat configuration updated",

View file

@ -4,7 +4,7 @@
"admin_password_change_failed": "Impossible de changer le mot de passe",
"admin_password_changed": "Le mot de passe d'administration a été modifié",
"app_already_installed": "{app} est déjà installé",
"app_argument_choice_invalid": "Choix invalide pour le paramètre '{name}'. Les valeurs acceptées sont {choices}, au lieu de '{value}'",
"app_argument_choice_invalid": "Choisissez une valeur valide pour l'argument '{name}' : '{value}' ne fait pas partie des choix disponibles ({choices})",
"app_argument_invalid": "Valeur invalide pour le paramètre '{name}' : {error}",
"app_argument_required": "Le paramètre '{name}' est requis",
"app_extraction_failed": "Impossible d'extraire les fichiers d'installation",
@ -431,7 +431,7 @@
"diagnosis_cache_still_valid": "(Le cache est encore valide pour le diagnostic {category}. Il ne sera pas re-diagnostiqué pour le moment !)",
"diagnosis_ignored_issues": "(+ {nb_ignored} problème(s) ignoré(s))",
"diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.",
"diagnosis_everything_ok": "Tout semble bien pour {category} !",
"diagnosis_everything_ok": "Tout semble OK pour {category} !",
"diagnosis_failed": "Échec de la récupération du résultat du diagnostic pour la catégorie '{category}' : {error}",
"diagnosis_ip_connected_ipv4": "Le serveur est connecté à Internet en IPv4 !",
"diagnosis_ip_no_ipv4": "Le serveur ne dispose pas d'une adresse IPv4.",
@ -593,7 +593,7 @@
"diagnosis_package_installed_from_sury": "Des paquets du système devraient être rétrogradé de version",
"additional_urls_already_added": "URL supplémentaire '{url}' déjà ajoutée pour la permission '{permission}'",
"unknown_main_domain_path": "Domaine ou chemin inconnu pour '{app}'. Vous devez spécifier un domaine et un chemin pour pouvoir spécifier une URL pour l'autorisation.",
"show_tile_cant_be_enabled_for_regex": "Vous ne pouvez pas activer 'show_tile' pour le moment, car l'URL de l'autorisation '{permission}' est une expression régulière",
"show_tile_cant_be_enabled_for_regex": "Vous ne pouvez pas activer 'show_tile' pour le moment, cela car l'URL de l'autorisation '{permission}' est une expression régulière",
"show_tile_cant_be_enabled_for_url_not_defined": "Vous ne pouvez pas activer 'show_tile' pour le moment, car vous devez d'abord définir une URL pour l'autorisation '{permission}'",
"regex_with_only_domain": "Vous ne pouvez pas utiliser une expression régulière pour le domaine, uniquement pour le chemin",
"regex_incompatible_with_tile": "/!\\ Packagers ! La permission '{permission}' a 'show_tile' définie sur 'true' et vous ne pouvez donc pas définir une URL regex comme URL principale",
@ -632,7 +632,7 @@
"global_settings_setting_security_webadmin_allowlist": "Adresses IP autorisées à accéder à la webadmin. Elles doivent être séparées par une virgule.",
"global_settings_setting_security_webadmin_allowlist_enabled": "Autoriser seulement certaines IP à accéder à la webadmin.",
"diagnosis_http_localdomain": "Le domaine {domain}, avec un TLD .local, ne devrait pas être exposé en dehors du réseau local.",
"diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial et ne devrait donc pas avoir d'enregistrements DNS réels.",
"diagnosis_dns_specialusedomain": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial comme .local ou .test et ne devrait donc pas avoir d'enregistrements DNS réels.",
"invalid_password": "Mot de passe incorrect",
"ldap_server_is_down_restart_it": "Le service LDAP est en panne, essayez de le redémarrer...",
"ldap_server_down": "Impossible d'atteindre le serveur LDAP",
@ -675,5 +675,39 @@
"log_app_config_set": "Appliquer la configuration à l'application '{}'",
"service_not_reloading_because_conf_broken": "Le service '{name}' n'a pas été rechargé/redémarré car sa configuration est cassée : {errors}",
"app_argument_password_help_keep": "Tapez sur Entrée pour conserver la valeur actuelle",
"app_argument_password_help_optional": "Tapez un espace pour vider le mot de passe"
}
"app_argument_password_help_optional": "Tapez un espace pour vider le mot de passe",
"domain_registrar_is_not_configured": "Le registrar n'est pas encore configuré pour le domaine {domain}.",
"domain_dns_push_not_applicable": "La fonction de configuration DNS automatique n'est pas applicable au domaine {domain}. Vous devez configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns_config.",
"domain_dns_registrar_yunohost": "Ce domaine est de type nohost.me / nohost.st / ynh.fr et sa configuration DNS est donc automatiquement gérée par YunoHost sans qu'il n'y ait d'autre configuration à faire. (voir la commande 'yunohost dyndns update')",
"domain_dns_registrar_supported": "YunoHost a détecté automatiquement que ce domaine est géré par le registrar **{registrar}**. Si vous le souhaitez, YunoHost configurera automatiquement cette zone DNS, si vous lui fournissez les identifiants API appropriés. Vous pouvez trouver de la documentation sur la façon d'obtenir vos identifiants API sur cette page : https://yunohost.org/registar_api_{registrar}. (Vous pouvez également configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns )",
"domain_config_features_disclaimer": "Jusqu'à présent, l'activation/désactivation des fonctionnalités de messagerie ou XMPP n'a d'impact que sur la configuration DNS recommandée et automatique, et non sur les configurations système !",
"domain_dns_push_managed_in_parent_domain": "La fonctionnalité de configuration DNS automatique est gérée dans le domaine parent {parent_domain}.",
"domain_dns_registrar_managed_in_parent_domain": "Ce domaine est un sous-domaine de {parent_domain_link}. La configuration du registrar DNS doit être gérée dans le panneau de configuration de {parent_domain}.",
"domain_dns_registrar_not_supported": "YunoHost n'a pas pu détecter automatiquement le bureau d'enregistrement gérant ce domaine. Vous devez configurer manuellement vos enregistrements DNS en suivant la documentation sur https://yunohost.org/dns.",
"domain_dns_registrar_experimental": "Jusqu'à présent, l'interface avec l'API de **{registrar}** n'a pas été correctement testée et revue par la communauté YunoHost. L'assistance est **très expérimentale** - soyez prudent !",
"domain_dns_push_failed_to_authenticate": "Échec de l'authentification sur l'API du bureau d'enregistrement pour le domaine « {domain} ». Très probablement les informations d'identification sont incorrectes? (Erreur: {error})",
"domain_dns_push_failed_to_list": "Échec de la liste des enregistrements actuels à l'aide de l'API du registraire : {error}",
"domain_dns_push_already_up_to_date": "Dossiers déjà à jour.",
"domain_dns_pushing": "Transmission des enregistrements DNS...",
"domain_dns_push_record_failed": "Échec de l'enregistrement {action} {type}/{name} : {error}",
"domain_dns_push_success": "Enregistrements DNS mis à jour !",
"domain_dns_push_failed": "La mise à jour des enregistrements DNS a échoué.",
"domain_dns_push_partial_failure": "Enregistrements DNS partiellement mis à jour : certains avertissements/erreurs ont été signalés.",
"domain_config_mail_in": "Emails entrants",
"domain_config_mail_out": "Emails sortants",
"domain_config_xmpp": "Messagerie instantanée (XMPP)",
"domain_config_auth_token": "Jeton d'authentification",
"domain_config_auth_key": "Clé d'authentification",
"domain_config_auth_secret": "Secret d'authentification",
"domain_config_api_protocol": "Protocole API",
"domain_config_auth_entrypoint": "Point d'entrée API",
"domain_config_auth_application_key": "Clé d'application",
"domain_config_auth_application_secret": "Clé secrète de l'application",
"ldap_attribute_already_exists": "L'attribut LDAP '{attribute}' existe déjà avec la valeur '{value}'",
"log_domain_config_set": "Mettre à jour la configuration du domaine '{}'",
"log_domain_dns_push": "Pousser les enregistrements DNS pour le domaine '{}'",
"diagnosis_http_special_use_tld": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial tel que .local ou .test et n'est donc pas censé être exposé en dehors du réseau local.",
"domain_dns_conf_special_use_tld": "Ce domaine est basé sur un domaine de premier niveau (TLD) à usage spécial tel que .local ou .test et ne devrait donc pas avoir d'enregistrements DNS réels.",
"other_available_options": "... et {n} autres options disponibles non affichées",
"domain_config_auth_consumer_key": "Consumer key"
}

View file

@ -17,7 +17,7 @@
"app_argument_required": "Requírese o argumento '{name}'",
"app_argument_password_no_default": "Erro ao procesar o argumento do contrasinal '{name}': o argumento do contrasinal non pode ter un valor por defecto por razón de seguridade",
"app_argument_invalid": "Elixe un valor válido para o argumento '{name}': {error}",
"app_argument_choice_invalid": "Usa unha destas opcións '{choices}' para o argumento '{name}' no lugar de '{value}'",
"app_argument_choice_invalid": "Elixe un valor válido para o argumento '{name}': '{value}' non está entre as opcións dispoñibles ({choices})",
"backup_archive_writing_error": "Non se puideron engadir os ficheiros '{source}' (chamados no arquivo '{dest}' para ser copiados dentro do arquivo comprimido '{archive}'",
"backup_archive_system_part_not_available": "A parte do sistema '{part}' non está dispoñible nesta copia",
"backup_archive_corrupted": "Semella que o arquivo de copia '{archive}' está estragado : {error}",
@ -102,7 +102,7 @@
"backup_copying_to_organize_the_archive": "Copiando {size}MB para organizar o arquivo",
"backup_cleaning_failed": "Non se puido baleirar o cartafol temporal para a copia",
"backup_cant_mount_uncompress_archive": "Non se puido montar o arquivo sen comprimir porque está protexido contra escritura",
"backup_ask_for_copying_if_needed": "Queres realizar a copia de apoio utilizando temporalmente {size}MB? (Faise deste xeito porque algúns ficheiros non hai xeito de preparalos usando unha forma máis eficiente).",
"backup_ask_for_copying_if_needed": "Queres realizar a copia de apoio utilizando temporalmente {size}MB? (Faise deste xeito porque algúns ficheiros non hai xeito de preparalos usando unha forma máis eficiente.)",
"backup_running_hooks": "Executando os ganchos da copia...",
"backup_permission": "Permiso de copia para {app}",
"backup_output_symlink_dir_broken": "O directorio de arquivo '{path}' é unha ligazón simbólica rota. Pode ser que esqueceses re/montar ou conectar o medio de almacenaxe ao que apunta.",
@ -455,7 +455,7 @@
"migration_0015_modified_files": "Ten en conta que os seguintes ficheiros semella que foron modificados manualmente e poderían ser sobrescritos na actualización: {manually_modified_files}",
"migration_0015_problematic_apps_warning": "Ten en conta que se detectaron as seguintes apps que poderían ser problemáticas. Semella que non foron instaladas usando o catálogo de YunoHost, ou non están marcadas como 'funcionais'. En consecuencia, non se pode garantir que seguirán funcionando após a actualización: {problematic_apps}",
"diagnosis_http_localdomain": "O dominio {domain}, cun TLD .local, non é de agardar que esté exposto ao exterior da rede local.",
"diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) polo que non é de agardar que realmente teña rexistros DNS.",
"diagnosis_dns_specialusedomain": "O dominio {domain} baséase un dominio de nivel alto e uso especial (TLD) como .local ou .test polo que non é de agardar que realmente teña rexistros DNS.",
"upnp_enabled": "UPnP activado",
"upnp_disabled": "UPnP desactivado",
"permission_creation_failed": "Non se creou o permiso '{permission}': {error}",
@ -675,5 +675,39 @@
"config_version_not_supported": "A versión do panel de configuración '{version}' non está soportada.",
"file_extension_not_accepted": "Rexeitouse o ficheiro '{path}' porque a súa extensión non está entre as aceptadas: {accept}",
"invalid_number_max": "Ten que ser menor de {max}",
"service_not_reloading_because_conf_broken": "Non se recargou/reiniciou o servizo '{name}' porque a súa configuración está estragada: {errors}"
}
"service_not_reloading_because_conf_broken": "Non se recargou/reiniciou o servizo '{name}' porque a súa configuración está estragada: {errors}",
"diagnosis_http_special_use_tld": "O dominio {domain} baséase nun dominio de alto-nivel (TLD) especial como .local ou .test e por isto non é de agardar que esté exposto fóra da rede local.",
"domain_dns_conf_special_use_tld": "Este dominio baséase nun dominio de alto-nivel (TLD) de uso especial como .local ou .test e por isto non é de agardar que teña rexistros DNS asociados.",
"domain_dns_registrar_managed_in_parent_domain": "Este dominio é un subdominio de {parent_domain_link}. A configuración DNS debe xestionarse no panel de configuración de {parent_domain}'s.",
"domain_dns_registrar_not_supported": "YunoHost non é quen de detectar a rexistradora que xestiona o dominio. Debes configurar manualmente os seus rexistros DNS seguindo a documentación en https://yunohost.org/dns.",
"domain_dns_registrar_experimental": "Ata o momento, a interface coa API de **{registrar}** aínda non foi comprobada e revisada pola comunidade YunoHost. O soporte é **moi experimental** - ten coidado!",
"domain_dns_push_failed_to_list": "Non se pode mostrar a lista actual de rexistros na API da rexistradora: {error}",
"domain_dns_push_already_up_to_date": "Rexistros ao día, nada que facer.",
"domain_dns_pushing": "Enviando rexistros DNS...",
"domain_dns_push_record_failed": "Fallou {action} do rexistro {type}/{name}: {error}",
"domain_dns_push_success": "Rexistros DNS actualizados!",
"domain_dns_push_failed": "Fallou completamente a actualización dos rexistros DNS.",
"domain_config_features_disclaimer": "Ata o momento, activar/desactivar as funcións de email ou XMPP só ten impacto na configuración automática da configuración DNS, non na configuración do sistema!",
"domain_config_mail_in": "Emails entrantes",
"domain_config_mail_out": "Emails saíntes",
"domain_config_xmpp": "Mensaxería instantánea (XMPP)",
"domain_config_auth_secret": "Segreda de autenticación",
"domain_config_api_protocol": "Protocolo API",
"domain_config_auth_application_key": "Chave da aplicación",
"domain_config_auth_application_secret": "Chave segreda da aplicación",
"domain_config_auth_consumer_key": "Chave consumidora",
"log_domain_dns_push": "Enviar rexistros DNS para o dominio '{}'",
"other_available_options": "... e outras {n} opcións dispoñibles non mostradas",
"domain_dns_registrar_yunohost": "Este dominio un dos de nohost.me / nohost.st / ynh.fr e a configuración DNS xestionaa directamente YunoHost se máis requisitos. (mira o comando 'yunohost dyndns update')",
"domain_dns_registrar_supported": "YunoHost detectou automáticamente que este dominio está xestionado pola rexistradora **{registrar}**. Se queres, YunoHost pode configurar automáticamente as súas zonas DNS, se proporcionas as credenciais de acceso á API. Podes ver a documentación sobre como obter as credenciais da API nesta páxina: https://yunohost.org/registrar_api_{registrar}. (Tamén podes configurar manualmente os rexistros DNS seguindo a documentación en https://yunohost.org/dns )",
"domain_dns_push_partial_failure": "Actualización parcial dos rexistros DNS: informouse dalgúns avisos/erros.",
"domain_config_auth_token": "Token de autenticación",
"domain_config_auth_key": "Chave de autenticación",
"domain_config_auth_entrypoint": "Punto de entrada da API",
"domain_dns_push_failed_to_authenticate": "Fallou a autenticación na API da rexistradora do dominio '{domain}'. Comprobaches que sexan as credenciais correctas? (Erro: {error})",
"domain_registrar_is_not_configured": "A rexistradora non aínda non está configurada para o dominio {domain}.",
"domain_dns_push_not_applicable": "A función de rexistro DNS automático non é aplicable ao dominio {domain}. Debes configurar manualmente os teus rexistros DNS seguindo a documentación de https://yunohost.org/dns_config.",
"domain_dns_push_managed_in_parent_domain": "A función de rexistro DNS automático está xestionada polo dominio nai {parent_domain}.",
"ldap_attribute_already_exists": "Xa existe o atributo LDAP '{attribute}' con valor '{value}'",
"log_domain_config_set": "Actualizar configuración para o dominio '{}'"
}

View file

@ -109,7 +109,7 @@
"backup_output_directory_forbidden": "Escolha um diretório de saída diferente. Backups não podem ser criados nos subdiretórios /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives",
"app_already_installed_cant_change_url": "Este aplicativo já está instalado. A URL não pode ser alterada apenas por esta função. Confira em `app changeurl` se está disponível.",
"app_already_up_to_date": "{app} já está atualizado",
"app_argument_choice_invalid": "Use uma das opções '{choices}' para o argumento '{name}' em vez de '{value}'",
"app_argument_choice_invalid": "Escolha um valor válido para o argumento '{name}' : '{value}' não está entre as opções disponíveis ({choices})",
"app_argument_invalid": "Escolha um valor válido para o argumento '{name}': {error}",
"app_argument_required": "O argumento '{name}' é obrigatório",
"app_location_unavailable": "Esta url ou não está disponível ou está em conflito com outra(s) aplicação(ões) já instalada(s):\n{apps}",
@ -182,7 +182,7 @@
"backup_csv_creation_failed": "Não foi possível criar o arquivo CSV necessário para a restauração",
"backup_csv_addition_failed": "Não foi possível adicionar os arquivos que estarão no backup ao arquivo CSV",
"backup_create_size_estimation": "O arquivo irá conter cerca de {size} de dados.",
"backup_couldnt_bind": "Não foi possível vincular {src} ao {dest}",
"backup_couldnt_bind": "Não foi possível vincular {src} ao {dest}.",
"certmanager_attempt_to_replace_valid_cert": "Você está tentando sobrescrever um certificado bom e válido para o domínio {domain}! (Use --force para prosseguir mesmo assim)",
"backup_with_no_restore_script_for_app": "A aplicação {app} não tem um script de restauração, você não será capaz de automaticamente restaurar o backup dessa aplicação.",
"backup_with_no_backup_script_for_app": "A aplicação '{app}' não tem um script de backup. Ignorando.",
@ -191,5 +191,68 @@
"backup_running_hooks": "Executando os hooks de backup...",
"backup_permission": "Permissão de backup para {app}",
"backup_output_symlink_dir_broken": "O diretório de seu arquivo '{path}' é um link simbólico quebrado. Talvez você tenha esquecido de re/montar ou conectar o dispositivo de armazenamento para onde o link aponta.",
"backup_output_directory_required": "Você deve especificar um diretório de saída para o backup"
}
"backup_output_directory_required": "Você deve especificar um diretório de saída para o backup",
"diagnosis_description_apps": "Aplicações",
"diagnosis_apps_allgood": "Todos os apps instalados respeitam práticas básicas de empacotamento",
"diagnosis_apps_issue": "Um problema foi encontrado para o app {app}",
"diagnosis_apps_not_in_app_catalog": "Esta aplicação não está no catálogo de aplicações do YunoHost. Se estava no passado e foi removida, você deve considerar desinstalar este app já que ele não mais receberá atualizações e pode comprometer a integridade e segurança do seu sistema.",
"diagnosis_apps_broken": "Esta aplicação está atualmente marcada como quebrada no catálogo de apps do YunoHost. Isto pode ser um problema temporário enquanto os mantenedores consertam o problema. Enquanto isso, atualizar este app está desabilitado.",
"diagnosis_apps_bad_quality": "Esta aplicação está atualmente marcada como quebrada no catálogo de apps do YunoHost. Isto pode ser um problema temporário enquanto os mantenedores consertam o problema. Enquanto isso, atualizar este app está desabilitado.",
"diagnosis_apps_outdated_ynh_requirement": "A versão instalada deste app requer tão somente yunohost >= 2.x, o que tende a indicar que o app não está atualizado com as práticas de empacotamento recomendadas. Você deve considerar seriamente atualizá-lo.",
"diagnosis_apps_deprecated_practices": "A versão instalada deste app usa práticas de empacotamento extremamente velhas que não são mais usadas. Você deve considerar seriamente atualizá-lo.",
"certmanager_domain_http_not_working": "O domínio {domain} não parece estar acessível por HTTP. Por favor cheque a categoria 'Web' no diagnóstico para mais informações. (Se você sabe o que está fazendo, use '--no-checks' para desativar estas checagens.)",
"diagnosis_description_regenconf": "Configurações do sistema",
"diagnosis_description_services": "Cheque de status dos serviços",
"diagnosis_basesystem_hardware": "A arquitetura hardware do servidor é {virt} {arch}",
"diagnosis_description_web": "Web",
"diagnosis_basesystem_ynh_single_version": "Versão {package}: {version} ({repo})",
"diagnosis_basesystem_ynh_main_version": "O servidor está rodando YunoHost {main_version} ({repo})",
"app_config_unable_to_apply": "Falha ao aplicar valores do painel de configuração.",
"app_config_unable_to_read": "Falha ao ler valores do painel de configuração.",
"config_apply_failed": "Aplicar as novas configuração falhou: {error}",
"config_cant_set_value_on_section": "Você não pode setar um único valor na seção de configuração inteira.",
"config_validate_time": "Deve ser um horário válido como HH:MM",
"config_validate_url": "Deve ser uma URL válida",
"config_version_not_supported": "Versões do painel de configuração '{version}' não são suportadas.",
"danger": "Perigo:",
"diagnosis_basesystem_ynh_inconsistent_versions": "Você está executando versões inconsistentes dos pacotes YunoHost... provavelmente por causa de uma atualização parcial ou que falhou.",
"diagnosis_description_basesystem": "Sistema base",
"certmanager_cert_signing_failed": "Não foi possível assinar o novo certificado",
"certmanager_unable_to_parse_self_CA_name": "Não foi possível processar nome da autoridade de auto-assinatura (arquivo: {file})",
"confirm_app_install_warning": "Aviso: Pode ser que essa aplicação funcione, mas ela não está bem integrada ao YunoHost. Algumas funcionalidades como single sign-on e backup/restauração podem não estar disponíveis. Instalar mesmo assim? [{answers}] ",
"config_forbidden_keyword": "A palavra chave '{keyword}' é reservada, você não pode criar ou usar um painel de configuração com uma pergunta com esse id.",
"config_no_panel": "Painel de configuração não encontrado.",
"config_unknown_filter_key": "A chave de filtro '{filter_key}' está incorreta.",
"config_validate_color": "Deve ser uma cor RGB hexadecimal válida",
"config_validate_date": "Deve ser uma data válida como no formato AAAA-MM-DD",
"config_validate_email": "Deve ser um email válido",
"diagnosis_basesystem_kernel": "O servidor está rodando Linux kernel {kernel_version}",
"diagnosis_cache_still_valid": "(O cache para a categoria de diagnóstico {category} ainda é valido. Não será diagnosticada novamente ainda)",
"diagnosis_cant_run_because_of_dep": "Impossível fazer diagnóstico para {category} enquanto ainda existem problemas importantes relacionados a {dep}.",
"diagnosis_diskusage_low": "Unidade de armazenamento <code>{mountpoint}</code> (no dispositivo <code>{device}</code>_) tem somente {free} ({free_percent}%) de espaço restante (de {total}). Tenha cuidado.",
"diagnosis_description_ip": "Conectividade internet",
"diagnosis_description_dnsrecords": "Registros DNS",
"diagnosis_description_mail": "Email",
"certmanager_domain_not_diagnosed_yet": "Ainda não há resultado de diagnóstico para o domínio {domain}. Por favor re-execute um diagnóstico para as categorias 'Registros DNS' e 'Web' na seção de diagnósticos para checar se o domínio está pronto para o Let's Encrypt. (Ou, se você souber o que está fazendo, use '--no-checks' para desativar estas checagens.)",
"diagnosis_basesystem_host": "O Servidor está rodando Debian {debian_version}",
"diagnosis_description_systemresources": "Recursos do sistema",
"certmanager_acme_not_configured_for_domain": "O challenge ACME não pode ser realizado para {domain} porque o código correspondente na configuração do nginx está ausente... Por favor tenha certeza de que sua configuração do nginx está atualizada executando o comando `yunohost tools regen-conf nginx --dry-run --with-diff`.",
"certmanager_attempt_to_renew_nonLE_cert": "O certificado para o domínio '{domain}' não foi emitido pelo Let's Encrypt. Não é possível renová-lo automaticamente!",
"certmanager_attempt_to_renew_valid_cert": "O certificado para o domínio '{domain}' não esta prestes a expirar! (Você pode usar --force se saber o que está fazendo)",
"certmanager_cannot_read_cert": "Algo de errado aconteceu ao tentar abrir o atual certificado para o domínio {domain} (arquivo: {file}), motivo: {reason}",
"certmanager_cert_install_success": "Certificado Let's Encrypt foi instalado para o domínio '{domain}'",
"certmanager_cert_install_success_selfsigned": "Certificado autoassinado foi instalado para o domínio '{domain}'",
"certmanager_certificate_fetching_or_enabling_failed": "Tentativa de usar o novo certificado para o domínio {domain} não funcionou...",
"certmanager_domain_cert_not_selfsigned": "O certificado para o domínio {domain} não é autoassinado. Você tem certeza que quer substituí-lo? (Use '--force' para fazê-lo)",
"certmanager_domain_dns_ip_differs_from_public_ip": "O registro de DNS para o domínio '{domain}' é diferente do IP deste servidor. Por favor cheque a categoria 'Registros DNS' (básico) no diagnóstico para mais informações. Se você modificou recentemente o registro 'A', espere um tempo para ele se propagar (alguns serviços de checagem de propagação de DNS estão disponíveis online). (Se você sabe o que está fazendo, use '--no-checks' para desativar estas checagens.)",
"certmanager_hit_rate_limit": "Foram emitidos certificados demais para este conjunto de domínios {domain} recentemente. Por favor tente novamente mais tarde. Veja https://letsencrypt.org/docs/rate-limits/ para mais detalhes",
"certmanager_no_cert_file": "Não foi possível ler o arquivo de certificado para o domínio {domain} (arquivo: {file})",
"certmanager_self_ca_conf_file_not_found": "Não foi possível encontrar o arquivo de configuração para a autoridade de auto-assinatura (arquivo: {file})",
"confirm_app_install_danger": "ATENÇÃO! Sabe-se que esta aplicação ainda é experimental (isso se não que explicitamente não funciona)! Você provavelmente NÃO deve instalar ela a não ser que você saiba o que você está fazendo. NENHUM SUPORTE será fornecido se esta aplicação não funcionar ou quebrar o seu sistema... Se você está disposto a tomar esse rico de toda forma, digite '{answers}'",
"confirm_app_install_thirdparty": "ATENÇÃO! Essa aplicação não faz parte do catálogo do YunoHost. Instalar aplicações de terceiros pode comprometer a integridade e segurança do seu sistema. Você provavelmente NÃO deve instalá-la a não ser que você saiba o que você está fazendo. NENHUM SUPORTE será fornecido se este app não funcionar ou quebrar seu sistema... Se você está disposto a tomar este risco de toda forma, digite '{answers}'",
"diagnosis_description_ports": "Exposição de portas",
"diagnosis_basesystem_hardware_model": "O modelo do servidor é {model}",
"diagnosis_backports_in_sources_list": "Parece que o apt (o gerenciador de pacotes) está configurado para usar o repositório backport. A não ser que você saiba o que você esteá fazendo, desencorajamos fortemente a instalação de pacotes de backports porque é provável que crie instabilidades ou conflitos no seu sistema.",
"certmanager_cert_renew_success": "Certificado Let's Encrypt renovado para o domínio '{domain}'",
"certmanager_warning_subdomain_dns_record": "O subdomínio '{subdomain}' não resolve para o mesmo IP que '{domain}'. Algumas funcionalidades não estarão disponíveis até que você conserte isto e regenere o certificado."
}

View file

@ -16,7 +16,7 @@
"app_argument_required": "Аргумент '{name}' необхідний",
"app_argument_password_no_default": "Помилка під час розбору аргументу пароля '{name}': аргумент пароля не може мати типове значення з причин безпеки",
"app_argument_invalid": "Виберіть правильне значення для аргументу '{name}': {error}",
"app_argument_choice_invalid": "Використовуйте один з цих варіантів '{choices}' для аргументу '{name}' замість '{value}'",
"app_argument_choice_invalid": "Виберіть дійсне значення для аргументу '{name}': '{value}' не є серед доступних варіантів ({choices})",
"app_already_up_to_date": "{app} має найостаннішу версію",
"app_already_installed_cant_change_url": "Цей застосунок уже встановлено. URL-адреса не може бути змінена тільки цією функцією. Перевірте в `app changeurl`, якщо вона доступна.",
"app_already_installed": "{app} уже встановлено",
@ -482,7 +482,7 @@
"diagnosis_domain_expiration_not_found_details": "Відомості WHOIS для домену {domain} не містять даних про строк дії?",
"diagnosis_domain_not_found_details": "Домен {domain} не існує в базі даних WHOIS або строк його дії сплив!",
"diagnosis_domain_expiration_not_found": "Неможливо перевірити строк дії деяких доменів",
"diagnosis_dns_specialusedomain": "Домен {domain} заснований на домені верхнього рівня спеціального призначення (TLD) і тому не очікується, що у нього будуть актуальні записи DNS.",
"diagnosis_dns_specialusedomain": "Домен {domain} заснований на домені верхнього рівня спеціального призначення (TLD) такого як .local або .test і тому не очікується, що у нього будуть актуальні записи DNS.",
"diagnosis_dns_try_dyndns_update_force": "Конфігурація DNS цього домену повинна автоматично управлятися YunoHost. Якщо це не так, ви можете спробувати примусово оновити її за допомогою команди <cmd>yunohost dyndns update --force</cmd>.",
"diagnosis_dns_point_to_doc": "Якщо вам потрібна допомога з налаштування DNS-записів, зверніться до документації на сайті <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a>.",
"diagnosis_dns_discrepancy": "Наступний запис DNS, схоже, не відповідає рекомендованій конфігурації: <br>Тип: <code>{type}</code><br>Назва: <code>{name}</code><br>Поточне значення: <code>{current}</code><br>Очікуване значення: <code>{value}</code>",
@ -504,7 +504,7 @@
"diagnosis_ip_connected_ipv4": "Сервер під'єднаний до Інтернету через IPv4!",
"diagnosis_no_cache": "Для категорії «{category}» ще немає кеша діагностики",
"diagnosis_failed": "Не вдалося отримати результат діагностики для категорії '{category}': {error}",
"diagnosis_everything_ok": "Усе виглядає добре для {category}!",
"diagnosis_everything_ok": "Здається, для категорії '{category}' все справно!",
"diagnosis_found_warnings": "Знайдено {warnings} пунктів, які можна поліпшити для {category}.",
"diagnosis_found_errors_and_warnings": "Знайдено {errors} істотний (і) питання (и) (і {warnings} попередження (я)), що відносяться до {category}!",
"diagnosis_found_errors": "Знайдена {errors} важлива проблема (і), пов'язана з {category}!",
@ -675,5 +675,39 @@
"log_app_config_set": "Застосувати конфігурацію до застосунку '{}'",
"service_not_reloading_because_conf_broken": "Неможливо перезавантажити/перезапустити службу '{name}', тому що її конфігурацію порушено: {errors}",
"app_argument_password_help_optional": "Введіть один пробіл, щоб очистити пароль",
"app_argument_password_help_keep": "Натисніть Enter, щоб зберегти поточне значення"
}
"app_argument_password_help_keep": "Натисніть Enter, щоб зберегти поточне значення",
"domain_registrar_is_not_configured": "Реєстратор ще не конфігуровано для домену {domain}.",
"domain_dns_push_not_applicable": "Функція автоматичної конфігурації DNS не застосовується до домену {domain}. Вам слід вручну конфігурувати записи DNS відповідно до документації за адресою https://yunohost.org/dns_config.",
"domain_dns_registrar_not_supported": "YunoHost не зміг автоматично виявити реєстратора, який обробляє цей домен. Вам слід вручну конфігурувати записи DNS відповідно до документації за адресою https://yunohost.org/dns.",
"diagnosis_http_special_use_tld": "Домен {domain} базується на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він буде відкритий за межами локальної мережі.",
"domain_dns_push_managed_in_parent_domain": "Функцією автоконфігурації DNS керує батьківський домен {parent_domain}.",
"domain_dns_registrar_managed_in_parent_domain": "Цей домен є піддоменом {parent_domain_link}. Конфігурацією реєстратора DNS слід керувати на панелі конфігурації {parent_domain}.",
"domain_dns_registrar_yunohost": "Цей домен є nohost.me/nohost.st/ynh.fr, тому його конфігурація DNS автоматично обробляється YunoHost без будь-якої подальшої конфігурації. (див. команду 'yunohost dyndns update')",
"domain_dns_conf_special_use_tld": "Цей домен засновано на спеціальному домені верхнього рівня (TLD), такому як .local або .test, і тому не очікується, що він матиме актуальні записи DNS.",
"domain_dns_registrar_supported": "YunoHost автоматично визначив, що цей домен обслуговується реєстратором **{registrar}**. Якщо ви хочете, YunoHost автоматично налаштує цю DNS-зону, якщо ви надасте йому відповідні облікові дані API. Ви можете знайти документацію про те, як отримати реєстраційні дані API на цій сторінці: https://yunohost.org/registar_api_{registrar}. (Ви також можете вручну налаштувати свої DNS-записи, дотримуючись документації на https://yunohost.org/dns)",
"domain_dns_registrar_experimental": "Поки що інтерфейс з API **{registrar}** не був належним чином протестований і перевірений спільнотою YunoHost. Підтримка є **дуже експериментальною** - будьте обережні!",
"domain_dns_push_success": "Записи DNS оновлено!",
"domain_dns_push_failed": "Оновлення записів DNS зазнало невдачі.",
"domain_dns_push_partial_failure": "DNS-записи частково оновлено: повідомлялося про деякі попередження/помилки.",
"domain_config_mail_in": "Вхідні електронні листи",
"domain_config_mail_out": "Вихідні електронні листи",
"domain_config_auth_token": "Токен автентифікації",
"domain_config_auth_entrypoint": "Точка входу API",
"domain_config_auth_consumer_key": "Ключ споживача",
"domain_dns_push_failed_to_authenticate": "Неможливо пройти автентифікацію на API реєстратора для домену '{domain}'. Ймовірно, облікові дані недійсні? (Помилка: {error})",
"domain_dns_push_failed_to_list": "Не вдалося скласти список поточних записів за допомогою API реєстратора: {error}",
"domain_dns_push_record_failed": "Не вдалося виконати дію {action} запису {type}/{name} : {error}",
"domain_config_features_disclaimer": "Поки що вмикання/вимикання функцій пошти або XMPP впливає тільки на рекомендовану та автоконфігурацію DNS, але не на конфігурацію системи!",
"domain_config_xmpp": "Миттєвий обмін повідомленнями (XMPP)",
"domain_config_auth_key": "Ключ автентифікації",
"domain_config_auth_secret": "Секрет автентифікації",
"domain_config_api_protocol": "API-протокол",
"domain_config_auth_application_key": "Ключ застосунку",
"domain_config_auth_application_secret": "Таємний ключ застосунку",
"log_domain_config_set": "Оновлення конфігурації для домену '{}'",
"log_domain_dns_push": "Передавання записів DNS для домену '{}'",
"other_available_options": "...і {n} інших доступних опцій, які не показано",
"domain_dns_pushing": "Передання записів DNS...",
"ldap_attribute_already_exists": "Атрибут LDAP '{attribute}' вже існує зі значенням '{value}'",
"domain_dns_push_already_up_to_date": "Записи вже оновлені, нічого не потрібно робити."
}

View file

@ -1,2 +0,0 @@
[report]
omit=tests/*,vendor/*,/usr/lib/moulinette/yunohost/

File diff suppressed because it is too large Load diff

255
src/yunohost/app_catalog.py Normal file
View file

@ -0,0 +1,255 @@
import os
import re
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.network import download_json
from moulinette.utils.filesystem import (
read_json,
read_yaml,
write_to_json,
write_to_yaml,
mkdir,
)
from yunohost.utils.i18n import _value_for_locale
from yunohost.utils.error import YunohostError
logger = getActionLogger("yunohost.app_catalog")
APPS_CATALOG_CACHE = "/var/cache/yunohost/repo"
APPS_CATALOG_CONF = "/etc/yunohost/apps_catalog.yml"
APPS_CATALOG_API_VERSION = 2
APPS_CATALOG_DEFAULT_URL = "https://app.yunohost.org/default"
# Old legacy function...
def app_fetchlist():
logger.warning(
"'yunohost app fetchlist' is deprecated. Please use 'yunohost tools update --apps' instead"
)
from yunohost.tools import tools_update
tools_update(target="apps")
def app_catalog(full=False, with_categories=False):
"""
Return a dict of apps available to installation from Yunohost's app catalog
"""
from yunohost.app import _installed_apps, _set_default_ask_questions
# Get app list from catalog cache
catalog = _load_apps_catalog()
installed_apps = set(_installed_apps())
# Trim info for apps if not using --full
for app, infos in catalog["apps"].items():
infos["installed"] = app in installed_apps
infos["manifest"]["description"] = _value_for_locale(
infos["manifest"]["description"]
)
if not full:
catalog["apps"][app] = {
"description": infos["manifest"]["description"],
"level": infos["level"],
}
else:
infos["manifest"]["arguments"] = _set_default_ask_questions(
infos["manifest"].get("arguments", {})
)
# Trim info for categories if not using --full
for category in catalog["categories"]:
category["title"] = _value_for_locale(category["title"])
category["description"] = _value_for_locale(category["description"])
for subtags in category.get("subtags", []):
subtags["title"] = _value_for_locale(subtags["title"])
if not full:
catalog["categories"] = [
{"id": c["id"], "description": c["description"]}
for c in catalog["categories"]
]
if not with_categories:
return {"apps": catalog["apps"]}
else:
return {"apps": catalog["apps"], "categories": catalog["categories"]}
def app_search(string):
"""
Return a dict of apps whose description or name match the search string
"""
# Retrieve a simple dict listing all apps
catalog_of_apps = app_catalog()
# Selecting apps according to a match in app name or description
matching_apps = {"apps": {}}
for app in catalog_of_apps["apps"].items():
if re.search(string, app[0], flags=re.IGNORECASE) or re.search(
string, app[1]["description"], flags=re.IGNORECASE
):
matching_apps["apps"][app[0]] = app[1]
return matching_apps
def _initialize_apps_catalog_system():
"""
This function is meant to intialize the apps_catalog system with YunoHost's default app catalog.
"""
default_apps_catalog_list = [{"id": "default", "url": APPS_CATALOG_DEFAULT_URL}]
try:
logger.debug(
"Initializing apps catalog system with YunoHost's default app list"
)
write_to_yaml(APPS_CATALOG_CONF, default_apps_catalog_list)
except Exception as e:
raise YunohostError(
"Could not initialize the apps catalog system... : %s" % str(e)
)
logger.success(m18n.n("apps_catalog_init_success"))
def _read_apps_catalog_list():
"""
Read the json corresponding to the list of apps catalogs
"""
try:
list_ = read_yaml(APPS_CATALOG_CONF)
# Support the case where file exists but is empty
# by returning [] if list_ is None
return list_ if list_ else []
except Exception as e:
raise YunohostError("Could not read the apps_catalog list ... : %s" % str(e))
def _actual_apps_catalog_api_url(base_url):
return "{base_url}/v{version}/apps.json".format(
base_url=base_url, version=APPS_CATALOG_API_VERSION
)
def _update_apps_catalog():
"""
Fetches the json for each apps_catalog and update the cache
apps_catalog_list is for example :
[ {"id": "default", "url": "https://app.yunohost.org/default/"} ]
Then for each apps_catalog, the actual json URL to be fetched is like :
https://app.yunohost.org/default/vX/apps.json
And store it in :
/var/cache/yunohost/repo/default.json
"""
apps_catalog_list = _read_apps_catalog_list()
logger.info(m18n.n("apps_catalog_updating"))
# Create cache folder if needed
if not os.path.exists(APPS_CATALOG_CACHE):
logger.debug("Initialize folder for apps catalog cache")
mkdir(APPS_CATALOG_CACHE, mode=0o750, parents=True, uid="root")
for apps_catalog in apps_catalog_list:
apps_catalog_id = apps_catalog["id"]
actual_api_url = _actual_apps_catalog_api_url(apps_catalog["url"])
# Fetch the json
try:
apps_catalog_content = download_json(actual_api_url)
except Exception as e:
raise YunohostError(
"apps_catalog_failed_to_download",
apps_catalog=apps_catalog_id,
error=str(e),
)
# Remember the apps_catalog api version for later
apps_catalog_content["from_api_version"] = APPS_CATALOG_API_VERSION
# Save the apps_catalog data in the cache
cache_file = "{cache_folder}/{list}.json".format(
cache_folder=APPS_CATALOG_CACHE, list=apps_catalog_id
)
try:
write_to_json(cache_file, apps_catalog_content)
except Exception as e:
raise YunohostError(
"Unable to write cache data for %s apps_catalog : %s"
% (apps_catalog_id, str(e))
)
logger.success(m18n.n("apps_catalog_update_success"))
def _load_apps_catalog():
"""
Read all the apps catalog cache files and build a single dict (merged_catalog)
corresponding to all known apps and categories
"""
merged_catalog = {"apps": {}, "categories": []}
for apps_catalog_id in [L["id"] for L in _read_apps_catalog_list()]:
# Let's load the json from cache for this catalog
cache_file = "{cache_folder}/{list}.json".format(
cache_folder=APPS_CATALOG_CACHE, list=apps_catalog_id
)
try:
apps_catalog_content = (
read_json(cache_file) if os.path.exists(cache_file) else None
)
except Exception as e:
raise YunohostError(
"Unable to read cache for apps_catalog %s : %s" % (cache_file, e),
raw_msg=True,
)
# Check that the version of the data matches version ....
# ... otherwise it means we updated yunohost in the meantime
# and need to update the cache for everything to be consistent
if (
not apps_catalog_content
or apps_catalog_content.get("from_api_version") != APPS_CATALOG_API_VERSION
):
logger.info(m18n.n("apps_catalog_obsolete_cache"))
_update_apps_catalog()
apps_catalog_content = read_json(cache_file)
del apps_catalog_content["from_api_version"]
# Add apps from this catalog to the output
for app, info in apps_catalog_content["apps"].items():
# (N.B. : there's a small edge case where multiple apps catalog could be listing the same apps ...
# in which case we keep only the first one found)
if app in merged_catalog["apps"]:
logger.warning(
"Duplicate app %s found between apps catalog %s and %s"
% (app, apps_catalog_id, merged_catalog["apps"][app]["repository"])
)
continue
info["repository"] = apps_catalog_id
merged_catalog["apps"][app] = info
# Annnnd categories
merged_catalog["categories"] += apps_catalog_content["categories"]
return merged_catalog

View file

@ -49,10 +49,6 @@ from yunohost.app import (
app_info,
_is_installed,
_make_environment_for_app_script,
_patch_legacy_helpers,
_patch_legacy_php_versions,
_patch_legacy_php_versions_in_settings,
LEGACY_PHP_VERSION_REPLACEMENTS,
_make_tmp_workdir_for_app,
)
from yunohost.hook import (
@ -1190,6 +1186,7 @@ class RestoreManager:
"""
Apply dirty patch to redirect php5 and php7.0 files to php7.3
"""
from yunohost.utils.legacy import LEGACY_PHP_VERSION_REPLACEMENTS
backup_csv = os.path.join(self.work_dir, "backup.csv")
@ -1351,6 +1348,11 @@ class RestoreManager:
app_instance_name -- (string) The app name to restore (no app with this
name should be already install)
"""
from yunohost.utils.legacy import (
_patch_legacy_php_versions,
_patch_legacy_php_versions_in_settings,
_patch_legacy_helpers,
)
from yunohost.user import user_group_list
from yunohost.permission import (
permission_create,
@ -1485,7 +1487,11 @@ class RestoreManager:
logger.debug(m18n.n("restore_running_app_script", app=app_instance_name))
# Prepare env. var. to pass to script
env_dict = _make_environment_for_app_script(app_instance_name)
# FIXME : workdir should be a tmp workdir
app_workdir = os.path.join(self.work_dir, "apps", app_instance_name, "settings")
env_dict = _make_environment_for_app_script(
app_instance_name, workdir=app_workdir
)
env_dict.update(
{
"YNH_BACKUP_DIR": self.work_dir,
@ -1493,9 +1499,6 @@ class RestoreManager:
"YNH_APP_BACKUP_DIR": os.path.join(
self.work_dir, "apps", app_instance_name, "backup"
),
"YNH_APP_BASEDIR": os.path.join(
self.work_dir, "apps", app_instance_name, "settings"
),
}
)
@ -1532,11 +1535,9 @@ class RestoreManager:
remove_script = os.path.join(app_scripts_in_archive, "remove")
# Setup environment for remove script
env_dict_remove = _make_environment_for_app_script(app_instance_name)
env_dict_remove["YNH_APP_BASEDIR"] = os.path.join(
self.work_dir, "apps", app_instance_name, "settings"
env_dict_remove = _make_environment_for_app_script(
app_instance_name, workdir=app_workdir
)
remove_operation_logger = OperationLogger(
"remove_on_failed_restore",
[("app", app_instance_name)],

View file

@ -851,14 +851,9 @@ def _backup_current_cert(domain):
def _check_domain_is_ready_for_ACME(domain):
dnsrecords = (
Diagnoser.get_cached_report(
"dnsrecords",
item={"domain": domain, "category": "basic"},
warn_if_no_cache=False,
)
or {}
)
from yunohost.domain import _get_parent_domain_of
from yunohost.dns import _get_dns_zone_for_domain
httpreachable = (
Diagnoser.get_cached_report(
"web", item={"domain": domain}, warn_if_no_cache=False
@ -866,16 +861,47 @@ def _check_domain_is_ready_for_ACME(domain):
or {}
)
if not dnsrecords or not httpreachable:
parent_domain = _get_parent_domain_of(domain)
dnsrecords = (
Diagnoser.get_cached_report(
"dnsrecords",
item={"domain": parent_domain, "category": "basic"},
warn_if_no_cache=False,
)
or {}
)
base_dns_zone = _get_dns_zone_for_domain(domain)
record_name = (
domain.replace(f".{base_dns_zone}", "") if domain != base_dns_zone else "@"
)
A_record_status = dnsrecords.get("data").get(f"A:{record_name}")
AAAA_record_status = dnsrecords.get("data").get(f"AAAA:{record_name}")
# Fallback to wildcard in case no result yet for the DNS name?
if not A_record_status:
A_record_status = dnsrecords.get("data").get("A:*")
if not AAAA_record_status:
AAAA_record_status = dnsrecords.get("data").get("AAAA:*")
if (
not httpreachable
or not dnsrecords.get("data")
or (A_record_status, AAAA_record_status) == (None, None)
):
raise YunohostValidationError(
"certmanager_domain_not_diagnosed_yet", domain=domain
)
# Check if IP from DNS matches public IP
if not dnsrecords.get("status") in [
"SUCCESS",
"WARNING",
]: # Warning is for missing IPv6 record which ain't critical for ACME
# - 'MISSING' for IPv6 ain't critical for ACME
# - IPv4 can be None assuming there's at least an IPv6, and viveversa
# - (the case where both are None is checked before)
if not (
A_record_status in [None, "OK"]
and AAAA_record_status in [None, "OK", "MISSING"]
):
raise YunohostValidationError(
"certmanager_domain_dns_ip_differs_from_public_ip", domain=domain
)

View file

@ -4,7 +4,8 @@ from shutil import copy2
from moulinette.utils.log import getActionLogger
from yunohost.app import _is_installed, _patch_legacy_php_versions_in_settings
from yunohost.app import _is_installed
from yunohost.utils.legacy import _patch_legacy_php_versions_in_settings
from yunohost.tools import Migration
from yunohost.service import _run_service_command

View file

@ -32,7 +32,7 @@ from collections import OrderedDict
from moulinette import m18n, Moulinette
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, write_to_file, read_toml
from moulinette.utils.filesystem import read_file, write_to_file, read_toml, mkdir
from yunohost.domain import (
domain_list,
@ -40,8 +40,9 @@ from yunohost.domain import (
domain_config_get,
_get_domain_settings,
_set_domain_settings,
_list_subdomains_of,
)
from yunohost.utils.dns import dig, YNH_DYNDNS_DOMAINS
from yunohost.utils.dns import dig, is_yunohost_dyndns_domain, is_special_use_tld
from yunohost.utils.error import YunohostValidationError, YunohostError
from yunohost.utils.network import get_public_ip
from yunohost.log import is_unit_operation
@ -61,6 +62,9 @@ def domain_dns_suggest(domain):
"""
if is_special_use_tld(domain):
return m18n.n("domain_dns_conf_special_use_tld")
_assert_domain_exists(domain)
dns_conf = _build_dns_conf(domain)
@ -104,18 +108,6 @@ def domain_dns_suggest(domain):
return result
def _list_subdomains_of(parent_domain):
_assert_domain_exists(parent_domain)
out = []
for domain in domain_list()["domains"]:
if domain.endswith(f".{parent_domain}"):
out.append(domain)
return out
def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
"""
Internal function that will returns a data structure containing the needed
@ -169,10 +161,7 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
# If this is a ynh_dyndns_domain, we're not gonna include all the subdomains in the conf
# Because dynette only accept a specific list of name/type
# And the wildcard */A already covers the bulk of use cases
if any(
base_domain.endswith("." + ynh_dyndns_domain)
for ynh_dyndns_domain in YNH_DYNDNS_DOMAINS
):
if is_yunohost_dyndns_domain(base_domain):
subdomains = []
else:
subdomains = _list_subdomains_of(base_domain)
@ -297,6 +286,12 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
# Defined by custom hooks ships in apps for example ...
# FIXME : this ain't practical for apps that may want to add
# custom dns records for a subdomain ... there's no easy way for
# an app to compare the base domain is the parent of the subdomain ?
# (On the other hand, in sep 2021, it looks like no app is using
# this mechanism...)
hook_results = hook_callback("custom_dns_rules", args=[base_domain])
for hook_name, results in hook_results.items():
#
@ -426,9 +421,14 @@ def _get_dns_zone_for_domain(domain):
# First, check if domain is a nohost.me / noho.st / ynh.fr
# This is mainly meant to speed up things for "dyndns update"
# ... otherwise we end up constantly doing a bunch of dig requests
for ynh_dyndns_domain in YNH_DYNDNS_DOMAINS:
if domain.endswith("." + ynh_dyndns_domain):
return ynh_dyndns_domain
if is_yunohost_dyndns_domain(domain):
# Keep only foo.nohost.me even if we have subsub.sub.foo.nohost.me
return ".".join(domain.rsplit(".", 3)[-3:])
# Same thing with .local, .test, ... domains
if is_special_use_tld(domain):
# Keep only foo.local even if we have subsub.sub.foo.local
return ".".join(domain.rsplit(".", 2)[-2:])
# Check cache
cache_folder = "/var/cache/yunohost/dns_zones"
@ -471,7 +471,7 @@ def _get_dns_zone_for_domain(domain):
# Check if there's a NS record for that domain
answer = dig(parent, rdtype="NS", full_answers=True, resolvers="force_external")
if answer[0] == "ok":
os.system(f"mkdir -p {cache_folder}")
mkdir(cache_folder, parents=True, force=True)
write_to_file(cache_file, parent)
return parent
@ -520,7 +520,7 @@ def _get_registrar_config_section(domain):
# TODO big project, integrate yunohost's dynette as a registrar-like provider
# TODO big project, integrate other dyndns providers such as netlib.re, or cf the list of dyndns providers supported by cloudron...
if dns_zone in YNH_DYNDNS_DOMAINS:
if is_yunohost_dyndns_domain(dns_zone):
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
@ -530,6 +530,15 @@ def _get_registrar_config_section(domain):
}
)
return OrderedDict(registrar_infos)
elif is_special_use_tld(dns_zone):
registrar_infos["registrar"] = OrderedDict(
{
"type": "alert",
"style": "info",
"ask": m18n.n("domain_dns_conf_special_use_tld"),
"value": None,
}
)
try:
registrar = _relevant_provider_for_domain(dns_zone)[0]
@ -603,6 +612,10 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=
_assert_domain_exists(domain)
if is_special_use_tld(domain):
logger.info(m18n.n("domain_dns_conf_special_use_tld"))
return {}
if not registrar or registrar == "None": # yes it's None as a string
raise YunohostValidationError("domain_dns_push_not_applicable", domain=domain)

View file

@ -29,7 +29,7 @@ from typing import Dict, Any
from moulinette import m18n, Moulinette
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, read_yaml, write_to_yaml
from moulinette.utils.filesystem import write_to_file, read_yaml, write_to_yaml, rm
from yunohost.app import (
app_ssowatconf,
@ -105,6 +105,33 @@ def _assert_domain_exists(domain):
raise YunohostValidationError("domain_name_unknown", domain=domain)
def _list_subdomains_of(parent_domain):
_assert_domain_exists(parent_domain)
out = []
for domain in domain_list()["domains"]:
if domain.endswith(f".{parent_domain}"):
out.append(domain)
return out
def _get_parent_domain_of(domain):
_assert_domain_exists(domain)
if "." not in domain:
return domain
parent_domain = domain.split(".", 1)[-1]
if parent_domain not in domain_list()["domains"]:
return domain # Domain is its own parent
else:
return _get_parent_domain_of(parent_domain)
@is_unit_operation()
def domain_add(operation_logger, domain, dyndns=False):
"""
@ -301,7 +328,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
]
for stuff in stuff_to_delete:
os.system("rm -rf {stuff}")
rm(stuff, force=True, recursive=True)
# Sometime we have weird issues with the regenconf where some files
# appears as manually modified even though they weren't touched ...

View file

@ -33,7 +33,7 @@ import subprocess
from moulinette import m18n
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_file, read_file
from moulinette.utils.filesystem import write_to_file, read_file, rm, chown, chmod
from moulinette.utils.network import download_json
from yunohost.utils.error import YunohostError, YunohostValidationError
@ -152,13 +152,12 @@ def dyndns_subscribe(
os.system(
"cd /etc/yunohost/dyndns && "
"dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER %s"
% domain
)
os.system(
"chmod 600 /etc/yunohost/dyndns/*.key /etc/yunohost/dyndns/*.private"
f"dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER {domain}"
)
chmod("/etc/yunohost/dyndns", 0o600, recursive=True)
chown("/etc/yunohost/dyndns", "root", recursive=True)
private_file = glob.glob("/etc/yunohost/dyndns/*%s*.private" % domain)[0]
key_file = glob.glob("/etc/yunohost/dyndns/*%s*.key" % domain)[0]
with open(key_file) as f:
@ -175,12 +174,12 @@ def dyndns_subscribe(
timeout=30,
)
except Exception as e:
os.system("rm -f %s" % private_file)
os.system("rm -f %s" % key_file)
rm(private_file, force=True)
rm(key_file, force=True)
raise YunohostError("dyndns_registration_failed", error=str(e))
if r.status_code != 201:
os.system("rm -f %s" % private_file)
os.system("rm -f %s" % key_file)
rm(private_file, force=True)
rm(key_file, force=True)
try:
error = json.loads(r.text)["error"]
except Exception:

View file

@ -31,7 +31,6 @@ from moulinette import m18n
from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import process
from moulinette.utils.log import getActionLogger
from moulinette.utils.text import prependlines
FIREWALL_FILE = "/etc/yunohost/firewall.yml"
UPNP_CRON_JOB = "/etc/cron.d/yunohost-firewall-upnp"
@ -240,7 +239,7 @@ def firewall_reload(skip_upnp=False):
except process.CalledProcessError as e:
logger.debug(
"iptables seems to be not available, it outputs:\n%s",
prependlines(e.output.rstrip(), "> "),
e.output.decode().strip(),
)
logger.warning(m18n.n("iptables_unavailable"))
else:
@ -273,7 +272,7 @@ def firewall_reload(skip_upnp=False):
except process.CalledProcessError as e:
logger.debug(
"ip6tables seems to be not available, it outputs:\n%s",
prependlines(e.output.rstrip(), "> "),
e.output.decode().strip(),
)
logger.warning(m18n.n("ip6tables_unavailable"))
else:
@ -526,6 +525,6 @@ def _on_rule_command_error(returncode, cmd, output):
'"%s" returned non-zero exit status %d:\n%s',
cmd,
returncode,
prependlines(output.rstrip(), "> "),
output.decode().strip(),
)
return True

View file

@ -34,7 +34,7 @@ from importlib import import_module
from moulinette import m18n, Moulinette
from yunohost.utils.error import YunohostError, YunohostValidationError
from moulinette.utils import log
from moulinette.utils.filesystem import read_yaml
from moulinette.utils.filesystem import read_yaml, cp
HOOK_FOLDER = "/usr/share/yunohost/hooks/"
CUSTOM_HOOK_FOLDER = "/etc/yunohost/hooks.d/"
@ -60,8 +60,7 @@ def hook_add(app, file):
os.makedirs(CUSTOM_HOOK_FOLDER + action)
finalpath = CUSTOM_HOOK_FOLDER + action + "/" + priority + "-" + app
os.system("cp %s %s" % (file, finalpath))
os.system("chown -hR admin: %s" % HOOK_FOLDER)
cp(file, finalpath)
return {"hook": finalpath}

View file

@ -407,7 +407,7 @@ def is_unit_operation(
if isinstance(value, IOBase):
try:
context[field] = value.name
except:
except Exception:
context[field] = "IOBase"
operation_logger = OperationLogger(op_key, related_to, args=context)

View file

@ -474,7 +474,7 @@ def permission_create(
protected=protected,
sync_perm=sync_perm,
)
except:
except Exception:
permission_delete(permission, force=True)
raise

View file

@ -135,6 +135,9 @@ def regen_conf(
if "glances" in names:
names.remove("glances")
if "avahi-daemon" in names:
names.remove("avahi-daemon")
# [Optimization] We compute and feed the domain list to the conf regen
# hooks to avoid having to call "yunohost domain list" so many times which
# ends up in wasted time (about 3~5 seconds per call on a RPi2)
@ -455,6 +458,10 @@ def _save_regenconf_infos(infos):
if "glances" in infos:
del infos["glances"]
# Ugly hack to get rid of legacy avahi stuff
if "avahi-daemon" in infos:
del infos["avahi-daemon"]
try:
with open(REGEN_CONF_FILE, "w") as f:
yaml.safe_dump(infos, f, default_flow_style=False)

View file

@ -9,7 +9,7 @@ from moulinette import m18n
from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml
from yunohost.utils.error import YunohostError
from yunohost.app import (
from yunohost.app_catalog import (
_initialize_apps_catalog_system,
_read_apps_catalog_list,
_update_apps_catalog,

View file

@ -2,9 +2,11 @@ import glob
import os
import shutil
import pytest
from mock import patch
from .conftest import get_test_apps_dir
from moulinette import Moulinette
from moulinette.utils.filesystem import read_file
from yunohost.domain import _get_maindomain
@ -146,7 +148,9 @@ def test_app_config_regular_setting(config_app):
assert app_config_get(config_app, "main.components.boolean") == "1"
assert app_setting(config_app, "boolean") == "1"
with pytest.raises(YunohostValidationError):
with pytest.raises(YunohostValidationError), patch.object(
os, "isatty", return_value=False
), patch.object(Moulinette, "prompt", return_value="pwet"):
app_config_set(config_app, "main.components.boolean", "pwet")

View file

@ -41,7 +41,13 @@ def clean():
os.system("mkdir -p /etc/ssowat/")
app_ssowatconf()
test_apps = ["break_yo_system", "legacy_app", "legacy_app__2", "full_domain_app"]
test_apps = [
"break_yo_system",
"legacy_app",
"legacy_app__2",
"full_domain_app",
"my_webapp",
]
for test_app in test_apps:
@ -189,6 +195,32 @@ def test_legacy_app_install_main_domain():
assert app_is_not_installed(main_domain, "legacy_app")
def test_app_from_catalog():
main_domain = _get_maindomain()
app_install(
"my_webapp",
args=f"domain={main_domain}&path=/site&with_sftp=0&password=superpassword&is_public=1&with_mysql=0",
)
app_map_ = app_map(raw=True)
assert main_domain in app_map_
assert "/site" in app_map_[main_domain]
assert "id" in app_map_[main_domain]["/site"]
assert app_map_[main_domain]["/site"]["id"] == "my_webapp"
assert app_is_installed(main_domain, "my_webapp")
assert app_is_exposed_on_http(main_domain, "/site", "Custom Web App")
# Try upgrade, should do nothing
app_upgrade("my_webapp")
# Force upgrade, should upgrade to the same version
app_upgrade("my_webapp", force=True)
app_remove("my_webapp")
assert app_is_not_installed(main_domain, "my_webapp")
def test_legacy_app_install_secondary_domain(secondary_domain):
install_legacy_app(secondary_domain, "/legacy")

View file

@ -4,7 +4,12 @@ import os
from .conftest import get_test_apps_dir
from yunohost.utils.error import YunohostError
from yunohost.app import app_install, app_remove, _normalize_domain_path
from yunohost.app import (
app_install,
app_remove,
_is_app_repo_url,
_parse_app_instance_name,
)
from yunohost.domain import _get_maindomain, domain_url_available
from yunohost.permission import _validate_and_sanitize_permission_url
@ -28,20 +33,55 @@ def teardown_function(function):
pass
def test_normalize_domain_path():
def test_parse_app_instance_name():
assert _normalize_domain_path("https://yolo.swag/", "macnuggets") == (
"yolo.swag",
"/macnuggets",
assert _parse_app_instance_name("yolo") == ("yolo", 1)
assert _parse_app_instance_name("yolo1") == ("yolo1", 1)
assert _parse_app_instance_name("yolo__0") == ("yolo__0", 1)
assert _parse_app_instance_name("yolo__1") == ("yolo", 1)
assert _parse_app_instance_name("yolo__23") == ("yolo", 23)
assert _parse_app_instance_name("yolo__42__72") == ("yolo__42", 72)
assert _parse_app_instance_name("yolo__23qdqsd") == ("yolo__23qdqsd", 1)
assert _parse_app_instance_name("yolo__23qdqsd56") == ("yolo__23qdqsd56", 1)
def test_repo_url_definition():
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foobar123_ynh")
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foobar123_ynh/")
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foobar123_ynh.git")
assert _is_app_repo_url(
"https://github.com/YunoHost-Apps/foobar123_ynh/tree/testing"
)
assert _normalize_domain_path("http://yolo.swag", "/macnuggets/") == (
"yolo.swag",
"/macnuggets",
assert _is_app_repo_url(
"https://github.com/YunoHost-Apps/foobar123_ynh/tree/testing/"
)
assert _normalize_domain_path("yolo.swag/", "macnuggets/") == (
"yolo.swag",
"/macnuggets",
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foo-bar-123_ynh")
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foo_bar_123_ynh")
assert _is_app_repo_url("https://github.com/YunoHost-Apps/FooBar123_ynh")
assert _is_app_repo_url("https://github.com/labriqueinternet/vpnclient_ynh")
assert _is_app_repo_url("https://framagit.org/YunoHost/apps/nodebb_ynh")
assert _is_app_repo_url(
"https://framagit.org/YunoHost/apps/nodebb_ynh/-/tree/testing"
)
assert _is_app_repo_url("https://gitlab.com/yunohost-apps/foobar_ynh")
assert _is_app_repo_url("https://code.antopie.org/miraty/qr_ynh")
assert _is_app_repo_url(
"https://gitlab.domainepublic.net/Neutrinet/neutrinet_ynh/-/tree/unstable"
)
assert _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_ynh/tree/1.23.4")
assert _is_app_repo_url("git@github.com:YunoHost-Apps/foobar_ynh.git")
assert not _is_app_repo_url("github.com/YunoHost-Apps/foobar_ynh")
assert not _is_app_repo_url("http://github.com/YunoHost-Apps/foobar_ynh")
assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_wat")
assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_ynh_wat")
assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar/tree/testing")
assert not _is_app_repo_url(
"https://github.com/YunoHost-Apps/foobar_ynh_wat/tree/testing"
)
assert not _is_app_repo_url("https://framagit.org/YunoHost/apps/")
assert not _is_app_repo_url("https://framagit.org/YunoHost/apps/pwet")
assert not _is_app_repo_url("https://framagit.org/YunoHost/apps/pwet_foo")
def test_urlavailable():

View file

@ -34,8 +34,13 @@ def test_get_dns_zone_from_domain_existing():
assert (
_get_dns_zone_for_domain("non-existing-domain.yunohost.org") == "yunohost.org"
)
assert _get_dns_zone_for_domain("yolo.nohost.me") == "nohost.me"
assert _get_dns_zone_for_domain("foo.yolo.nohost.me") == "nohost.me"
assert _get_dns_zone_for_domain("yolo.nohost.me") == "yolo.nohost.me"
assert _get_dns_zone_for_domain("foo.yolo.nohost.me") == "yolo.nohost.me"
assert _get_dns_zone_for_domain("bar.foo.yolo.nohost.me") == "yolo.nohost.me"
assert _get_dns_zone_for_domain("yolo.test") == "yolo.test"
assert _get_dns_zone_for_domain("foo.yolo.test") == "yolo.test"
assert _get_dns_zone_for_domain("yolo.tld") == "yolo.tld"
assert _get_dns_zone_for_domain("foo.yolo.tld") == "yolo.tld"

View file

@ -1049,7 +1049,7 @@ def test_permission_app_remove():
def test_permission_app_change_url():
app_install(
os.path.join(get_test_apps_dir(), "permissions_app_ynh"),
args="domain=%s&domain_2=%s&path=%s&admin=%s"
args="domain=%s&domain_2=%s&path=%s&is_public=1&admin=%s"
% (maindomain, other_domains[0], "/urlpermissionapp", "alice"),
force=True,
)
@ -1072,7 +1072,7 @@ def test_permission_app_change_url():
def test_permission_protection_management_by_helper():
app_install(
os.path.join(get_test_apps_dir(), "permissions_app_ynh"),
args="domain=%s&domain_2=%s&path=%s&admin=%s"
args="domain=%s&domain_2=%s&path=%s&is_public=1&admin=%s"
% (maindomain, other_domains[0], "/urlpermissionapp", "alice"),
force=True,
)
@ -1135,7 +1135,7 @@ def test_permission_legacy_app_propagation_on_ssowat():
app_install(
os.path.join(get_test_apps_dir(), "legacy_app_ynh"),
args="domain=%s&domain_2=%s&path=%s"
args="domain=%s&domain_2=%s&path=%s&is_public=1"
% (maindomain, other_domains[0], "/legacy"),
force=True,
)

File diff suppressed because it is too large Load diff

View file

@ -34,13 +34,15 @@ from typing import List
from moulinette import Moulinette, m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output
from moulinette.utils.filesystem import read_yaml, write_to_yaml
from moulinette.utils.filesystem import read_yaml, write_to_yaml, cp, mkdir, rm
from yunohost.app import (
_update_apps_catalog,
app_info,
app_upgrade,
)
from yunohost.app_catalog import (
_initialize_apps_catalog_system,
_update_apps_catalog,
)
from yunohost.domain import domain_add
from yunohost.dyndns import _dyndns_available, _dyndns_provides
@ -1145,12 +1147,14 @@ class Migration(object):
backup_folder = "/home/yunohost.backup/premigration/" + time.strftime(
"%Y%m%d-%H%M%S", time.gmtime()
)
os.makedirs(backup_folder, 0o750)
mkdir(backup_folder, 0o750, parents=True)
os.system("systemctl stop slapd")
os.system(f"cp -r --preserve /etc/ldap {backup_folder}/ldap_config")
os.system(f"cp -r --preserve /var/lib/ldap {backup_folder}/ldap_db")
os.system(
f"cp -r --preserve /etc/yunohost/apps {backup_folder}/apps_settings"
cp("/etc/ldap", f"{backup_folder}/ldap_config", recursive=True)
cp("/var/lib/ldap", f"{backup_folder}/ldap_db", recursive=True)
cp(
"/etc/yunohost/apps",
f"{backup_folder}/apps_settings",
recursive=True,
)
except Exception as e:
raise YunohostError(
@ -1167,17 +1171,19 @@ class Migration(object):
)
os.system("systemctl stop slapd")
# To be sure that we don't keep some part of the old config
os.system("rm -r /etc/ldap/slapd.d")
os.system(f"cp -r --preserve {backup_folder}/ldap_config/. /etc/ldap/")
os.system(f"cp -r --preserve {backup_folder}/ldap_db/. /var/lib/ldap/")
os.system(
f"cp -r --preserve {backup_folder}/apps_settings/. /etc/yunohost/apps/"
rm("/etc/ldap/slapd.d", force=True, recursive=True)
cp(f"{backup_folder}/ldap_config", "/etc/ldap", recursive=True)
cp(f"{backup_folder}/ldap_db", "/var/lib/ldap", recursive=True)
cp(
f"{backup_folder}/apps_settings",
"/etc/yunohost/apps",
recursive=True,
)
os.system("systemctl start slapd")
os.system(f"rm -r {backup_folder}")
rm(backup_folder, force=True, recursive=True)
logger.info(m18n.n("migration_ldap_rollback_success"))
raise
else:
os.system(f"rm -r {backup_folder}")
rm(backup_folder, force=True, recursive=True)
return func

View file

@ -420,7 +420,9 @@ def user_update(
# without a specified value, change_password will be set to the const 0.
# In this case we prompt for the new password.
if Moulinette.interface.type == "cli" and not change_password:
change_password = Moulinette.prompt(m18n.n("ask_password"), True, True)
change_password = Moulinette.prompt(
m18n.n("ask_password"), is_password=True, confirm=True
)
# Ensure sufficiently complex password
assert_password_is_strong_enough("user", change_password)
@ -675,7 +677,7 @@ def user_import(operation_logger, csvfile, update=False, delete=False):
def to_list(str_list):
L = str_list.split(",") if str_list else []
L = [l.strip() for l in L]
L = [element.strip() for element in L]
return L
existing_users = user_list()["users"]

View file

@ -24,13 +24,16 @@ import re
import urllib.parse
import tempfile
import shutil
import ast
import operator as op
from collections import OrderedDict
from typing import Optional, Dict, List
from typing import Optional, Dict, List, Union, Any, Mapping
from moulinette.interfaces.cli import colorize
from moulinette import Moulinette, m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import (
read_file,
write_to_file,
read_toml,
read_yaml,
@ -45,6 +48,145 @@ from yunohost.log import OperationLogger
logger = getActionLogger("yunohost.config")
CONFIG_PANEL_VERSION_SUPPORTED = 1.0
# Those js-like evaluate functions are used to eval safely visible attributes
# The goal is to evaluate in the same way than js simple-evaluate
# https://github.com/shepherdwind/simple-evaluate
def evaluate_simple_ast(node, context={}):
operators = {
ast.Not: op.not_,
ast.Mult: op.mul,
ast.Div: op.truediv, # number
ast.Mod: op.mod, # number
ast.Add: op.add, # str
ast.Sub: op.sub, # number
ast.USub: op.neg, # Negative number
ast.Gt: op.gt,
ast.Lt: op.lt,
ast.GtE: op.ge,
ast.LtE: op.le,
ast.Eq: op.eq,
ast.NotEq: op.ne,
}
context["true"] = True
context["false"] = False
context["null"] = None
# Variable
if isinstance(node, ast.Name): # Variable
return context[node.id]
# Python <=3.7 String
elif isinstance(node, ast.Str):
return node.s
# Python <=3.7 Number
elif isinstance(node, ast.Num):
return node.n
# Boolean, None and Python 3.8 for Number, Boolean, String and None
elif isinstance(node, (ast.Constant, ast.NameConstant)):
return node.value
# + - * / %
elif (
isinstance(node, ast.BinOp) and type(node.op) in operators
): # <left> <operator> <right>
left = evaluate_simple_ast(node.left, context)
right = evaluate_simple_ast(node.right, context)
if type(node.op) == ast.Add:
if isinstance(left, str) or isinstance(right, str): # support 'I am ' + 42
left = str(left)
right = str(right)
elif type(left) != type(right): # support "111" - "1" -> 110
left = float(left)
right = float(right)
return operators[type(node.op)](left, right)
# Comparison
# JS and Python don't give the same result for multi operators
# like True == 10 > 2.
elif (
isinstance(node, ast.Compare) and len(node.comparators) == 1
): # <left> <ops> <comparators>
left = evaluate_simple_ast(node.left, context)
right = evaluate_simple_ast(node.comparators[0], context)
operator = node.ops[0]
if isinstance(left, (int, float)) or isinstance(right, (int, float)):
try:
left = float(left)
right = float(right)
except ValueError:
return type(operator) == ast.NotEq
try:
return operators[type(operator)](left, right)
except TypeError: # support "e" > 1 -> False like in JS
return False
# and / or
elif isinstance(node, ast.BoolOp): # <op> <values>
for value in node.values:
value = evaluate_simple_ast(value, context)
if isinstance(node.op, ast.And) and not value:
return False
elif isinstance(node.op, ast.Or) and value:
return True
return isinstance(node.op, ast.And)
# not / USub (it's negation number -\d)
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
return operators[type(node.op)](evaluate_simple_ast(node.operand, context))
# match function call
elif isinstance(node, ast.Call) and node.func.__dict__.get("id") == "match":
return re.match(
evaluate_simple_ast(node.args[1], context), context[node.args[0].id]
)
# Unauthorized opcode
else:
opcode = str(type(node))
raise YunohostError(
f"Unauthorize opcode '{opcode}' in visible attribute", raw_msg=True
)
def js_to_python(expr):
in_string = None
py_expr = ""
i = 0
escaped = False
for char in expr:
if char in r"\"'":
# Start a string
if not in_string:
in_string = char
# Finish a string
elif in_string == char and not escaped:
in_string = None
# If we are not in a string, replace operators
elif not in_string:
if char == "!" and expr[i + 1] != "=":
char = "not "
elif char in "|&" and py_expr[-1:] == char:
py_expr = py_expr[:-1]
char = " and " if char == "&" else " or "
# Determine if next loop will be in escaped mode
escaped = char == "\\" and not escaped
py_expr += char
i += 1
return py_expr
def evaluate_simple_js_expression(expr, context={}):
if not expr.strip():
return False
node = ast.parse(js_to_python(expr), mode="eval").body
return evaluate_simple_ast(node, context)
class ConfigPanel:
def __init__(self, config_path, save_path=None):
@ -99,6 +241,11 @@ class ConfigPanel:
result[key]["value"] = question_class.humanize(
option["current_value"], option
)
# FIXME: semantics, technically here this is not about a prompt...
if question_class.hide_user_input_in_prompt:
result[key][
"value"
] = "**************" # Prevent displaying password in `config get`
if mode == "full":
return self.config
@ -164,6 +311,9 @@ class ConfigPanel:
raise
finally:
# Delete files uploaded from API
# FIXME : this is currently done in the context of config panels,
# but could also happen in the context of app install ... (or anywhere else
# where we may parse args etc...)
FileQuestion.clean_upload_dirs()
self._reload_services()
@ -198,20 +348,20 @@ class ConfigPanel:
# Transform toml format into internal format
format_description = {
"toml": {
"root": {
"properties": ["version", "i18n"],
"default": {"version": 1.0},
"defaults": {"version": 1.0},
},
"panels": {
"properties": ["name", "services", "actions", "help"],
"default": {
"defaults": {
"services": [],
"actions": {"apply": {"en": "Apply"}},
},
},
"sections": {
"properties": ["name", "services", "optional", "help", "visible"],
"default": {
"defaults": {
"name": "",
"services": [],
"optional": True,
@ -241,11 +391,11 @@ class ConfigPanel:
"accept",
"redact",
],
"default": {},
"defaults": {},
},
}
def convert(toml_node, node_type):
def _build_internal_config_panel(raw_infos, level):
"""Convert TOML in internal format ('full' mode used by webadmin)
Here are some properties of 1.0 config panel in toml:
- node properties and node children are mixed,
@ -253,48 +403,47 @@ class ConfigPanel:
- some properties have default values
This function detects all children nodes and put them in a list
"""
# Prefill the node default keys if needed
default = format_description[node_type]["default"]
node = {key: toml_node.get(key, value) for key, value in default.items()}
properties = format_description[node_type]["properties"]
defaults = format_description[level]["defaults"]
properties = format_description[level]["properties"]
# Define the filter_key part to use and the children type
i = list(format_description).index(node_type)
subnode_type = (
list(format_description)[i + 1] if node_type != "options" else None
)
# Start building the ouput (merging the raw infos + defaults)
out = {key: raw_infos.get(key, value) for key, value in defaults.items()}
# Now fill the sublevels (+ apply filter_key)
i = list(format_description).index(level)
sublevel = list(format_description)[i + 1] if level != "options" else None
search_key = filter_key[i] if len(filter_key) > i else False
for key, value in toml_node.items():
for key, value in raw_infos.items():
# Key/value are a child node
if (
isinstance(value, OrderedDict)
and key not in properties
and subnode_type
and sublevel
):
# We exclude all nodes not referenced by the filter_key
if search_key and key != search_key:
continue
subnode = convert(value, subnode_type)
subnode = _build_internal_config_panel(value, sublevel)
subnode["id"] = key
if node_type == "toml":
if level == "root":
subnode.setdefault("name", {"en": key.capitalize()})
elif node_type == "sections":
elif level == "sections":
subnode["name"] = key # legacy
subnode.setdefault("optional", toml_node.get("optional", True))
node.setdefault(subnode_type, []).append(subnode)
subnode.setdefault("optional", raw_infos.get("optional", True))
out.setdefault(sublevel, []).append(subnode)
# Key/value are a property
else:
if key not in properties:
logger.warning(f"Unknown key '{key}' found in config toml")
logger.warning(f"Unknown key '{key}' found in config panel")
# Todo search all i18n keys
node[key] = (
out[key] = (
value if key not in ["ask", "help", "name"] else {"en": value}
)
return node
return out
self.config = convert(toml_config_panel, "toml")
self.config = _build_internal_config_panel(toml_config_panel, "root")
try:
self.config["panels"][0]["sections"][0]["options"][0]
@ -376,14 +525,15 @@ class ConfigPanel:
display_header(f"\n# {name}")
# Check and ask unanswered questions
questions = ask_questions_and_parse_answers(section["options"], self.args)
self.new_values.update(
parse_args_in_yunohost_format(self.args, section["options"])
{
question.name: question.value
for question in questions
if question.value is not None
}
)
self.new_values = {
key: value[0]
for key, value in self.new_values.items()
if not value[0] is None
}
self.errors = None
def _get_default_values(self):
@ -457,18 +607,22 @@ class Question(object):
hide_user_input_in_prompt = False
pattern: Optional[Dict] = None
def __init__(self, question, user_answers):
def __init__(self, question: Dict[str, Any], context: Mapping[str, Any] = {}):
self.name = question["name"]
self.type = question.get("type", "string")
self.default = question.get("default", None)
self.current_value = question.get("current_value")
self.optional = question.get("optional", False)
self.visible = question.get("visible", None)
self.context = context
self.choices = question.get("choices", [])
self.pattern = question.get("pattern", self.pattern)
self.ask = question.get("ask", {"en": self.name})
self.help = question.get("help")
self.value = user_answers.get(self.name)
self.redact = question.get("redact", False)
# .current_value is the currently stored value
self.current_value = question.get("current_value")
# .value is the "proposed" value which we got from the user
self.value = question.get("value")
# Empty value is parsed as empty string
if self.default == "":
@ -480,6 +634,8 @@ class Question(object):
@staticmethod
def normalize(value, option={}):
if isinstance(value, str):
value = value.strip()
return value
def _prompt(self, text):
@ -491,12 +647,25 @@ class Question(object):
self.value = Moulinette.prompt(
message=text,
is_password=self.hide_user_input_in_prompt,
confirm=False, # We doesn't want to confirm this kind of password like in webadmin
confirm=False,
prefill=prefill,
is_multiline=(self.type == "text"),
autocomplete=self.choices,
help=_value_for_locale(self.help),
)
def ask_if_needed(self):
if self.visible and not evaluate_simple_js_expression(
self.visible, context=self.context
):
# FIXME There could be several use case if the question is not displayed:
# - we doesn't want to give a specific value
# - we want to keep the previous value
# - we want the default value
self.value = None
return self.value
for i in range(5):
# Display question if no value filled or if it's a readonly message
if Moulinette.interface.type == "cli" and os.isatty(1):
@ -513,12 +682,9 @@ class Question(object):
):
self.value = class_default if self.default is None else self.default
# Normalization
# This is done to enforce a certain formating like for boolean
self.value = self.normalize(self.value, self)
# Prevalidation
try:
# Normalize and validate
self.value = self.normalize(self.value, self)
self._prevalidate()
except YunohostValidationError as e:
# If in interactive cli, re-ask the current question
@ -531,9 +697,10 @@ class Question(object):
raise
break
self.value = self._post_parse_value()
return (self.value, self.argument_type)
return self.value
def _prevalidate(self):
if self.value in [None, ""] and not self.optional:
@ -542,7 +709,12 @@ class Question(object):
# we have an answer, do some post checks
if self.value not in [None, ""]:
if self.choices and self.value not in self.choices:
self._raise_invalid_answer()
raise YunohostValidationError(
"app_argument_choice_invalid",
name=self.name,
value=self.value,
choices=", ".join(self.choices),
)
if self.pattern and not re.match(self.pattern["regexp"], str(self.value)):
raise YunohostValidationError(
self.pattern["error"],
@ -550,25 +722,31 @@ class Question(object):
value=self.value,
)
def _raise_invalid_answer(self):
raise YunohostValidationError(
"app_argument_choice_invalid",
name=self.name,
value=self.value,
choices=", ".join(self.choices),
)
def _format_text_for_user_input_in_cli(self):
def _format_text_for_user_input_in_cli(self, column=False):
text_for_user_input_in_cli = _value_for_locale(self.ask)
if self.choices:
text_for_user_input_in_cli += " [{0}]".format(" | ".join(self.choices))
if self.help or column:
text_for_user_input_in_cli += ":\033[m"
if self.help:
text_for_user_input_in_cli += "\n - "
text_for_user_input_in_cli += _value_for_locale(self.help)
# Prevent displaying a shitload of choices
# (e.g. 100+ available users when choosing an app admin...)
choices = (
list(self.choices.keys())
if isinstance(self.choices, dict)
else self.choices
)
choices_to_display = choices[:20]
remaining_choices = len(choices[20:])
if remaining_choices > 0:
choices_to_display += [
m18n.n("other_available_options", n=remaining_choices)
]
choices_to_display = " | ".join(choices_to_display)
text_for_user_input_in_cli += f" [{choices_to_display}]"
return text_for_user_input_in_cli
def _post_parse_value(self):
@ -659,6 +837,8 @@ class TagsQuestion(Question):
def normalize(value, option={}):
if isinstance(value, list):
return ",".join(value)
if isinstance(value, str):
value = value.strip()
return value
def _prevalidate(self):
@ -684,20 +864,14 @@ class PasswordQuestion(Question):
default_value = ""
forbidden_chars = "{}"
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
def __init__(self, question, context: Mapping[str, Any] = {}):
super().__init__(question, context)
self.redact = True
if self.default is not None:
raise YunohostValidationError(
"app_argument_password_no_default", name=self.name
)
@staticmethod
def humanize(value, option={}):
if value:
return "********" # Avoid to display the password on screen
return ""
def _prevalidate(self):
super()._prevalidate()
@ -712,34 +886,31 @@ class PasswordQuestion(Question):
assert_password_is_strong_enough("user", self.value)
def _format_text_for_user_input_in_cli(self):
need_column = self.current_value or self.optional
text_for_user_input_in_cli = super()._format_text_for_user_input_in_cli(
need_column
)
if self.current_value:
text_for_user_input_in_cli += "\n - " + m18n.n(
"app_argument_password_help_keep"
)
if self.optional:
text_for_user_input_in_cli += "\n - " + m18n.n(
"app_argument_password_help_optional"
)
return text_for_user_input_in_cli
def _prompt(self, text):
super()._prompt(text)
if self.current_value and self.value == "":
self.value = self.current_value
elif self.value == " ":
self.value = ""
class PathQuestion(Question):
argument_type = "path"
default_value = ""
@staticmethod
def normalize(value, option={}):
option = option.__dict__ if isinstance(option, Question) else option
if not value.strip():
if option.get("optional"):
return ""
# Hmpf here we could just have a "else" case
# but we also want PathQuestion.normalize("") to return "/"
# (i.e. if no option is provided, hence .get("optional") is None
elif option.get("optional") is False:
raise YunohostValidationError(
"app_argument_invalid",
name=option.get("name"),
error="Question is mandatory",
)
return "/" + value.strip().strip(" /")
class BooleanQuestion(Question):
argument_type = "boolean"
@ -750,50 +921,70 @@ class BooleanQuestion(Question):
@staticmethod
def humanize(value, option={}):
option = option.__dict__ if isinstance(option, Question) else option
yes = option.get("yes", 1)
no = option.get("no", 0)
value = str(value).lower()
if value == str(yes).lower():
return "yes"
if value == str(no).lower():
return "no"
if value in BooleanQuestion.yes_answers:
return "yes"
if value in BooleanQuestion.no_answers:
return "no"
if value in ["none", ""]:
value = BooleanQuestion.normalize(value, option)
if value == yes:
return "yes"
if value == no:
return "no"
if value is None:
return ""
raise YunohostValidationError(
"app_argument_choice_invalid",
name=option.get("name", ""),
name=option.get("name"),
value=value,
choices="yes, no, y, n, 1, 0",
choices="yes/no",
)
@staticmethod
def normalize(value, option={}):
yes = option.get("yes", 1)
no = option.get("no", 0)
if str(value).lower() in BooleanQuestion.yes_answers:
return yes
option = option.__dict__ if isinstance(option, Question) else option
if str(value).lower() in BooleanQuestion.no_answers:
return no
if isinstance(value, str):
value = value.strip()
if value in [None, ""]:
technical_yes = option.get("yes", 1)
technical_no = option.get("no", 0)
no_answers = BooleanQuestion.no_answers
yes_answers = BooleanQuestion.yes_answers
assert (
str(technical_yes).lower() not in no_answers
), f"'yes' value can't be in {no_answers}"
assert (
str(technical_no).lower() not in yes_answers
), f"'no' value can't be in {yes_answers}"
no_answers += [str(technical_no).lower()]
yes_answers += [str(technical_yes).lower()]
strvalue = str(value).lower()
if strvalue in yes_answers:
return technical_yes
if strvalue in no_answers:
return technical_no
if strvalue in ["none", ""]:
return None
raise YunohostValidationError(
"app_argument_choice_invalid",
name=option.get("name", ""),
value=value,
choices="yes, no, y, n, 1, 0",
name=option.get("name"),
value=strvalue,
choices="yes/no",
)
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
def __init__(self, question, context: Mapping[str, Any] = {}):
super().__init__(question, context)
self.yes = question.get("yes", 1)
self.no = question.get("no", 0)
if self.default is None:
@ -807,42 +998,44 @@ class BooleanQuestion(Question):
return text_for_user_input_in_cli
def get(self, key, default=None):
try:
return getattr(self, key)
except AttributeError:
return default
return getattr(self, key, default)
class DomainQuestion(Question):
argument_type = "domain"
def __init__(self, question, user_answers):
def __init__(self, question, context: Mapping[str, Any] = {}):
from yunohost.domain import domain_list, _get_maindomain
super().__init__(question, user_answers)
super().__init__(question, context)
if self.default is None:
self.default = _get_maindomain()
self.choices = domain_list()["domains"]
def _raise_invalid_answer(self):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("domain_name_unknown", domain=self.value),
)
@staticmethod
def normalize(value, option={}):
if value.startswith("https://"):
value = value[len("https://") :]
elif value.startswith("http://"):
value = value[len("http://") :]
# Remove trailing slashes
value = value.rstrip("/").lower()
return value
class UserQuestion(Question):
argument_type = "user"
def __init__(self, question, user_answers):
def __init__(self, question, context: Mapping[str, Any] = {}):
from yunohost.user import user_list, user_info
from yunohost.domain import _get_maindomain
super().__init__(question, user_answers)
self.choices = user_list()["users"]
super().__init__(question, context)
self.choices = list(user_list()["users"].keys())
if not self.choices:
raise YunohostValidationError(
@ -853,42 +1046,42 @@ class UserQuestion(Question):
if self.default is None:
root_mail = "root@%s" % _get_maindomain()
for user in self.choices.keys():
for user in self.choices:
if root_mail in user_info(user).get("mail-aliases", []):
self.default = user
break
def _raise_invalid_answer(self):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("user_unknown", user=self.value),
)
class NumberQuestion(Question):
argument_type = "number"
default_value = None
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
def __init__(self, question, context: Mapping[str, Any] = {}):
super().__init__(question, context)
self.min = question.get("min", None)
self.max = question.get("max", None)
self.step = question.get("step", None)
@staticmethod
def normalize(value, option={}):
if isinstance(value, int):
return value
if isinstance(value, str):
value = value.strip()
if isinstance(value, str) and value.isdigit():
return int(value)
if value in [None, ""]:
return value
option = option.__dict__ if isinstance(option, Question) else option
raise YunohostValidationError(
"app_argument_invalid", name=option.name, error=m18n.n("invalid_number")
"app_argument_invalid",
name=option.get("name"),
error=m18n.n("invalid_number"),
)
def _prevalidate(self):
@ -915,8 +1108,8 @@ class DisplayTextQuestion(Question):
argument_type = "display_text"
readonly = True
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
def __init__(self, question, context: Mapping[str, Any] = {}):
super().__init__(question, context)
self.optional = True
self.style = question.get(
@ -946,90 +1139,53 @@ class FileQuestion(Question):
@classmethod
def clean_upload_dirs(cls):
# Delete files uploaded from API
if Moulinette.interface.type == "api":
for upload_dir in cls.upload_dirs:
if os.path.exists(upload_dir):
shutil.rmtree(upload_dir)
for upload_dir in cls.upload_dirs:
if os.path.exists(upload_dir):
shutil.rmtree(upload_dir)
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
if question.get("accept"):
self.accept = question.get("accept")
else:
self.accept = ""
if Moulinette.interface.type == "api":
if user_answers.get(f"{self.name}[name]"):
self.value = {
"content": self.value,
"filename": user_answers.get(f"{self.name}[name]", self.name),
}
def __init__(self, question, context: Mapping[str, Any] = {}):
super().__init__(question, context)
self.accept = question.get("accept", "")
def _prevalidate(self):
if self.value is None:
self.value = self.current_value
super()._prevalidate()
if (
isinstance(self.value, str)
and self.value
and not os.path.exists(self.value)
):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("file_does_not_exist", path=self.value),
)
if self.value in [None, ""] or not self.accept:
return
filename = self.value if isinstance(self.value, str) else self.value["filename"]
if "." not in filename or "." + filename.split(".")[
-1
] not in self.accept.replace(" ", "").split(","):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n(
"file_extension_not_accepted", file=filename, accept=self.accept
),
)
if Moulinette.interface.type != "api":
if not self.value or not os.path.exists(str(self.value)):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("file_does_not_exist", path=str(self.value)),
)
def _post_parse_value(self):
from base64 import b64decode
# Upload files from API
# A file arg contains a string with "FILENAME:BASE64_CONTENT"
if not self.value:
return self.value
if Moulinette.interface.type == "api" and isinstance(self.value, dict):
upload_dir = tempfile.mkdtemp(prefix="ynh_filequestion_")
_, file_path = tempfile.mkstemp(dir=upload_dir)
upload_dir = tempfile.mkdtemp(prefix="tmp_configpanel_")
FileQuestion.upload_dirs += [upload_dir]
filename = self.value["filename"]
logger.debug(
f"Save uploaded file {self.value['filename']} from API into {upload_dir}"
)
FileQuestion.upload_dirs += [upload_dir]
# Filename is given by user of the API. For security reason, we have replaced
# os.path.join to avoid the user to be able to rewrite a file in filesystem
# i.e. os.path.join("/foo", "/etc/passwd") == "/etc/passwd"
file_path = os.path.normpath(upload_dir + "/" + filename)
if not file_path.startswith(upload_dir + "/"):
raise YunohostError(
f"Filename '{filename}' received from the API got a relative parent path, which is forbidden",
raw_msg=True,
)
i = 2
while os.path.exists(file_path):
file_path = os.path.normpath(upload_dir + "/" + filename + (".%d" % i))
i += 1
logger.debug(f"Saving file {self.name} for file question into {file_path}")
content = self.value["content"]
def is_file_path(s):
return isinstance(s, str) and s.startswith("/") and os.path.exists(s)
write_to_file(file_path, b64decode(content), file_mode="wb")
if Moulinette.interface.type != "api" or is_file_path(self.value):
content = read_file(str(self.value), file_mode="rb")
else:
content = b64decode(self.value)
write_to_file(file_path, content, file_mode="wb")
self.value = file_path
self.value = file_path
return self.value
@ -1057,25 +1213,41 @@ ARGUMENTS_TYPE_PARSERS = {
}
def parse_args_in_yunohost_format(user_answers, argument_questions):
def ask_questions_and_parse_answers(
raw_questions: Dict, prefilled_answers: Union[str, Mapping[str, Any]] = {}
) -> List[Question]:
"""Parse arguments store in either manifest.json or actions.json or from a
config panel against the user answers when they are present.
Keyword arguments:
user_answers -- a dictionnary of arguments from the user (generally
empty in CLI, filed from the admin interface)
argument_questions -- the arguments description store in yunohost
format from actions.json/toml, manifest.json/toml
or config_panel.json/toml
raw_questions -- the arguments description store in yunohost
format from actions.json/toml, manifest.json/toml
or config_panel.json/toml
prefilled_answers -- a url "query-string" such as "domain=yolo.test&path=/foobar&admin=sam"
or a dict such as {"domain": "yolo.test", "path": "/foobar", "admin": "sam"}
"""
parsed_answers_dict = OrderedDict()
for question in argument_questions:
question_class = ARGUMENTS_TYPE_PARSERS[question.get("type", "string")]
question = question_class(question, user_answers)
if isinstance(prefilled_answers, str):
# FIXME FIXME : this is not uniform with config_set() which uses parse.qs (no l)
# parse_qsl parse single values
# whereas parse.qs return list of values (which is useful for tags, etc)
# For now, let's not migrate this piece of code to parse_qs
# Because Aleks believes some bits of the app CI rely on overriding values (e.g. foo=foo&...&foo=bar)
answers = dict(
urllib.parse.parse_qsl(prefilled_answers or "", keep_blank_values=True)
)
elif isinstance(prefilled_answers, Mapping):
answers = {**prefilled_answers}
else:
answers = {}
answer = question.ask_if_needed()
if answer is not None:
parsed_answers_dict[question.name] = answer
out = []
return parsed_answers_dict
for raw_question in raw_questions:
question_class = ARGUMENTS_TYPE_PARSERS[raw_question.get("type", "string")]
raw_question["value"] = answers.get(raw_question["name"])
question = question_class(raw_question, context=answers)
answers[question.name] = question.ask_if_needed()
out.append(question)
return out

View file

@ -23,6 +23,8 @@ from typing import List
from moulinette.utils.filesystem import read_file
SPECIAL_USE_TLDS = ["local", "localhost", "onion", "test"]
YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"]
# Lazy dev caching to avoid re-reading the file multiple time when calling
@ -30,6 +32,18 @@ YNH_DYNDNS_DOMAINS = ["nohost.me", "noho.st", "ynh.fr"]
external_resolvers_: List[str] = []
def is_yunohost_dyndns_domain(domain):
return any(
domain.endswith(f".{dyndns_domain}") for dyndns_domain in YNH_DYNDNS_DOMAINS
)
def is_special_use_tld(domain):
return any(domain.endswith(f".{tld}") for tld in SPECIAL_USE_TLDS)
def external_resolvers():
global external_resolvers_

View file

@ -1,7 +1,16 @@
import os
import re
import glob
from moulinette import m18n
from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import write_to_json, read_yaml
from moulinette.utils.filesystem import (
read_file,
write_to_file,
write_to_json,
write_to_yaml,
read_yaml,
)
from yunohost.user import user_list
from yunohost.app import (
@ -14,6 +23,8 @@ from yunohost.permission import (
user_permission_update,
permission_sync_to_user,
)
from yunohost.utils.error import YunohostValidationError
logger = getActionLogger("yunohost.legacy")
@ -237,3 +248,213 @@ def translate_legacy_rules_in_ssowant_conf_json_persistent():
logger.warning(
"YunoHost automatically translated some legacy rules in /etc/ssowat/conf.json.persistent to match the new permission system"
)
LEGACY_PHP_VERSION_REPLACEMENTS = [
("/etc/php5", "/etc/php/7.3"),
("/etc/php/7.0", "/etc/php/7.3"),
("/var/run/php5-fpm", "/var/run/php/php7.3-fpm"),
("/var/run/php/php7.0-fpm", "/var/run/php/php7.3-fpm"),
("php5", "php7.3"),
("php7.0", "php7.3"),
(
'phpversion="${phpversion:-7.0}"',
'phpversion="${phpversion:-7.3}"',
), # Many helpers like the composer ones use 7.0 by default ...
(
'"$phpversion" == "7.0"',
'$(bc <<< "$phpversion >= 7.3") -eq 1',
), # patch ynh_install_php to refuse installing/removing php <= 7.3
]
def _patch_legacy_php_versions(app_folder):
files_to_patch = []
files_to_patch.extend(glob.glob("%s/conf/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/*/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder))
files_to_patch.append("%s/manifest.json" % app_folder)
files_to_patch.append("%s/manifest.toml" % app_folder)
for filename in files_to_patch:
# Ignore non-regular files
if not os.path.isfile(filename):
continue
c = (
"sed -i "
+ "".join(
"-e 's@{pattern}@{replace}@g' ".format(pattern=p, replace=r)
for p, r in LEGACY_PHP_VERSION_REPLACEMENTS
)
+ "%s" % filename
)
os.system(c)
def _patch_legacy_php_versions_in_settings(app_folder):
settings = read_yaml(os.path.join(app_folder, "settings.yml"))
if settings.get("fpm_config_dir") == "/etc/php/7.0/fpm":
settings["fpm_config_dir"] = "/etc/php/7.3/fpm"
if settings.get("fpm_service") == "php7.0-fpm":
settings["fpm_service"] = "php7.3-fpm"
if settings.get("phpversion") == "7.0":
settings["phpversion"] = "7.3"
# We delete these checksums otherwise the file will appear as manually modified
list_to_remove = ["checksum__etc_php_7.0_fpm_pool", "checksum__etc_nginx_conf.d"]
settings = {
k: v
for k, v in settings.items()
if not any(k.startswith(to_remove) for to_remove in list_to_remove)
}
write_to_yaml(app_folder + "/settings.yml", settings)
def _patch_legacy_helpers(app_folder):
files_to_patch = []
files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder))
stuff_to_replace = {
# Replace
# sudo yunohost app initdb $db_user -p $db_pwd
# by
# ynh_mysql_setup_db --db_user=$db_user --db_name=$db_user --db_pwd=$db_pwd
"yunohost app initdb": {
"pattern": r"(sudo )?yunohost app initdb \"?(\$\{?\w+\}?)\"?\s+-p\s\"?(\$\{?\w+\}?)\"?",
"replace": r"ynh_mysql_setup_db --db_user=\2 --db_name=\2 --db_pwd=\3",
"important": True,
},
# Replace
# sudo yunohost app checkport whaterver
# by
# ynh_port_available whatever
"yunohost app checkport": {
"pattern": r"(sudo )?yunohost app checkport",
"replace": r"ynh_port_available",
"important": True,
},
# We can't migrate easily port-available
# .. but at the time of writing this code, only two non-working apps are using it.
"yunohost tools port-available": {"important": True},
# Replace
# yunohost app checkurl "${domain}${path_url}" -a "${app}"
# by
# ynh_webpath_register --app=${app} --domain=${domain} --path_url=${path_url}
"yunohost app checkurl": {
"pattern": r"(sudo )?yunohost app checkurl \"?(\$\{?\w+\}?)\/?(\$\{?\w+\}?)\"?\s+-a\s\"?(\$\{?\w+\}?)\"?",
"replace": r"ynh_webpath_register --app=\4 --domain=\2 --path_url=\3",
"important": True,
},
# Remove
# Automatic diagnosis data from YunoHost
# __PRE_TAG1__$(yunohost tools diagnosis | ...)__PRE_TAG2__"
#
"yunohost tools diagnosis": {
"pattern": r"(Automatic diagnosis data from YunoHost( *\n)*)? *(__\w+__)? *\$\(yunohost tools diagnosis.*\)(__\w+__)?",
"replace": r"",
"important": False,
},
# Old $1, $2 in backup/restore scripts...
"app=$2": {
"only_for": ["scripts/backup", "scripts/restore"],
"pattern": r"app=\$2",
"replace": r"app=$YNH_APP_INSTANCE_NAME",
"important": True,
},
# Old $1, $2 in backup/restore scripts...
"backup_dir=$1": {
"only_for": ["scripts/backup", "scripts/restore"],
"pattern": r"backup_dir=\$1",
"replace": r"backup_dir=.",
"important": True,
},
# Old $1, $2 in backup/restore scripts...
"restore_dir=$1": {
"only_for": ["scripts/restore"],
"pattern": r"restore_dir=\$1",
"replace": r"restore_dir=.",
"important": True,
},
# Old $1, $2 in install scripts...
# We ain't patching that shit because it ain't trivial to patch all args...
"domain=$1": {"only_for": ["scripts/install"], "important": True},
}
for helper, infos in stuff_to_replace.items():
infos["pattern"] = (
re.compile(infos["pattern"]) if infos.get("pattern") else None
)
infos["replace"] = infos.get("replace")
for filename in files_to_patch:
# Ignore non-regular files
if not os.path.isfile(filename):
continue
try:
content = read_file(filename)
except MoulinetteError:
continue
replaced_stuff = False
show_warning = False
for helper, infos in stuff_to_replace.items():
# Ignore if not relevant for this file
if infos.get("only_for") and not any(
filename.endswith(f) for f in infos["only_for"]
):
continue
# If helper is used, attempt to patch the file
if helper in content and infos["pattern"]:
content = infos["pattern"].sub(infos["replace"], content)
replaced_stuff = True
if infos["important"]:
show_warning = True
# If the helper is *still* in the content, it means that we
# couldn't patch the deprecated helper in the previous lines. In
# that case, abort the install or whichever step is performed
if helper in content and infos["important"]:
raise YunohostValidationError(
"This app is likely pretty old and uses deprecated / outdated helpers that can't be migrated easily. It can't be installed anymore.",
raw_msg=True,
)
if replaced_stuff:
# Check the app do load the helper
# If it doesn't, add the instruction ourselve (making sure it's after the #!/bin/bash if it's there...
if filename.split("/")[-1] in [
"install",
"remove",
"upgrade",
"backup",
"restore",
]:
source_helpers = "source /usr/share/yunohost/helpers"
if source_helpers not in content:
content.replace("#!/bin/bash", "#!/bin/bash\n" + source_helpers)
if source_helpers not in content:
content = source_helpers + "\n" + content
# Actually write the new content in the file
write_to_file(filename, content)
if show_warning:
# And complain about those damn deprecated helpers
logger.error(
r"/!\ Packagers ! This app uses a very old deprecated helpers ... Yunohost automatically patched the helpers to use the new recommended practice, but please do consider fixing the upstream code right now ..."
)

View file

@ -0,0 +1,92 @@
ynhtest_exec_warn_less() {
FOO='foo'
bar=""
BAR='$bar'
FOOBAR="foo bar"
# These looks like stupid edge case
# but in fact happens when dealing with passwords
# (which could also contain bash chars like [], {}, ...)
# or urls containing &, ...
FOOANDBAR="foo&bar"
FOO1QUOTEBAR="foo'bar"
FOO2QUOTEBAR="foo\"bar"
ynh_exec_warn_less uptime
test ! -e $FOO
ynh_exec_warn_less touch $FOO
test -e $FOO
rm $FOO
test ! -e $FOO1QUOTEBAR
ynh_exec_warn_less touch $FOO1QUOTEBAR
test -e $FOO1QUOTEBAR
rm $FOO1QUOTEBAR
test ! -e $FOO2QUOTEBAR
ynh_exec_warn_less touch $FOO2QUOTEBAR
test -e $FOO2QUOTEBAR
rm $FOO2QUOTEBAR
test ! -e $BAR
ynh_exec_warn_less touch $BAR
test -e $BAR
rm $BAR
test ! -e "$FOOBAR"
ynh_exec_warn_less touch "$FOOBAR"
test -e "$FOOBAR"
rm "$FOOBAR"
test ! -e "$FOOANDBAR"
ynh_exec_warn_less touch $FOOANDBAR
test -e "$FOOANDBAR"
rm "$FOOANDBAR"
###########################
# Legacy stuff using eval #
###########################
test ! -e $FOO
ynh_exec_warn_less "touch $FOO"
test -e $FOO
rm $FOO
test ! -e $FOO1QUOTEBAR
ynh_exec_warn_less "touch \"$FOO1QUOTEBAR\""
# (this works but expliciy *double* quotes have to be provided)
test -e $FOO1QUOTEBAR
rm $FOO1QUOTEBAR
#test ! -e $FOO2QUOTEBAR
#ynh_exec_warn_less "touch \'$FOO2QUOTEBAR\'"
## (this doesn't work with simple or double quotes)
#test -e $FOO2QUOTEBAR
#rm $FOO2QUOTEBAR
test ! -e $BAR
ynh_exec_warn_less 'touch $BAR'
# That one works because $BAR is only interpreted during eval
test -e $BAR
rm $BAR
#test ! -e $BAR
#ynh_exec_warn_less "touch $BAR"
# That one doesn't work because $bar gets interpreted as empty var by eval...
#test -e $BAR
#rm $BAR
test ! -e "$FOOBAR"
ynh_exec_warn_less "touch \"$FOOBAR\""
# (works but requires explicit double quotes otherwise eval would interpret 'foo bar' as two separate args..)
test -e "$FOOBAR"
rm "$FOOBAR"
test ! -e "$FOOANDBAR"
ynh_exec_warn_less "touch \"$FOOANDBAR\""
# (works but requires explicit double quotes otherwise eval would interpret '&' as a "run command in background" and also bar is not a valid command)
test -e "$FOOANDBAR"
rm "$FOOANDBAR"
}

View file

@ -0,0 +1,71 @@
ynhtest_acceptable_path_to_delete() {
mkdir -p /home/someuser
mkdir -p /home/$app
mkdir -p /home/yunohost.app/$app
mkdir -p /var/www/$app
touch /var/www/$app/bar
touch /etc/cron.d/$app
! _acceptable_path_to_delete /
! _acceptable_path_to_delete ////
! _acceptable_path_to_delete " //// "
! _acceptable_path_to_delete /var
! _acceptable_path_to_delete /var/www
! _acceptable_path_to_delete /var/cache
! _acceptable_path_to_delete /usr
! _acceptable_path_to_delete /usr/bin
! _acceptable_path_to_delete /home
! _acceptable_path_to_delete /home/yunohost.backup
! _acceptable_path_to_delete /home/yunohost.app
! _acceptable_path_to_delete /home/yunohost.app/
! _acceptable_path_to_delete ///home///yunohost.app///
! _acceptable_path_to_delete /home/yunohost.app/$app/..
! _acceptable_path_to_delete ///home///yunohost.app///$app///..//
! _acceptable_path_to_delete /home/yunohost.app/../$app/..
! _acceptable_path_to_delete /home/someuser
! _acceptable_path_to_delete /home/yunohost.app//../../$app
! _acceptable_path_to_delete " /home/yunohost.app/// "
! _acceptable_path_to_delete /etc/cron.d/
! _acceptable_path_to_delete /etc/yunohost/
_acceptable_path_to_delete /home/yunohost.app/$app
_acceptable_path_to_delete /home/yunohost.app/$app/bar
_acceptable_path_to_delete /etc/cron.d/$app
_acceptable_path_to_delete /var/www/$app/bar
_acceptable_path_to_delete /var/www/$app
rm /var/www/$app/bar
rm /etc/cron.d/$app
rmdir /home/yunohost.app/$app
rmdir /home/$app
rmdir /home/someuser
rmdir /var/www/$app
}
ynhtest_secure_remove() {
mkdir -p /home/someuser
mkdir -p /home/yunohost.app/$app
mkdir -p /var/www/$app
mkdir -p /var/whatever
touch /var/www/$app/bar
touch /etc/cron.d/$app
! ynh_secure_remove --file="/home/someuser"
! ynh_secure_remove --file="/home/yunohost.app/"
! ynh_secure_remove --file="/var/whatever"
ynh_secure_remove --file="/home/yunohost.app/$app"
ynh_secure_remove --file="/var/www/$app"
ynh_secure_remove --file="/etc/cron.d/$app"
test -e /home/someuser
test -e /home/yunohost.app
test -e /var/whatever
! test -e /home/yunohost.app/$app
! test -e /var/www/$app
! test -e /etc/cron.d/$app
rmdir /home/someuser
rmdir /var/whatever
}

View file

@ -9,7 +9,7 @@ deps =
py37-mypy: mypy >= 0.900
commands =
py37-lint: flake8 src doc data tests --ignore E402,E501,E203,W503 --exclude src/yunohost/vendor
py37-invalidcode: flake8 src data --exclude src/yunohost/tests,src/yunohost/vendor --select F
py37-invalidcode: flake8 src data --exclude src/yunohost/tests,src/yunohost/vendor --select F,E722,W605
py37-black-check: black --check --diff src doc data tests
py37-black-run: black src doc data tests
py37-mypy: mypy --ignore-missing-import --install-types --non-interactive --follow-imports silent src/yunohost/ --exclude (acme_tiny|data_migrations)