Merge branch 'stretch-unstable' into rework-update-and-upgrade

This commit is contained in:
Alexandre Aubin 2019-04-21 01:31:20 +02:00
commit 0386345637
44 changed files with 1550 additions and 883 deletions

View file

@ -1623,6 +1623,32 @@ tools:
full: --force
action: store_true
### tools_regen_conf()
regen-conf:
action_help: Regenerate the configuration file(s)
api: PUT /tools/regenconf
arguments:
names:
help: Categories to regenerate configuration of (all by default)
nargs: "*"
metavar: NAME
-d:
full: --with-diff
help: Show differences in case of configuration changes
action: store_true
-f:
full: --force
help: Override all manual modifications in configuration files
action: store_true
-n:
full: --dry-run
help: Show what would have been regenerated
action: store_true
-p:
full: --list-pending
help: List pending configuration files and exit
action: store_true
subcategories:
migrations:

View file

@ -3,17 +3,17 @@
# Use logrotate to manage the logfile
#
# usage: ynh_use_logrotate [--logfile=/log/file] [--nonappend] [--specific_user=user/group]
# | arg: -l, --logfile= - absolute path of logfile
# | arg: -n, --nonappend - (Option) Replace the config file instead of appending this new config.
# | arg: -l, --logfile - absolute path of logfile
# | arg: -n, --nonappend - (optional) Replace the config file instead of appending this new config.
# | arg: -u, --specific_user : run logrotate as the specified user and group. If not specified logrotate is runned as root.
#
# If no argument provided, a standard directory will be use. /var/log/${app}
# You can provide a path with the directory only or with the logfile.
# If no --logfile is provided, /var/log/${app} will be used as default.
# logfile can be just a directory, or a full path to a logfile :
# /parentdir/logdir
# /parentdir/logdir/logfile.log
#
# It's possible to use this helper several times, each config will be added to the same logrotate config file.
# Unless you use the option --non-append
# It's possible to use this helper multiple times, each config will be added to
# the same logrotate config file. Unless you use the option --non-append
#
# Requires YunoHost version 2.6.4 or higher.
ynh_use_logrotate () {
@ -175,8 +175,7 @@ ynh_remove_systemd_config () {
#
# usage: ynh_add_nginx_config "list of others variables to replace"
#
# | arg: list of others variables to replace separeted by a space
# | for example : 'path_2 port_2 ...'
# | arg: list - (Optional) list of others variables to replace separated by spaces. For example : 'path_2 port_2 ...'
#
# This will use a template in ../conf/nginx.conf
# __PATH__ by $path_url
@ -249,13 +248,23 @@ ynh_remove_nginx_config () {
# Create a dedicated php-fpm config
#
# usage: ynh_add_fpm_config
# usage: ynh_add_fpm_config [--phpversion=7.X]
# | arg: -v, --phpversion - Version of php to use.
#
# Requires YunoHost version 2.7.2 or higher.
ynh_add_fpm_config () {
# Declare an array to define the options of this helper.
local legacy_args=v
declare -Ar args_array=( [v]=phpversion= )
local phpversion
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
# Configure PHP-FPM 7.0 by default
local fpm_config_dir="/etc/php/7.0/fpm"
local fpm_service="php7.0-fpm"
phpversion="${phpversion:-7.0}"
local fpm_config_dir="/etc/php/$phpversion/fpm"
local fpm_service="php${phpversion}-fpm"
# Configure PHP-FPM 5 on Debian Jessie
if [ "$(ynh_get_debian_release)" == "jessie" ]; then
fpm_config_dir="/etc/php5/fpm"
@ -269,6 +278,7 @@ ynh_add_fpm_config () {
ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf"
ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalphpconf"
ynh_replace_string --match_string="__USER__" --replace_string="$app" --target_file="$finalphpconf"
ynh_replace_string --match_string="__PHPVERSION__" --replace_string="$phpversion" --target_file="$finalphpconf"
sudo chown root: "$finalphpconf"
ynh_store_file_checksum --file="$finalphpconf"

View file

@ -37,6 +37,8 @@ ynh_debug () {
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
@ -44,6 +46,8 @@ ynh_debug () {
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 -x

View file

@ -15,16 +15,13 @@ CAN_BIND=${CAN_BIND:-1}
# If DEST is ended by a slash it complete this path with the basename of SRC.
#
# usage: ynh_backup --src_path=src_path [--dest_path=dest_path] [--is_big] [--not_mandatory]
# | arg: -s, --src_path - file or directory to bind or symlink or copy. it shouldn't be in
# the backup dir.
# | arg: -d, --dest_path - destination file or directory inside the
# backup dir
# | arg: -s, --src_path - file or directory to bind or symlink or copy. it shouldn't be in the backup dir.
# | arg: -d, --dest_path - destination file or directory inside the backup dir
# | arg: -b, --is_big - Indicate data are big (mail, video, image ...)
# | arg: -m, --not_mandatory - Indicate that if the file is missing, the backup can ignore it.
# | arg: arg - Deprecated arg
#
# example:
# # Wordpress app context
# Example in the context of a wordpress app
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf"
# # => This line will be added into CSV file
@ -80,7 +77,7 @@ ynh_backup() {
echo "Source path '${src_path}' does not exist" >&2
if [ "$not_mandatory" == "0" ]
then
echo "Source path '${SRC_PATH}' does not exist" >&2
echo "Source path '${src_path}' does not exist" >&2
# This is a temporary fix for fail2ban config files missing after the migration to stretch.
if echo "${src_path}" | grep --quiet "/etc/fail2ban"
@ -198,28 +195,25 @@ with open(sys.argv[1], 'r') as backup_file:
# Restore a file or a directory
#
# Use the registered path in backup_list by ynh_backup to restore the file at
# the good place.
# the right place.
#
# usage: ynh_restore_file --origin_path=origin_path [--dest_path=dest_path] [--not_mandatory]
# | arg: -o, --origin_path - Path where was located the file or the directory before
# to be backuped or relative path to $YNH_CWD where it is located in the backup archive
# | arg: -d, --dest_path - Path where restore the file or the dir, if unspecified,
# the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in
# the archive, the destination will be searched into backup.csv
# | arg: -o, --origin_path - Path where was located the file or the directory before to be backuped or relative path to $YNH_CWD where it is located in the backup archive
# | arg: -d, --dest_path - Path where restore the file or the dir, if unspecified, the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in the archive, the destination will be searched into backup.csv
# | arg: -m, --not_mandatory - Indicate that if the file is missing, the restore process can ignore it.
#
# examples:
# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf"
# # You can also use relative paths:
# ynh_restore_file "conf/nginx.conf"
#
# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in
# /home/yunohost.conf/backup/. Otherwise, the existing file is removed.
#
# examples:
# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf"
# # if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into
# # /etc/nginx/conf.d/$domain.d/$app.conf
# # if no, search a correspondance in the csv (eg: conf/nginx.conf) and restore it into
# # /etc/nginx/conf.d/$domain.d/$app.conf
#
# # DON'T GIVE THE ARCHIVE PATH:
# ynh_restore_file "conf/nginx.conf"
# if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into
# /etc/nginx/conf.d/$domain.d/$app.conf
# if no, search for a match in the csv (eg: conf/nginx.conf) and restore it into
# /etc/nginx/conf.d/$domain.d/$app.conf
#
# Requires YunoHost version 2.6.4 or higher.
ynh_restore_file () {
@ -348,8 +342,7 @@ ynh_store_file_checksum () {
#
# usage: ynh_backup_if_checksum_is_different --file=file
# | arg: -f, --file - The file on which the checksum test will be perfomed.
#
# | ret: Return the name a the backup file, or nothing
# | ret: the name of a backup file, or nothing
#
# Requires YunoHost version 2.6.4 or higher.
ynh_backup_if_checksum_is_different () {

View file

@ -152,10 +152,15 @@ ynh_handle_getopts_args () {
# If there's already another value for this option, add a ; before adding the new value
eval ${option_var}+="\;"
fi
# Escape double quote to prevent any interpretation during the eval
all_args[$i]="${all_args[$i]//\"/\\\"}"
eval ${option_var}+=\"${all_args[$i]}\"
# For the record.
# We're using eval here to get the content of the variable stored itself as simple text in $option_var...
# Other ways to get that content would be to use either ${!option_var} or declare -g ${option_var}
# But... ${!option_var} can't be used as left part of an assignation.
# declare -g ${option_var} will create a local variable (despite -g !) and will not be available for the helper itself.
# So... Stop fucking arguing each time that eval is evil... Go find an other working solution if you can find one!
eval ${option_var}+='"${all_args[$i]}"'
shift_value=$(( shift_value + 1 ))
fi
done
@ -193,12 +198,9 @@ ynh_handle_getopts_args () {
# The variable name will be stored in 'option_var'
local option_var="${args_array[$option_flag]%=}"
# Escape double quote to prevent any interpretation during the eval
arguments[$i]="${arguments[$i]//\"/\\\"}"
# Store each value given as argument in the corresponding variable
# The values will be stored in the same order than $args_array
eval ${option_var}+=\"${arguments[$i]}\"
eval ${option_var}+='"${arguments[$i]}"'
done
unset legacy_args
else

View file

@ -186,7 +186,7 @@ ynh_mysql_drop_user() {
# usage: ynh_mysql_setup_db --db_user=user --db_name=name [--db_pwd=pwd]
# | arg: -u, --db_user - Owner of the database
# | arg: -n, --db_name - Name of the database
# | arg: -p, --db_pwd - Password of the database. If not given, a password will be generated
# | arg: -p, --db_pwd - Password of the database. If not provided, a password will be generated
#
# Requires YunoHost version 2.6.4 or higher.
ynh_mysql_setup_db () {
@ -200,7 +200,7 @@ ynh_mysql_setup_db () {
ynh_handle_getopts_args "$@"
local new_db_pwd=$(ynh_string_random) # Generate a random password
# If $db_pwd is not given, use new_db_pwd instead for db_pwd
# If $db_pwd is not provided, use new_db_pwd instead for db_pwd
db_pwd="${db_pwd:-$new_db_pwd}"
ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd" # Create the database

View file

@ -1,14 +1,16 @@
#!/bin/bash
# Normalize the url path syntax
#
# Handle the slash at the beginning of path and its absence at ending
# Return a normalized url path
#
# example: url_path=$(ynh_normalize_url_path $url_path)
# ynh_normalize_url_path example -> /example
# ynh_normalize_url_path /example -> /example
# ynh_normalize_url_path /example/ -> /example
# ynh_normalize_url_path / -> /
# examples:
# url_path=$(ynh_normalize_url_path $url_path)
# ynh_normalize_url_path example # -> /example
# ynh_normalize_url_path /example # -> /example
# ynh_normalize_url_path /example/ # -> /example
# ynh_normalize_url_path / # -> /
#
# usage: ynh_normalize_url_path --path_url=path_to_normalize
# | arg: -p, --path_url - URL path to normalize before using it

View file

@ -123,7 +123,7 @@ ynh_install_nodejs () {
fi
# Store the ID of this app and the version of node requested for it
echo "$YNH_APP_ID:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version"
echo "$YNH_APP_INSTANCE_NAME:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version"
# Store nodejs_version into the config of this app
ynh_app_setting_set --app=$app --key=nodejs_version --value=$nodejs_version
@ -147,7 +147,7 @@ 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_ID:$nodejs_version/d" "$n_install_dir/ynh_app_version"
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"

View file

@ -192,6 +192,7 @@ ynh_print_ON () {
# | arg: -m, --message= - The text to print
# | arg: -w, --weight= - The weight for this progression. This value is 1 by default. Use a bigger value for a longer part of the script.
# | arg: -t, --time= - Print the execution time since the last call to this helper. Especially usefull to define weights.
# The execution time is given for the duration since the previous call. So the weight should be applied to this previous call.
# | arg: -l, --last= - Use for the last call of the helper, to fill te progression bar.
#
# Requires YunoHost version 3.?.? or higher.
@ -201,6 +202,7 @@ previous_weight=0
base_time=$(date +%s)
ynh_script_progression () {
# Declare an array to define the options of this helper.
local legacy_args=mwtl
declare -Ar args_array=( [m]=message= [w]=weight= [t]=time [l]=last )
local message
local weight
@ -222,9 +224,9 @@ ynh_script_progression () {
local weight_calls=$(grep --perl-regexp --count "^[^#]*ynh_script_progression.*(--weight|-w )" $0)
# Get the weight of each occurrences of 'ynh_script_progression' in the script using --weight
local weight_valuesA="$(grep --perl-regexp "^[^#]*ynh_script_progression.*--weight" $0 | sed 's/.*--weight[= ]\([[:digit:]].*\)/\1/g')"
# Get the weight of each occurrences of 'ynh_script_progression' in the script using -w
local weight_valuesB="$(grep --perl-regexp "^[^#]*ynh_script_progression.*-w " $0 | sed 's/.*-w[= ]\([[:digit:]].*\)/\1/g')"
local weight_valuesA="$(grep --perl-regexp "^[^#]*ynh_script_progression.*--weight" $0 | sed 's/.*--weight[= ]\([[:digit:]]*\).*/\1/g')"
# Get the weight of each occurrences of 'ynh_script_progression' in the script using -w
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 ))

View file

@ -174,7 +174,7 @@ ynh_psql_database_exists() {
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(sudo cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT datname FROM pg_database WHERE datname='$database';" | grep --quiet "$user"; then
if ! sudo --login --user=postgres PGUSER="postgres" PGPASSWORD="$(sudo cat $PSQL_ROOT_PWD_FILE)" psql -tAc "SELECT datname FROM pg_database WHERE datname='$database';" | grep --quiet "$database"; then
return 1
else
return 0

View file

@ -75,6 +75,7 @@ ynh_get_debian_release () {
# | arg: -e, --length= - Length of the error log : Default : 20
ynh_systemd_action() {
# Declare an array to define the options of this helper.
local legacy_args=nalpte
declare -Ar args_array=( [n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length= )
local service_name
local action
@ -175,6 +176,7 @@ ynh_clean_check_starting () {
# Requires YunoHost version 3.?.? or higher.
ynh_read_manifest () {
# Declare an array to define the options of this helper.
local legacy_args=mk
declare -Ar args_array=( [m]=manifest= [k]=manifest_key= )
local manifest
local manifest_key
@ -200,6 +202,8 @@ ynh_read_manifest () {
#
# Requires YunoHost version 3.?.? or higher.
ynh_app_upstream_version () {
# Declare an array to define the options of this helper.
local legacy_args=m
declare -Ar args_array=( [m]=manifest= )
local manifest
# Manage arguments with getopts
@ -221,6 +225,8 @@ ynh_app_upstream_version () {
#
# Requires YunoHost version 3.?.? or higher.
ynh_app_package_version () {
# Declare an array to define the options of this helper.
local legacy_args=m
declare -Ar args_array=( [m]=manifest= )
local manifest
# Manage arguments with getopts
@ -235,7 +241,7 @@ ynh_app_package_version () {
# - UPGRADE_APP if the upstream app version has changed
# - UPGRADE_PACKAGE if only the YunoHost package has changed
#
## It stops the current script without error if the package is up-to-date
# It stops the current script without error if the package is up-to-date
#
# This helper should be used to avoid an upgrade of an app, or the upstream part
# of it, when it's not needed

View file

@ -89,16 +89,15 @@ ynh_system_group_exists() {
# Create a system user
#
# examples:
# - ynh_system_user_create --username=nextcloud -> creates a nextcloud user with
# no home directory and /usr/sbin/nologin login shell (hence no login capability)
# - ynh_system_user_create --username=discourse --home_dir=/var/www/discourse --use_shell --> creates a
# discourse user using /var/www/discourse as home directory and the default login shell
# # Create a nextcloud user with no home directory and /usr/sbin/nologin login shell (hence no login capability)
# ynh_system_user_create --username=nextcloud
# # Create a discourse user using /var/www/discourse as home directory and the default login shell
# ynh_system_user_create --username=discourse --home_dir=/var/www/discourse --use_shell
#
# usage: ynh_system_user_create --username=user_name [--home_dir=home_dir] [--use_shell]
# | arg: -u, --username - Name of the system user that will be create
# | arg: -h, --home_dir - Path of the home dir for the user. Usually the final path of the app. If this argument is omitted, the user will be created without home
# | arg: -s, --use_shell - Create a user using the default login shell if present.
# If this argument is omitted, the user will be created with /usr/sbin/nologin shell
# | arg: -s, --use_shell - Create a user using the default login shell if present. If this argument is omitted, the user will be created with /usr/sbin/nologin shell
#
# Requires YunoHost version 2.6.4 or higher.
ynh_system_user_create () {

View file

@ -12,7 +12,7 @@ do_pre_regen() {
[[ ! -f /etc/yunohost/from_script ]] || return 0
cd /usr/share/yunohost/templates/ssh
# do not listen to IPv6 if unavailable
[[ -f /proc/net/if_inet6 ]] && ipv6_enabled=true || ipv6_enabled=false
@ -23,6 +23,9 @@ do_pre_regen() {
ssh_keys="$ssh_keys $(ls /etc/ssh/ssh_host_dsa_key 2>/dev/null || true)"
fi
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.ssh.compatibility')"
export ssh_keys
export ipv6_enabled
ynh_render_template "sshd_config" "${pending_dir}/etc/ssh/sshd_config"

View file

@ -10,7 +10,25 @@ do_init_regen() {
exit 1
fi
do_pre_regen ""
cd /usr/share/yunohost/templates/nginx
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"
# 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 "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
# Restart nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null && service nginx restart || (nginx -t && exit 1)
exit 0
}
do_pre_regen() {
@ -22,20 +40,16 @@ do_pre_regen() {
nginx_conf_dir="${nginx_dir}/conf.d"
mkdir -p "$nginx_conf_dir"
# install plain conf files
# install / update plain conf files
cp plain/* "$nginx_conf_dir"
# probably run with init: just disable default site, restart NGINX and exit
if [[ -z "$pending_dir" ]]; then
rm -f "${nginx_dir}/sites-enabled/default"
service nginx restart
exit 0
fi
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host)
domain_list=$(sudo yunohost domain list --output-as plain --quiet)
# Support different strategy for security configurations
export compatibility="$(yunohost settings get 'security.nginx.compatibility')"
# add domain conf files
for domain in $domain_list; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
@ -58,6 +72,8 @@ do_pre_regen() {
done
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
# remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \
| awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }')

View file

@ -1,8 +1,8 @@
# Insert YunoHost panel
sub_filter </head> '<script type="text/javascript" src="/ynhpanel.js"></script></head>';
# Insert YunoHost button + portal overlay
sub_filter </head> '<script type="text/javascript" src="/ynh_portal.js"></script><link type="text/css" rel="stylesheet" href="/ynh_overlay.css"></link><script type="text/javascript" src="/ynhtheme/custom_portal.js"></script><link type="text/css" rel="stylesheet" href="/ynhtheme/custom_overlay.css"></link></head>';
sub_filter_once on;
# Apply to other mime types than text/html
sub_filter_types application/xhtml+xml;
# Prevent YunoHost panel files from being blocked by specific app rules
location ~ ynhpanel\.(js|json|css) {
location ~ (ynh_portal.js|ynh_overlay.css|ynh_userinfo.json) {
}

View file

@ -29,6 +29,14 @@ server {
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
{% if compatibility == "modern" %}
# Ciphers with modern compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
# The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
{% else %}
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
@ -38,15 +46,10 @@ server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
# Ciphers with modern compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
# Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
#ssl_protocols TLSv1.2;
#ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
# Uncomment the following directive after DH generation
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
#ssl_dhparam /etc/ssl/private/dh2048.pem;
{% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security

View file

@ -20,6 +20,14 @@ server {
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
{% if compatibility == "modern" %}
# Ciphers with modern compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
# Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
{% else %}
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
@ -29,20 +37,15 @@ server {
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
# Ciphers with modern compatibility
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern
# Uncomment the following to use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...)
#ssl_protocols TLSv1.2;
#ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
# Uncomment the following directive after DH generation
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048
#ssl_dhparam /etc/ssl/private/dh2048.pem;
{% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security
# https://observatory.mozilla.org/
more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
# https://observatory.mozilla.org/
more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
more_set_headers "Referrer-Policy : 'same-origin'";
more_set_headers "Content-Security-Policy : upgrade-insecure-requests; object-src 'none'; script-src https: 'unsafe-eval'";
more_set_headers "X-Content-Type-Options : nosniff";

View file

@ -15,10 +15,17 @@ HostKey {{ key }}{% endfor %}
# https://infosec.mozilla.org/guidelines/openssh
# ##############################################
# Keys, ciphers and MACS
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
{% if compatibility == "intermediate" %}
KexAlgorithms diffie-hellman-group-exchange-sha256
Ciphers aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512,hmac-sha2-256
{% else %}
# By default use "modern" Mozilla configuration
# Keys, ciphers and MACS
KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
{% endif %}
# Use kernel sandbox mechanisms where possible in unprivileged processes
UsePrivilegeSeparation sandbox

View file

@ -20,8 +20,6 @@ mysql:
glances: {}
ssh:
log: /var/log/auth.log
ssl:
status: null
metronome:
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
slapd:
@ -34,10 +32,9 @@ yunohost-firewall:
need_lock: true
nslcd:
log: /var/log/syslog
nsswitch:
status: null
yunohost:
status: null
nsswitch: null
ssl: null
yunohost: null
bind9: null
tahoe-lafs: null
memcached: null

50
debian/changelog vendored
View file

@ -1,3 +1,53 @@
yunohost (3.5.2.2) stable; urgency=low
- Hotfix for ynh_psql_remove_db (from ljf)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 18 Apr 2019 17:32:00 +0000
yunohost (3.5.2.1) stable; urgency=low
- [fix] Fresh install was broken because of yunohost_admin.conf initialization
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 11 Apr 2019 14:38:00 +0000
yunohost (3.5.2) stable; urgency=low
- Release as stable !
- [doc] Update script to automatically generate helper doc
- [i18n] Update translations for Catalan, Arabic, Italian
Thanks to all contributors: Aleks, xaloc, BoF, silkevicious ! <3
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 10 Apr 2019 01:53:00 +0000
yunohost (3.5.1.1) testing; urgency=low
- [fix] enabled/disabled status for sysv services
- [fix] Nodejs helpers : use YNH_APP_INSTANCE_NAME instead of YNH_APP_ID (#700)
- [fix] nginx diagnosis when there's an error throwing a huge useless traceback. Use Popen instead to display the real error
- [fix] service_status returns different type of data if you ask for one or multiple services
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 03 Apr 2019 17:28:00 +0000
yunohost (3.5.1) testing; urgency=low
- [fix] Fix the dbus interface to get info for services (#698)
- [mod] Use ask key for display_text instead and support i18n (#697)
- [fix] Rework tools update (#695)
- [enh] Nginx conf tweaks for theme (#689)
- [fix] Fix argument escaping in getopts (#685, #683)
- [enh] Support php versions in ynh_add_fpm_config (#674)
- [enh] Check that required services are up before running app install and upgrade (#670)
- [doc] Add min version for all helpers (#664)
- [enh] Add a setting to control compatibility/security tradeoff for nginx and ssh configurations (#640)
- [enh] Hooks to allow apps to extend the recommended DNS configuration (#517)
- Misc technical fixes / improvements (0bd781b, fad3edf, 1268872, 847ceca, 26e77b7, b6cff68)
- [i18n] Update translation for French, Catalan, Esperanto, Occitan
Thanks to all contributors: Aleks, Bram, Gabriel Corona, Jibec, Josue, Maniack C, Mélanie C., Quentí, Romuald du Song, ljf, ppr, Xaloc ! <3
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 03 Apr 2019 02:13:00 +0000
yunohost (3.5.0.2) testing; urgency=low
- [fix] Make sure that `ynh_system_user_delete` also deletes the group (#680)

2
debian/postinst vendored
View file

@ -12,7 +12,7 @@ do_configure() {
bash /usr/share/yunohost/hooks/conf_regen/15-nginx init
else
echo "Regenerating configuration, this might take a while..."
yunohost service regen-conf --output-as none
yunohost tools regen-conf --output-as none
echo "Launching migrations.."
yunohost tools migrations migrate --auto

View file

@ -4,7 +4,12 @@ import os
import glob
import datetime
def render(data):
def render(helpers):
data = { "helpers": helpers,
"date": datetime.datetime.now().strftime("%m/%d/%Y"),
"version": open("../debian/changelog").readlines()[0].split()[1].strip("()")
}
from jinja2 import Template
from ansi2html import Ansi2HTMLConverter
@ -43,7 +48,7 @@ class Parser():
"code": [] }
for i, line in enumerate(self.file):
if line.startswith("#!/bin/bash"):
continue
@ -103,7 +108,6 @@ class Parser():
b["usage"] = ""
b["args"] = []
b["ret"] = ""
b["example"] = ""
subblocks = '\n'.join(b["comments"]).split("\n\n")
@ -114,17 +118,29 @@ class Parser():
b["brief"] = subblock
continue
elif subblock.startswith("example"):
elif subblock.startswith("example:"):
b["example"] = " ".join(subblock.split()[1:])
continue
elif subblock.startswith("examples:"):
b["examples"] = subblock.split("\n")[1:]
continue
elif subblock.startswith("usage"):
for line in subblock.split("\n"):
if line.startswith("| arg"):
argname = line.split()[2]
argdescr = " ".join(line.split()[4:])
b["args"].append((argname, argdescr))
linesplit = line.split()
argname = linesplit[2]
# Detect that there's a long argument version (-f, --foo - Some description)
if argname.endswith(",") and linesplit[3].startswith("--"):
argname = argname.strip(",")
arglongname = linesplit[3]
argdescr = " ".join(linesplit[5:])
b["args"].append((argname, arglongname, argdescr))
else:
argdescr = " ".join(linesplit[4:])
b["args"].append((argname, argdescr))
elif line.startswith("| ret"):
b["ret"] = " ".join(line.split()[2:])
else:
@ -136,9 +152,17 @@ class Parser():
elif subblock.startswith("| arg"):
for line in subblock.split("\n"):
if line.startswith("| arg"):
argname = line.split()[2]
argdescr = line.split()[4:]
b["args"].append((argname, argdescr))
linesplit = line.split()
argname = linesplit[2]
# Detect that there's a long argument version (-f, --foo - Some description)
if argname.endswith(",") and linesplit[3].startswith("--"):
argname = argname.strip(",")
arglongname = linesplit[3]
argdescr = " ".join(linesplit[5:])
b["args"].append((argname, arglongname, argdescr))
else:
argdescr = " ".join(linesplit[4:])
b["args"].append((argname, argdescr))
continue
else:

View file

@ -2,7 +2,7 @@
<h1>App helpers</h1>
{% for category, helpers in data %}
{% for category, helpers in data.helpers %}
<h3 style="text-transform: uppercase; font-weight: bold">{{ category }}</h3>
@ -27,8 +27,12 @@
<p>
<strong>Arguments</strong>:
<ul>
{% for name, descr in h.args %}
<li><code>{{ name }}</code> : {{ descr }}</li>
{% for infos in h.args %}
{% if infos|length == 2 %}
<li><code>{{ infos[0] }}</code> : {{ infos[1] }}</li>
{% else %}
<li><code>{{ infos[0] }}</code>, <code>{{ infos[1] }}</code> : {{ infos[2] }}</li>
{% endif %}
{% endfor %}
</ul>
</p>
@ -38,11 +42,25 @@
<strong>Returns</strong>: {{ h.ret }}
</p>
{% endif %}
{% if h.example %}
{% if "example" in h.keys() %}
<p>
<strong>Example</strong>: <code class="helper-code">{{ h.example }}</code>
</p>
{% endif %}
{% if "examples" in h.keys() %}
<p>
<strong>Examples</strong>:<ul>
{% for example in h.examples %}
{% if not example.strip().startswith("# ") %}
<code class="helper-code">{{ example }}</code>
{% else %}
{{ example.strip("# ") }}
{% endif %}
<br>
{% endfor %}
</ul>
</p>
{% endif %}
{% if h.details %}
<p>
<strong>Details</strong>:
@ -63,6 +81,8 @@
{% endfor %}
{% endfor %}
<p>Generated by <a href="https://github.com/YunoHost/yunohost/blob/stretch-unstable/doc/generate_helper_doc.py">this script</a> on {{data.date}} (Yunohost version {{data.version}})</p>
<style>
/*=================================================

View file

@ -29,13 +29,13 @@
"app_not_properly_removed": "لم يتم حذف تطبيق {app:s} بشكلٍ جيّد",
"app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes",
"app_removed": "تمت إزالة تطبيق {app:s}",
"app_requirements_checking": "جار فحص الحزم اللازمة لـ {app} ...",
"app_requirements_checking": "جار فحص الحزم اللازمة لـ {app}",
"app_requirements_failed": "Unable to meet requirements for {app}: {error}",
"app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}",
"app_sources_fetch_failed": "تعذرت عملية جلب مصادر الملفات",
"app_unknown": "برنامج مجهول",
"app_unsupported_remote_type": "Unsupported remote type used for the app",
"app_upgrade_app_name": "جارٍ تحديث برنامج {app}...",
"app_upgrade_app_name": "جارٍ تحديث تطبيق {app}…",
"app_upgrade_failed": "تعذرت عملية ترقية {app:s}",
"app_upgrade_some_app_failed": "تعذرت عملية ترقية بعض البرمجيات",
"app_upgraded": "تم تحديث التطبيق {app:s}",
@ -413,5 +413,18 @@
"service_description_mysql": "يقوم بتخزين بيانات التطبيقات (قواعد بيانات SQL)",
"service_description_rspamd": "يقوم بتصفية البريد المزعج و إدارة ميزات أخرى للبريد",
"service_description_yunohost-firewall": "يريد فتح و غلق منافذ الإتصال إلى الخدمات",
"users_available": "المستخدمون المتوفرون:"
"users_available": "المستخدمون المتوفرون:",
"aborting": "إلغاء.",
"admin_password_too_long": "يرجى اختيار كلمة سرية أقصر مِن 127 حرف",
"app_not_upgraded": "لم يتم تحديث التطبيقات التالية: {apps}",
"app_start_install": "جارٍ تثبيت التطبيق {app}…",
"app_start_remove": "جارٍ حذف التطبيق {app}…",
"app_start_restore": "جارٍ استرجاع التطبيق {app}…",
"app_upgrade_several_apps": "سوف يتم تحديث التطبيقات التالية: {apps}",
"ask_new_domain": "نطاق جديد",
"ask_new_path": "مسار جديد",
"global_settings_setting_security_password_admin_strength": "قوة الكلمة السرية الإدارية",
"global_settings_setting_security_password_user_strength": "قوة الكلمة السرية للمستخدم",
"log_app_addaccess": "إضافة ترخيص بالنفاذ إلى '{}'",
"password_too_simple_1": "يجب أن يكون طول الكلمة السرية على الأقل 8 حروف"
}

1
locales/bn_BD.json Normal file
View file

@ -0,0 +1 @@
{}

View file

@ -29,20 +29,20 @@
"app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament",
"app_package_need_update": "El paquet de l'aplicació {app} ha de ser actualitzat per poder seguir els canvis de YunoHost",
"app_removed": "{app:s} ha estat suprimida",
"app_requirements_checking": "Verificació dels paquets requerits per {app}",
"app_requirements_checking": "Verificació dels paquets requerits per {app}",
"app_requirements_failed": "No es poden satisfer els requeriments per {app}: {error}",
"app_requirements_unmeet": "No es compleixen els requeriments per {app}, el paquet {pkgname} ({version}) ha de ser {spec}",
"app_sources_fetch_failed": "No s'han pogut carregar els fitxers font",
"app_unknown": "Aplicació desconeguda",
"app_unsupported_remote_type": "El tipus remot utilitzat per l'aplicació no està suportat",
"app_upgrade_app_name": "Actualitzant l'aplicació {app}...",
"app_upgrade_app_name": "Actualitzant l'aplicació {app}",
"app_upgrade_failed": "No s'ha pogut actualitzar {app:s}",
"app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions",
"app_upgraded": "{app:s} ha estat actualitzada",
"appslist_corrupted_json": "No s'han pogut carregar les llistes d'aplicacions. Sembla que {filename:s} està danyat.",
"appslist_could_not_migrate": "No s'ha pogut migrar la llista d'aplicacions {appslist:s}! No s'ha pogut analitzar la URL... L'antic cronjob s'ha guardat a {bkp_file:s}.",
"appslist_fetched": "S'ha descarregat la llista d'aplicacions {appslist:s} correctament",
"appslist_migrating": "Migrant la llista d'aplicacions {appslist:s} ...",
"appslist_migrating": "Migrant la llista d'aplicacions {appslist:s}",
"appslist_name_already_tracked": "Ja hi ha una llista d'aplicacions registrada amb el nom {name:s}.",
"appslist_removed": "S'ha eliminat la llista d'aplicacions {appslist:s}",
"appslist_retrieve_bad_format": "L'arxiu obtingut per la llista d'aplicacions {appslist:s} no és vàlid",
@ -61,10 +61,10 @@
"backup_abstract_method": "Encara no s'ha implementat aquest mètode de copia de seguretat",
"backup_action_required": "S'ha d'especificar què s'ha de guardar",
"backup_app_failed": "No s'ha pogut fer la còpia de seguretat de l'aplicació \"{app:s}\"",
"backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup...",
"backup_applying_method_copy": "Còpia de tots els fitxers a la còpia de seguretat...",
"backup_applying_method_custom": "Crida del mètode de còpia de seguretat personalitzat \"{method:s}\"...",
"backup_applying_method_tar": "Creació de l'arxiu tar de la còpia de seguretat...",
"backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup",
"backup_applying_method_copy": "Còpia de tots els fitxers a la còpia de seguretat",
"backup_applying_method_custom": "Crida del mètode de còpia de seguretat personalitzat \"{method:s}\"",
"backup_applying_method_tar": "Creació de l'arxiu tar de la còpia de seguretat",
"backup_archive_app_not_found": "L'aplicació \"{app:s}\" no es troba dins l'arxiu de la còpia de seguretat",
"backup_archive_broken_link": "No s'ha pogut accedir a l'arxiu de la còpia de seguretat (enllaç invàlid cap a {path:s})",
"backup_archive_mount_failed": "No s'ha pogut carregar l'arxiu de la còpia de seguretat",
@ -80,5 +80,134 @@
"backup_copying_to_organize_the_archive": "Copiant {size:s}MB per organitzar l'arxiu",
"backup_couldnt_bind": "No es pot lligar {src:s} amb {dest:s}.",
"backup_created": "S'ha creat la còpia de seguretat",
"backup_creating_archive": "Creant l'arxiu de la còpia de seguretat"
"backup_creating_archive": "Creant l'arxiu de la còpia de seguretat…",
"aborting": "Avortant.",
"app_not_upgraded": "Les següents aplicacions no s'han actualitzat: {apps}",
"app_start_install": "instal·lant l'aplicació {app}…",
"app_start_remove": "Eliminant l'aplicació {app}…",
"app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per {app}…",
"app_start_restore": "Recuperant l'aplicació {app}…",
"app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}",
"ask_new_domain": "Nou domini",
"ask_new_path": "Nou camí",
"backup_actually_backuping": "S'està creant un arxiu de còpia de seguretat a partir dels fitxers recuperats…",
"backup_creation_failed": "Ha fallat la creació de la còpia de seguretat",
"backup_csv_addition_failed": "No s'han pogut afegir fitxers per a fer-ne la còpia de seguretat al fitxer CSV",
"backup_csv_creation_failed": "No s'ha pogut crear el fitxer CSV necessari per a futures operacions de recuperació",
"backup_custom_backup_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"backup\"",
"backup_custom_mount_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"mount\"",
"backup_custom_need_mount_error": "El mètode de còpia de seguretat personalitzat ha fallat a l'etapa \"need_mount\"",
"backup_delete_error": "No s'ha pogut suprimir \"{path:s}\"",
"backup_deleted": "S'ha suprimit la còpia de seguretat",
"backup_extracting_archive": "Extraient l'arxiu de la còpia de seguretat…",
"backup_hook_unknown": "Script de còpia de seguretat \"{hook:s}\" desconegut",
"backup_invalid_archive": "Arxiu de còpia de seguretat no vàlid",
"backup_method_borg_finished": "La còpia de seguretat a borg ha acabat",
"backup_method_copy_finished": "La còpia de la còpia de seguretat ha acabat",
"backup_method_custom_finished": "El mètode de còpia de seguretat personalitzat \"{method:s}\" ha acabat",
"backup_method_tar_finished": "S'ha creat l'arxiu de còpia de seguretat tar",
"backup_mount_archive_for_restore": "Preparant l'arxiu per la restauració…",
"good_practices_about_user_password": "Esteu a punt de definir una nova contrasenya d'usuari. La contrasenya ha de tenir un mínim de 8 caràcters ; tot i que és de bona pràctica utilitzar una contrasenya més llarga (és a dir una frase de contrasenya) i/o utilitzar diferents tipus de caràcters (majúscules, minúscules, dígits i caràcters especials).",
"password_listed": "Aquesta contrasenya és una de les més utilitzades en el món. Si us plau utilitzeu-ne una més única.",
"password_too_simple_1": "La contrasenya ha de tenir un mínim de 8 caràcters",
"password_too_simple_2": "La contrasenya ha de tenir un mínim de 8 caràcters i ha de contenir dígits, majúscules i minúscules",
"password_too_simple_3": "La contrasenya ha de tenir un mínim de 8 caràcters i tenir dígits, majúscules, minúscules i caràcters especials",
"password_too_simple_4": "La contrasenya ha de tenir un mínim de 12 caràcters i tenir dígits, majúscules, minúscules i caràcters especials",
"backup_no_uncompress_archive_dir": "El directori de l'arxiu descomprimit no existeix",
"backup_nothings_done": "No hi ha res a guardar",
"backup_output_directory_forbidden": "Directori de sortida no permès. Les còpies de seguretat no es poden crear ni dins els directoris /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ni dins els subdirectoris /home/yunohost.backup/archives",
"backup_output_directory_not_empty": "El directori de sortida no està buit",
"backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat",
"backup_output_symlink_dir_broken": "Teniu un enllaç simbòlic trencat en lloc del directori dels arxius '{path:s}'. Pot ser teniu una configuració per la còpia de seguretat específica en un altre sistema de fitxers, si és el cas segurament heu oblidat muntar o connectar el disc dur o la clau USB.",
"backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar php7, la restauració de les vostres aplicacions pot fallar (raó: {error:s})",
"backup_running_hooks": "Executant els scripts de la còpia de seguretat…",
"backup_system_part_failed": "No s'ha pogut fer la còpia de seguretat de la part \"{part:s}\" del sistema",
"backup_unable_to_organize_files": "No s'han pogut organitzar els fitxers dins de l'arxiu amb el mètode ràpid",
"backup_with_no_backup_script_for_app": "L'aplicació {app:s} no té un script de còpia de seguretat. Serà ignorat.",
"backup_with_no_restore_script_for_app": "L'aplicació {app:s} no té un script de restauració, no podreu restaurar automàticament la còpia de seguretat d'aquesta aplicació.",
"certmanager_acme_not_configured_for_domain": "El certificat pel domini {domain:s} sembla que no està instal·lat correctament. Si us plau executeu primer cert-install per aquest domini.",
"certmanager_attempt_to_renew_nonLE_cert": "El certificat pel domini {domain:s} no ha estat emès per Let's Encrypt. No es pot renovar automàticament!",
"certmanager_attempt_to_renew_valid_cert": "El certificat pel domini {domain:s} està a punt de caducar! Utilitzeu --force per ometre",
"certmanager_attempt_to_replace_valid_cert": "Esteu intentant sobreescriure un certificat correcte i vàlid pel domini {domain:s}! (Utilitzeu --force per ometre)",
"certmanager_cannot_read_cert": "S'ha produït un error al intentar obrir el certificat actual pel domini {domain:s} (arxiu: {file:s}), raó: {reason:s}",
"certmanager_cert_install_success": "S'ha instal·lat correctament un certificat Let's Encrypt pel domini {domain:s}!",
"certmanager_cert_install_success_selfsigned": "S'ha instal·lat correctament un certificat auto-signat pel domini {domain:s}!",
"certmanager_cert_renew_success": "S'ha renovat correctament el certificat Let's Encrypt pel domini {domain:s}!",
"certmanager_cert_signing_failed": "No s'ha pogut firmar el nou certificat",
"certmanager_certificate_fetching_or_enabling_failed": "Sembla que l'activació del nou certificat per {domain:s} ha fallat…",
"certmanager_conflicting_nginx_file": "No s'ha pogut preparar el domini per al desafiament ACME: l'arxiu de configuració nginx {filepath:s} entra en conflicte i s'ha d'eliminar primer",
"certmanager_couldnt_fetch_intermediate_cert": "S'ha exhaurit el temps d'esperar al intentar recollir el certificat intermedi des de Let's Encrypt. La instal·lació/renovació del certificat s'ha cancel·lat - torneu a intentar-ho més tard.",
"certmanager_domain_cert_not_selfsigned": "El certificat pel domini {domain:s} no és auto-signat Esteu segur de voler canviar-lo? (Utilitzeu --force per fer-ho)",
"certmanager_domain_dns_ip_differs_from_public_ip": "El registre DNS \"A\" pel domini {domain:s} és diferent a l'adreça IP d'aquest servidor. Si heu modificat recentment el registre A, si us plau espereu a que es propagui (hi ha eines per verificar la propagació disponibles a internet). (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)",
"certmanager_domain_http_not_working": "Sembla que el domini {domain:s} no és accessible via HTTP. Si us plau verifiqueu que les configuracions DNS i nginx siguin correctes",
"certmanager_domain_not_resolved_locally": "El domini {domain:s} no es pot resoldre dins del vostre servidor YunoHost. Això pot passar si heu modificat recentment el registre DNS. Si és així, si us plau espereu unes hores per a que es propagui. Si el problema continua, considereu afegir {domain:s} a /etc/hosts. (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)",
"certmanager_domain_unknown": "Domini desconegut {domain:s}",
"certmanager_error_no_A_record": "No s'ha trobat cap registre DNS \"A\" per {domain:s}. Heu de fer que el vostre nom de domini apunti cap a la vostra màquina per tal de poder instal·lar un certificat Let's Encrypt! (Si sabeu el que esteu fent, podeu utilitzar --no-checks per desactivar aquestes comprovacions.)",
"certmanager_hit_rate_limit": "S'han emès massa certificats recentment per aquest mateix conjunt de dominis {domain:s}. Si us plau torneu-ho a intentar més tard. Consulteu https://letsencrypt.org/docs/rate-limits/ per obtenir més detalls",
"certmanager_http_check_timeout": "S'ha exhaurit el temps d'espera quan el servidor ha intentat contactar amb ell mateix via HTTP utilitzant la seva adreça IP pública (domini domain:s} amb IP {ip:s}). Pot ser degut a hairpinning o a que el talla focs/router al que està connectat el servidor estan mal configurats.",
"certmanager_no_cert_file": "No s'ha pogut llegir l'arxiu del certificat pel domini {domain:s} (fitxer: {file:s})",
"certmanager_self_ca_conf_file_not_found": "No s'ha trobat el fitxer de configuració per l'autoritat del certificat auto-signat (fitxer: {file:s})",
"certmanager_unable_to_parse_self_CA_name": "No s'ha pogut analitzar el nom de l'autoritat del certificat auto-signat (fitxer: {file:s})",
"confirm_app_install_warning": "Atenció: aquesta aplicació funciona però no està ben integrada amb YunoHost. Algunes característiques com la autenticació única i la còpia de seguretat/restauració poden no estar disponibles. Voleu instal·lar-la de totes maneres? [{answers:s}] ",
"confirm_app_install_danger": "ATENCIÓ! Aquesta aplicació encara és experimental (si no és que no funciona directament) i és probable que trenqui el sistema! No hauríeu d'instal·lar-la a no ser que sapigueu el que feu. Esteu segurs de voler córrer aquest risc? [{answers:s}] ",
"confirm_app_install_thirdparty": "ATENCIÓ! La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. Faci-ho sota la seva responsabilitat.No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. Esteu segurs de voler córrer aquest risc? [{answers:s}] ",
"custom_app_url_required": "Heu de especificar una URL per actualitzar la vostra aplicació personalitzada {app:s}",
"custom_appslist_name_required": "Heu d'especificar un nom per la vostra llista d'aplicacions personalitzada",
"diagnosis_debian_version_error": "No s'ha pogut obtenir la versió Debian: {error}",
"diagnosis_kernel_version_error": "No s'ha pogut obtenir la versió del nucli: {error}",
"diagnosis_monitor_disk_error": "No es poden monitorar els discs: {error}",
"diagnosis_monitor_network_error": "No es pot monitorar la xarxa: {error}",
"diagnosis_monitor_system_error": "No es pot monitorar el sistema: {error}",
"diagnosis_no_apps": "No hi ha cap aplicació instal·lada",
"admin_password_too_long": "Trieu una contrasenya de menys de 127 caràcters",
"dpkg_is_broken": "No es pot fer això en aquest instant perquè dpkg/apt (els gestors de paquets del sistema) sembla estar mal configurat... Podeu intentar solucionar-ho connectant-vos per ssh i executant \"sudo dpkg --configure -a\".",
"dnsmasq_isnt_installed": "sembla que dnsmasq no està instal·lat, executeu \"apt-get remove bind9 && apt-get install dnsmasq\"",
"domain_cannot_remove_main": "No es pot eliminar el domini principal. S'ha d'establir un nou domini primer",
"domain_cert_gen_failed": "No s'ha pogut generar el certificat",
"domain_created": "S'ha creat el domini",
"domain_creation_failed": "No s'ha pogut crear el domini",
"domain_deleted": "S'ha eliminat el domini",
"domain_deletion_failed": "No s'ha pogut eliminar el domini",
"domain_exists": "El domini ja existeix",
"app_action_cannot_be_ran_because_required_services_down": "Aquesta aplicació necessita serveis que estan aturats. Abans de continuar, hauríeu d'intentar arrancar de nou els serveis següents (i també investigar perquè estan aturats) : {services}",
"domain_dns_conf_is_just_a_recommendation": "Aquesta ordre mostra la configuració *recomanada*. En cap cas fa la configuració del DNS. És la vostra responsabilitat configurar la zona DNS en el vostre registrar en acord amb aquesta recomanació.",
"domain_dyndns_already_subscribed": "Ja us heu subscrit a un domini DynDNS",
"domain_dyndns_dynette_is_unreachable": "No s'ha pogut abastar la dynette YunoHost, o bé YunoHost no està connectat a internet correctament o bé el servidor dynette està caigut. Error: {error}",
"domain_dyndns_invalid": "Domini no vàlid per utilitzar amb DynDNS",
"domain_dyndns_root_unknown": "Domini DynDNS principal desconegut",
"domain_hostname_failed": "No s'ha pogut establir un nou nom d'amfitrió",
"domain_uninstall_app_first": "Hi ha una o més aplicacions instal·lades en aquest domini. Desinstal·leu les abans d'eliminar el domini",
"domain_unknown": "Domini desconegut",
"domain_zone_exists": "El fitxer de zona DNS ja existeix",
"domain_zone_not_found": "No s'ha trobat el fitxer de zona DNS pel domini {:s}",
"domains_available": "Dominis disponibles:",
"done": "Fet",
"downloading": "Descarregant…",
"dyndns_could_not_check_provide": "No s'ha pogut verificar si {provider:s} pot oferir {domain:s}.",
"dyndns_could_not_check_available": "No s'ha pogut verificar la disponibilitat de {domain:s} a {provider:s}.",
"dyndns_ip_update_failed": "No s'ha pogut actualitzar l'adreça IP al DynDNS",
"dyndns_ip_updated": "S'ha actualitzat l'adreça IP al DynDNS",
"dyndns_key_generating": "S'està generant la clau DNS, això pot trigar una estona…",
"dyndns_key_not_found": "No s'ha trobat la clau DNS pel domini",
"dyndns_no_domain_registered": "No hi ha cap domini registrat amb DynDNS",
"dyndns_registered": "S'ha registrat el domini DynDNS",
"dyndns_registration_failed": "No s'ha pogut registrar el domini DynDNS: {error:s}",
"dyndns_domain_not_provided": "El proveïdor {provider:s} no pot oferir el domini {domain:s}.",
"dyndns_unavailable": "El domini {domain:s} no està disponible.",
"executing_command": "Execució de l'ordre « {command:s} »…",
"executing_script": "Execució de l'script « {script:s} »…",
"extracting": "Extracció en curs…",
"dyndns_cron_installed": "S'ha instal·lat la tasca cron pel DynDNS",
"dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron pel DynDNS",
"dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS",
"experimental_feature": "Atenció: aquesta funcionalitat és experimental i no es considera estable, no s'ha d'utilitzar a excepció de saber el que esteu fent.",
"field_invalid": "Camp incorrecte « {:s} »",
"file_does_not_exist": "El camí {path:s} no existeix.",
"firewall_reload_failed": "No s'ha pogut tornar a carregar el tallafoc",
"firewall_reloaded": "S'ha tornat a carregar el tallafoc",
"firewall_rules_cmd_failed": "No s'han pogut aplicar algunes regles del tallafoc. Mireu el registre per a més informació.",
"format_datetime_short": "%d/%m/%Y %H:%M",
"global_settings_bad_choice_for_enum": "Opció pel paràmetre {setting:s} incorrecta, s'ha rebut «{choice:s}» però les opcions disponibles són: {available_choices:s}",
"global_settings_bad_type_for_setting": "El tipus del paràmetre {setting:s} és incorrecte. S'ha rebut {received_type:s}, però s'esperava {expected_type:s}",
"global_settings_cant_open_settings": "No s'ha pogut obrir el fitxer de configuració, raó: {reason:s}"
}

View file

@ -4,6 +4,8 @@
"admin_password": "Administration password",
"admin_password_change_failed": "Unable to change password",
"admin_password_changed": "The administration password has been changed",
"app_action_cannot_be_ran_because_required_services_down": "This app requires some services which are currently down. Before continuing, you should try to restart the following services (and possibly investigate why they are down) : {services}",
"admin_password_too_long": "Please choose a password shorter than 127 characters",
"app_already_installed": "{app:s} is already installed",
"app_already_installed_cant_change_url": "This app is already installed. The url cannot be changed just by this function. Look into `app changeurl` if it's available.",
"app_already_up_to_date": "{app:s} is already up to date",
@ -199,7 +201,7 @@
"firewall_reloaded": "The firewall has been reloaded",
"firewall_rules_cmd_failed": "Some firewall rules commands have failed. For more information, see the log.",
"format_datetime_short": "%m/%d/%Y %I:%M %p",
"global_settings_bad_choice_for_enum": "Bad value for setting {setting:s}, received {received_type:s}, except {expected_type:s}",
"global_settings_bad_choice_for_enum": "Bad choice for setting {setting:s}, received '{choice:s}' but available choices are : {available_choices:s}",
"global_settings_bad_type_for_setting": "Bad type for setting {setting:s}, received {received_type:s}, except {expected_type:s}",
"global_settings_cant_open_settings": "Failed to open settings file, reason: {reason:s}",
"global_settings_cant_serialize_settings": "Failed to serialize settings data, reason: {reason:s}",
@ -210,9 +212,11 @@
"global_settings_setting_example_enum": "Example enum option",
"global_settings_setting_example_int": "Example int option",
"global_settings_setting_example_string": "Example string option",
"global_settings_setting_security_nginx_compatibility": "Compatibility vs. security tradeoff for the web server nginx. Affects the ciphers (and other security-related aspects)",
"global_settings_setting_security_password_admin_strength": "Admin password strength",
"global_settings_setting_security_password_user_strength": "User password strength",
"global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/unkown_settings.json",
"global_settings_setting_security_ssh_compatibility": "Compatibility vs. security tradeoff for the SSH server. Affects the ciphers (and other security-related aspects)",
"global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discarding it and save it in /etc/yunohost/settings-unknown.json",
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration",
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it's not a type supported by the system.",
"good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters - though it is good practice to use longer password (i.e. a passphrase) and/or to use various kind of characters (uppercase, lowercase, digits and special characters).",
@ -258,7 +262,7 @@
"log_selfsigned_cert_install": "Install self signed certificate on '{}' domain",
"log_letsencrypt_cert_renew": "Renew '{}' Let's encrypt certificate",
"log_service_enable": "Enable '{}' service",
"log_service_regen_conf": "Regenerate system configurations '{}'",
"log_regen_conf": "Regenerate system configurations '{}'",
"log_user_create": "Add '{}' user",
"log_user_delete": "Delete '{}' user",
"log_user_update": "Update information of '{}' user",
@ -295,6 +299,8 @@
"migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords",
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)",
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)",
"migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services",
"migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead",
"migration_0003_backward_impossible": "The stretch migration cannot be reverted.",
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
"migration_0003_patching_sources_list": "Patching the sources.lists…",
@ -320,6 +326,7 @@
"migration_0008_dsa": " - the DSA key will be disabled. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;",
"migration_0008_warning": "If you understand those warnings and agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
"migration_0008_no_warning": "No major risk has been indentified about overriding your SSH configuration - but we can't be absolutely sure ;)! If you agree to let YunoHost override your current configuration, run the migration. Otherwise, you can also skip the migration - though it is not recommended.",
"migration_0009_not_needed": "This migration already happened somehow ? Skipping.",
"migrations_backward": "Migrating backward.",
"migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}",
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
@ -387,6 +394,21 @@
"port_available": "Port {port:d} is available",
"port_unavailable": "Port {port:d} is not available",
"recommend_to_add_first_user": "The post-install is finished but YunoHost needs at least one user to work correctly, you should add one using 'yunohost user create' or the admin interface.",
"regenconf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'",
"regenconf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'",
"regenconf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by regen-conf (category {category}) but has been kept back.",
"regenconf_file_manually_modified": "The configuration file '{conf}' has been manually modified and will not be updated",
"regenconf_file_manually_removed": "The configuration file '{conf}' has been manually removed and will not be created",
"regenconf_file_remove_failed": "Unable to remove the configuration file '{conf}'",
"regenconf_file_removed": "The configuration file '{conf}' has been removed",
"regenconf_file_updated": "The configuration file '{conf}' has been updated",
"regenconf_now_managed_by_yunohost": "The configuration file '{conf}' is now managed by YunoHost (category {category}).",
"regenconf_up_to_date": "The configuration is already up-to-date for category '{category}'",
"regenconf_updated": "The configuration has been updated for category '{category}'",
"regenconf_would_be_updated": "The configuration would have been updated for category '{category}'",
"regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…",
"regenconf_failed": "Unable to regenerate the configuration for category(s): {categories}",
"regenconf_pending_applying": "Applying pending configuration for category '{category}'…",
"restore_action_required": "You must specify something to restore",
"restore_already_installed_app": "An app is already installed with the id '{app:s}'",
"restore_app_failed": "Unable to restore the app '{app:s}'",
@ -415,18 +437,6 @@
"service_already_started": "Service '{service:s}' has already been started",
"service_already_stopped": "Service '{service:s}' has already been stopped",
"service_cmd_exec_failed": "Unable to execute command '{command:s}'",
"service_conf_file_backed_up": "The configuration file '{conf}' has been backed up to '{backup}'",
"service_conf_file_copy_failed": "Unable to copy the new configuration file '{new}' to '{conf}'",
"service_conf_file_kept_back": "The configuration file '{conf}' is expected to be deleted by service {service} but has been kept back.",
"service_conf_file_manually_modified": "The configuration file '{conf}' has been manually modified and will not be updated",
"service_conf_file_manually_removed": "The configuration file '{conf}' has been manually removed and will not be created",
"service_conf_file_remove_failed": "Unable to remove the configuration file '{conf}'",
"service_conf_file_removed": "The configuration file '{conf}' has been removed",
"service_conf_file_updated": "The configuration file '{conf}' has been updated",
"service_conf_now_managed_by_yunohost": "The configuration file '{conf}' is now managed by YunoHost.",
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
"service_conf_updated": "The configuration has been updated for service '{service}'",
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
"service_description_avahi-daemon": "allows to reach your server using yunohost.local on your local network",
"service_description_dnsmasq": "handles domain name resolution (DNS)",
"service_description_dovecot": "allows e-mail client to access/fetch email (via IMAP and POP3)",
@ -450,9 +460,7 @@
"service_enable_failed": "Unable to enable service '{service:s}'\n\nRecent service logs:{logs:s}",
"service_enabled": "The service '{service:s}' has been enabled",
"service_no_log": "No log to display for service '{service:s}'",
"service_regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for service '{service}'…",
"service_regenconf_failed": "Unable to regenerate the configuration for service(s): {services}",
"service_regenconf_pending_applying": "Applying pending configuration for service '{service}'…",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' is deprecated! Please use 'yunohost tools regen-conf' instead.",
"service_remove_failed": "Unable to remove service '{service:s}'",
"service_removed": "The service '{service:s}' has been removed",
"service_reload_failed": "Unable to reload service '{service:s}'\n\nRecent service logs:{logs:s}",
@ -479,7 +487,8 @@
"unit_unknown": "Unknown unit '{unit:s}'",
"unlimit": "No quota",
"unrestore_app": "App '{app:s}' will not be restored",
"update_cache_failed": "Unable to update APT cache",
"update_apt_cache_failed": "Unable to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}",
"update_apt_cache_warning": "Some errors happened while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines which might help to identify problematic lines : \n{sourceslist}",
"updating_apt_cache": "Fetching available upgrades for system packages…",
"upgrade_complete": "Upgrade complete",
"upgrading_packages": "Upgrading packages…",

View file

@ -16,7 +16,7 @@
"yunohost_already_installed": "YunoHost estas jam instalita",
"yunohost_ca_creation_failed": "Ne eblas krei atestan aŭtoritaton",
"yunohost_ca_creation_success": "Loka atesta aŭtoritato estas kreita.",
"yunohost_installing": "Instalata YunoHost...",
"yunohost_installing": "Instalante YunoHost…",
"service_description_glances": "monitoras sisteminformojn de via servilo",
"service_description_metronome": "mastrumas XMPP tujmesaĝilon kontojn",
"service_description_mysql": "stokas aplikaĵojn datojn (SQL datumbazo)",

View file

@ -1,13 +1,13 @@
{
"action_invalid": "Action « {action:s} » incorrecte",
"action_invalid": "Action '{action:s}' incorrecte",
"admin_password": "Mot de passe dadministration",
"admin_password_change_failed": "Impossible de changer le mot de passe",
"admin_password_changed": "Le mot de passe dadministration a été modifié",
"app_already_installed": "{app:s} est déjà installé",
"app_argument_choice_invalid": "Choix invalide pour le paramètre « {name:s} », il doit être lun de {choices:s}",
"app_argument_invalid": "Valeur invalide pour le paramètre `{name:s}` : {error:s}",
"app_argument_choice_invalid": "Choix invalide pour le paramètre '{name:s}', il doit être lun de {choices:s}",
"app_argument_invalid": "Valeur invalide pour le paramètre '{name:s}' : {error:s}",
"app_argument_missing": "Paramètre manquant « {:s} »",
"app_argument_required": "Le paramètre `{name:s}` est requis",
"app_argument_required": "Le paramètre '{name:s}' est requis",
"app_extraction_failed": "Impossible dextraire les fichiers dinstallation",
"app_id_invalid": "Identifiant dapplication invalide",
"app_incompatible": "Lapplication {app} est incompatible avec votre version de YunoHost",
@ -35,7 +35,7 @@
"appslist_retrieve_error": "Impossible de récupérer la liste dapplications distante {appslist:s} : {error:s}",
"appslist_unknown": "La liste dapplications {appslist:s} est inconnue.",
"ask_current_admin_password": "Mot de passe dadministration actuel",
"ask_email": "Adresse courriel",
"ask_email": "Adresse de courriel",
"ask_firstname": "Prénom",
"ask_lastname": "Nom",
"ask_list_to_remove": "Liste à supprimer",
@ -43,7 +43,7 @@
"ask_new_admin_password": "Nouveau mot de passe dadministration",
"ask_password": "Mot de passe",
"backup_action_required": "Vous devez préciser ce qui est à sauvegarder",
"backup_app_failed": "Impossible de sauvegarder lapplication « {app:s} »",
"backup_app_failed": "Impossible de sauvegarder lapplication '{app:s}'",
"backup_archive_app_not_found": "Lapplication '{app:s}' na pas été trouvée dans larchive de la sauvegarde",
"backup_archive_hook_not_exec": "Le script « {hook:s} » n'a pas été exécuté dans cette sauvegarde",
"backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà",
@ -60,8 +60,8 @@
"backup_invalid_archive": "Archive de sauvegarde invalide",
"backup_nothings_done": "Il ny a rien à sauvegarder",
"backup_output_directory_forbidden": "Dossier de destination interdit. Les sauvegardes ne peuvent être créées dans les sous-dossiers /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives",
"backup_output_directory_not_empty": "Le dossier de sortie nest pas vide",
"backup_output_directory_required": "Vous devez spécifier un dossier de sortie pour la sauvegarde",
"backup_output_directory_not_empty": "Le répertoire de destination n'est pas vide",
"backup_output_directory_required": "Vous devez spécifier un dossier de destination pour la sauvegarde",
"backup_running_app_script": "Lancement du script de sauvegarde de lapplication « {app:s} »...",
"backup_running_hooks": "Exécution des scripts de sauvegarde …",
"custom_app_url_required": "Vous devez spécifier une URL pour mettre à jour votre application personnalisée {app:s}",
@ -111,7 +111,7 @@
"hook_choice_invalid": "Choix incorrect : '{:s}'",
"hook_exec_failed": "Échec de lexécution du script : {path:s}",
"hook_exec_not_terminated": "Lexécution du script {path:s} ne sest pas terminée correctement",
"hook_list_by_invalid": "La propriété de tri des actions est invalide",
"hook_list_by_invalid": "Propriété invalide pour lister les actions par",
"hook_name_unknown": "Nom de l'action '{name:s}' inconnu",
"installation_complete": "Installation terminée",
"installation_failed": "Échec de linstallation",
@ -119,8 +119,8 @@
"iptables_unavailable": "Vous ne pouvez pas jouer avec iptables ici. Vous êtes soit dans un conteneur, soit votre noyau ne le prend pas en charge",
"ldap_initialized": "Lannuaire LDAP a été initialisé",
"license_undefined": "indéfinie",
"mail_alias_remove_failed": "Impossible de supprimer lalias courriel '{mail:s}'",
"mail_domain_unknown": "Le domaine '{domain:s}' du courriel est inconnu",
"mail_alias_remove_failed": "Impossible de supprimer lalias de courriel '{mail:s}'",
"mail_domain_unknown": "Le domaine « {domain:s} » du courriel est inconnu",
"mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'",
"maindomain_change_failed": "Impossible de modifier le domaine principal",
"maindomain_changed": "Le domaine principal a été modifié",
@ -136,126 +136,126 @@
"mysql_db_creation_failed": "Impossible de créer la base de données MySQL",
"mysql_db_init_failed": "Impossible dinitialiser la base de données MySQL",
"mysql_db_initialized": "La base de données MySQL a été initialisée",
"network_check_mx_ko": "Lenregistrement DNS MX nest pas précisé",
"network_check_mx_ko": "Lenregistrement DNS MX nest pas défini",
"network_check_smtp_ko": "Le trafic courriel sortant (port 25 SMTP) semble bloqué par votre réseau",
"network_check_smtp_ok": "Le trafic courriel sortant (port 25 SMTP) nest pas bloqué",
"new_domain_required": "Vous devez spécifier le nouveau domaine principal",
"no_appslist_found": "Aucune liste dapplications na été trouvée",
"no_internet_connection": "Le serveur nest pas connecté à Internet",
"no_ipv6_connectivity": "La connectivité IPv6 nest pas disponible",
"no_restore_script": "Le script de sauvegarde na pas été trouvé pour lapplication « {app:s} »",
"no_restore_script": "Le script de sauvegarde na pas été trouvé pour lapplication '{app:s}'",
"no_such_conf_file": "Le fichier {file:s} nexiste pas, il ne peut pas être copié",
"not_enough_disk_space": "Lespace disque est insuffisant sur « {path:s} »",
"package_not_installed": "Le paquet « {pkgname} » nest pas installé",
"package_unexpected_error": "Une erreur inattendue est survenue avec le paquet « {pkgname} »",
"package_unknown": "Paquet « {pkgname} » inconnu",
"not_enough_disk_space": "Lespace disque est insuffisant sur '{path:s}'",
"package_not_installed": "Le paquet '{pkgname}' nest pas installé",
"package_unexpected_error": "Une erreur inattendue s'est produite lors du traitement du paquet '{pkgname}'",
"package_unknown": "Le paquet '{pkgname}' est inconnu",
"packages_no_upgrade": "Il ny a aucun paquet à mettre à jour",
"packages_upgrade_critical_later": "Les paquets critiques ({packages:s}) seront mis à jour ultérieurement",
"packages_upgrade_failed": "Impossible de mettre à jour tous les paquets",
"path_removal_failed": "Impossible de supprimer le chemin {:s}",
"pattern_backup_archive_name": "Doit être un nom de fichier valide avec un maximum de 30 caractères, et composé uniquement de caractères alphanumériques et de tirets tels que - et _",
"pattern_domain": "Doit être un nom de domaine valide (ex : mon-domaine.org)",
"pattern_email": "Doit être une adresse courriel valide (ex. : pseudo@domain.org)",
"pattern_backup_archive_name": "Doit être un nom de fichier valide avec un maximum de 30 caractères, et composé de caractères alphanumériques et -_. uniquement",
"pattern_domain": "Doit être un nom de domaine valide (ex : mon-domaine.fr)",
"pattern_email": "Doit être une adresse de courriel valide (ex. : pseudo@domaine.fr)",
"pattern_firstname": "Doit être un prénom valide",
"pattern_lastname": "Doit être un nom valide",
"pattern_listname": "Doit être composé uniquement de caractères alphanumériques et de tirets bas",
"pattern_mailbox_quota": "Doit être une taille avec le suffixe b/k/M/G/T ou 0 pour désactiver le quota",
"pattern_listname": "Doit être composé uniquement de caractères alphanumériques et de tirets bas (aussi appelé tiret du 8 ou underscore)",
"pattern_mailbox_quota": "Doit avoir une taille suffixée avec b/k/M/G/T ou 0 pour désactiver le quota",
"pattern_password": "Doit être composé dau moins 3 caractères",
"pattern_port": "Doit être un numéro de port valide (ex. : 0-65535)",
"pattern_port_or_range": "Doit être un numéro de port valide (ex. : 0-65535) ou une gamme de ports (ex. : 100:200)",
"pattern_port": "Doit être un numéro de port valide compris entre 0 et 65535",
"pattern_port_or_range": "Doit être un numéro de port valide compris entre 0 et 65535, ou une gamme de ports (exemple : 100:200)",
"pattern_positive_number": "Doit être un nombre positif",
"pattern_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas",
"pattern_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas (aussi appelé tiret du 8 ou underscore)",
"port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version:s}",
"port_already_opened": "Le port {port:d} est déjà ouvert pour les connexions {ip_version:s}",
"port_available": "Le port {port:d} est disponible",
"port_unavailable": "Le port {port:d} nest pas disponible",
"restore_action_required": "Vous devez préciser ce qui est à restaurer",
"restore_already_installed_app": "Une application est déjà installée avec lid « {app:s} »",
"restore_app_failed": "Impossible de restaurer lapplication « {app:s} »",
"restore_already_installed_app": "Une application est déjà installée avec lidentifiant '{app:s}'",
"restore_app_failed": "Impossible de restaurer lapplication '{app:s}'",
"restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration",
"restore_complete": "Restauration terminée",
"restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]",
"restore_failed": "Impossible de restaurer le système",
"restore_hook_unavailable": "Le script de restauration « {part:s} » nest pas disponible sur votre système, et nest pas non plus dans larchive",
"restore_hook_unavailable": "Le script de restauration '{part:s}' nest pas disponible sur votre système, et ne l'est pas non plus dans larchive",
"restore_nothings_done": "Rien na été restauré",
"restore_running_app_script": "Exécution du script de restauration de l'application '{app:s}' .…",
"restore_running_hooks": "Exécution des scripts de restauration …",
"service_add_configuration": "Ajout du fichier de configuration {file:s}",
"service_add_failed": "Impossible dajouter le service « {service:s} »",
"service_added": "Le service « {service:s} » a été ajouté",
"service_already_started": "Le service « {service:s} » est déjà démarré",
"service_already_stopped": "Le service « {service:s} » est déjà arrêté",
"service_cmd_exec_failed": "Impossible dexécuter la commande « {command:s} »",
"service_conf_file_backed_up": "Le fichier de configuration « {conf} » a été sauvegardé dans « {backup} »",
"service_conf_file_copy_failed": "Impossible de copier le nouveau fichier de configuration « {new} » vers « {conf} »",
"service_conf_file_manually_modified": "Le fichier de configuration « {conf} » a été modifié manuellement et ne sera pas mis à jour",
"service_conf_file_manually_removed": "Le fichier de configuration « {conf} » a été supprimé manuellement et ne sera pas créé",
"service_add_failed": "Impossible dajouter le service '{service:s}'",
"service_added": "Le service '{service:s}' a été ajouté",
"service_already_started": "Le service '{service:s}' est déjà démarré",
"service_already_stopped": "Le service '{service:s}' est déjà arrêté",
"service_cmd_exec_failed": "Impossible dexécuter la commande '{command:s}'",
"service_conf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé dans '{backup}'",
"service_conf_file_copy_failed": "Impossible de copier le nouveau fichier de configuration '{new}' vers '{conf}'",
"service_conf_file_manually_modified": "Le fichier de configuration '{conf}' a été modifié manuellement et ne sera pas mis à jour",
"service_conf_file_manually_removed": "Le fichier de configuration '{conf}' a été supprimé manuellement et ne sera pas créé",
"service_conf_file_not_managed": "Le fichier de configuration « {conf} » n'est pas géré pour l'instant et ne sera pas mis à jour",
"service_conf_file_remove_failed": "Impossible de supprimer le fichier de configuration « {conf} »",
"service_conf_file_removed": "Le fichier de configuration « {conf} » a été supprimé",
"service_conf_file_updated": "Le fichier de configuration « {conf} » a été mis à jour",
"service_conf_up_to_date": "La configuration du service « {service} » est déjà à jour",
"service_conf_updated": "La configuration a été mise à jour pour le service « {service} »",
"service_conf_would_be_updated": "La configuration du service « {service} » aurait été mise à jour",
"service_conf_file_remove_failed": "Impossible de supprimer le fichier de configuration '{conf}'",
"service_conf_file_removed": "Le fichier de configuration '{conf}' a été supprimé",
"service_conf_file_updated": "Le fichier de configuration '{conf}' a été mis à jour",
"service_conf_up_to_date": "La configuration du service '{service}' est déjà à jour",
"service_conf_updated": "La configuration a été mise à jour pour le service '{service}'",
"service_conf_would_be_updated": "La configuration du service '{service}' aurait été mise à jour",
"service_configuration_conflict": "Le fichier {file:s} a été modifié depuis sa dernière génération. Veuillez y appliquer les modifications manuellement ou utiliser loption --force (ce qui écrasera toutes les modifications effectuées sur le fichier).",
"service_configured": "La configuration du service « {service:s} » a été générée avec succès",
"service_configured_all": "La configuration de tous les services a été générée avec succès",
"service_disable_failed": "Impossible de désactiver le service « {service:s} »\n\nJournaux récents : {logs:s}",
"service_disabled": "Le service « {service:s} » a été désactivé",
"service_enable_failed": "Impossible dactiver le service « {service:s} »\n\nJournaux récents : {logs:s}",
"service_enabled": "Le service « {service:s} » a été activé",
"service_no_log": "Aucun journal à afficher pour le service « {service:s} »",
"service_regenconf_dry_pending_applying": "Vérification des configurations en attentes qui pourraient être appliquées pour le service « {service} »…",
"service_disable_failed": "Impossible de désactiver le service '{service:s}'\n\nJournaux historisés récents : {logs:s}",
"service_disabled": "Le service '{service:s}' a été désactivé",
"service_enable_failed": "Impossible dactiver le service '{service:s}'\n\nJournaux historisés récents : {logs:s}",
"service_enabled": "Le service '{service:s}' a été activé",
"service_no_log": "Aucun journal historisé à afficher pour le service '{service:s}'",
"service_regenconf_dry_pending_applying": "Vérification des configurations en attentes qui pourraient être appliquées au le service '{service}' …",
"service_regenconf_failed": "Impossible de régénérer la configuration pour les services : {services}",
"service_regenconf_pending_applying": "Application des configurations en attentes pour le service « {service} »…",
"service_remove_failed": "Impossible denlever le service « {service:s} »",
"service_removed": "Le service « {service:s} » a été enlevé",
"service_start_failed": "Impossible de démarrer le service « {service:s} »\n\nJournaux récents : {logs:s}",
"service_started": "Le service « {service:s} » a été démarré",
"service_status_failed": "Impossible de déterminer le statut du service « {service:s} »",
"service_stop_failed": "Impossible darrêter le service « {service:s} »\n\nJournaux récents : {logs:s}",
"service_stopped": "Le service « {service:s} » a été arrêté",
"service_unknown": "Service « {service:s} » inconnu",
"service_regenconf_pending_applying": "Application des configurations en attentes pour le service '{service}' …",
"service_remove_failed": "Impossible de supprimer le service '{service:s}'",
"service_removed": "Le service '{service:s}' a été supprimé",
"service_start_failed": "Impossible de démarrer le service '{service:s}'\n\nJournaux historisés récents : {logs:s}",
"service_started": "Le service '{service:s}' a été démarré",
"service_status_failed": "Impossible de déterminer le statut du service '{service:s}'",
"service_stop_failed": "Impossible darrêter le service '{service:s}'\n\nJournaux historisés récents : {logs:s}",
"service_stopped": "Le service '{service:s}' a été arrêté",
"service_unknown": "Le service '{service:s}' est inconnu",
"services_configured": "La configuration a été générée avec succès",
"show_diff": "Voici les différences :\n{diff:s}",
"ssowat_conf_generated": "La configuration de SSOwat a été générée",
"ssowat_conf_updated": "La configuration de SSOwat a été mise à jour",
"system_upgraded": "Le système a été mis à jour",
"system_username_exists": "Le nom dutilisateur existe déjà dans les utilisateurs système",
"unbackup_app": "Lapplication « {app:s} » ne sera pas sauvegardée",
"unexpected_error": "Une erreur inattendue est survenue",
"unit_unknown": "Unité « {unit:s} » inconnue",
"system_username_exists": "Ce nom dutilisateur existe déjà dans les utilisateurs système",
"unbackup_app": "Lapplication '{app:s}' ne sera pas sauvegardée",
"unexpected_error": "Une erreur inattendue est survenue : {error}",
"unit_unknown": "L'unité '{unit:s}' est inconnue",
"unlimit": "Pas de quota",
"unrestore_app": "Lapplication « {app:s} » ne sera pas restaurée",
"update_cache_failed": "Impossible de mettre à jour le cache de lAPT",
"updating_apt_cache": "Récupération des mises à jour disponibles pour les paquets du système .…",
"unrestore_app": "Lapplication '{app:s}' ne sera pas restaurée",
"update_cache_failed": "Impossible de mettre à jour le cache de l'outil de gestion avancée des paquets (APT)",
"updating_apt_cache": "Récupération des mises à jour disponibles pour les paquets du système …",
"upgrade_complete": "Mise à jour terminée",
"upgrading_packages": "Mise à jour des paquets en cours …",
"upnp_dev_not_found": "Aucun périphérique compatible UPnP na été trouvé",
"upnp_disabled": "UPnP a été désactivé",
"upnp_enabled": "UPnP a été activé",
"upnp_port_open_failed": "Impossible douvrir les ports avec UPnP",
"upnp_port_open_failed": "Impossible douvrir les ports UPnP",
"user_created": "Lutilisateur a été créé",
"user_creation_failed": "Impossible de créer lutilisateur",
"user_deleted": "Lutilisateur a été supprimé",
"user_deletion_failed": "Impossible de supprimer lutilisateur",
"user_home_creation_failed": "Impossible de créer le dossier personnel de lutilisateur",
"user_info_failed": "Impossible de récupérer les informations de lutilisateur",
"user_unknown": "Utilisateur « {user:s} » inconnu",
"user_unknown": "L'utilisateur {user:s} est inconnu",
"user_update_failed": "Impossible de modifier lutilisateur",
"user_updated": "Lutilisateur a été modifié",
"yunohost_already_installed": "YunoHost est déjà installé",
"yunohost_ca_creation_failed": "Impossible de créer lautorité de certification",
"yunohost_configured": "YunoHost a été configuré",
"yunohost_installing": "Installation de YunoHost en cours …",
"yunohost_not_installed": "YunoHost nest pas ou pas correctement installé. Veuillez exécuter « yunohost tools postinstall »",
"yunohost_installing": "L'installation de YunoHost est en cours …",
"yunohost_not_installed": "YunoHost nest pas ou pas correctement installé. Veuillez exécuter 'yunohost tools postinstall'",
"certmanager_attempt_to_replace_valid_cert": "Vous êtes en train de vouloir remplacer un certificat correct et valide pour le domaine {domain:s} ! (Utilisez --force pour contourner cela)",
"certmanager_domain_unknown": "Domaine {domain:s} inconnu",
"certmanager_domain_cert_not_selfsigned": "Le certificat du domaine {domain:s} nest pas auto-signé. Voulez-vous vraiment le remplacer ? (Utilisez --force pour cela)",
"certmanager_certificate_fetching_or_enabling_failed": "Il semble que lactivation du nouveau certificat pour {domain:s} a échoué …",
"certmanager_attempt_to_renew_nonLE_cert": "Le certificat pour le domaine {domain:s} nest pas émis par Lets Encrypt. Impossible de le renouveler automatiquement !",
"certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain:s} est sur le point dexpirer ! Utilisez --force pour contourner cela",
"certmanager_domain_http_not_working": "Il semble que le domaine {domain:s} nest pas accessible via HTTP. Veuillez vérifier que vos configuration DNS et Nginx sont correctes",
"certmanager_domain_http_not_working": "Il semble que le domaine {domain:s} ne soit pas accessible via HTTP. Veuillez vérifier que vos configuration DNS et Nginx sont correctes",
"certmanager_error_no_A_record": "Aucun enregistrement DNS 'A' na été trouvé pour {domain:s}. Vous devez faire pointer votre nom de domaine vers votre machine pour être en mesure dinstaller un certificat Lets Encrypt ! (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)",
"certmanager_domain_dns_ip_differs_from_public_ip": "Lenregistrement DNS 'A' du domaine {domain:s} est différent de ladresse IP de ce serveur. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (quelques vérificateur de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)",
"certmanager_cannot_read_cert": "Quelque chose sest mal passé lors de la tentative douverture du certificat actuel pour le domaine {domain:s} (fichier : {file:s}), la cause est : {reason:s}",
@ -264,22 +264,22 @@
"certmanager_cert_renew_success": "Renouvellement avec succès dun certificat Lets Encrypt pour le domaine {domain:s} !",
"certmanager_old_letsencrypt_app_detected": "\nYunoHost a détecté que lapplication « letsencrypt » est installé, ce qui est en conflit avec les nouvelles fonctionnalités de gestion intégrée de certificats dans YunoHost. Si vous souhaitez utiliser ces nouvelles fonctionnalités intégrées, veuillez lancer les commandes suivantes pour migrer votre installation :\n\n yunohost app remove letsencrypt\n yunohost domain cert-install\n\nN.B. : cela tentera de réinstaller les certificats de tous les domaines avec un certificat Let's Encrypt ou ceux auto-signés",
"certmanager_cert_signing_failed": "La signature du nouveau certificat a échoué",
"certmanager_no_cert_file": "Impossible de lire le fichier de certificat pour le domaine {domain:s} (fichier : {file:s})",
"certmanager_no_cert_file": "Impossible de lire le fichier du certificat pour le domaine {domain:s} (fichier : {file:s})",
"certmanager_conflicting_nginx_file": "Impossible de préparer le domaine pour le défi ACME : le fichier de configuration Nginx {filepath:s} est en conflit et doit être préalablement retiré",
"certmanager_hit_rate_limit": "Trop de certificats ont déjà été émis récemment pour ce même ensemble de domaines {domain:s}. Veuillez réessayer plus tard. Lisez https://letsencrypt.org/docs/rate-limits/ pour obtenir plus de détails sur les ratios et limitations",
"ldap_init_failed_to_create_admin": "Linitialisation de LDAP na pas réussi à créer lutilisateur admin",
"ldap_init_failed_to_create_admin": "Linitialisation de l'annuaire LDAP na pas réussi à créer lutilisateur admin",
"ssowat_persistent_conf_read_error": "Erreur lors de la lecture de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"ssowat_persistent_conf_write_error": "Erreur lors de la sauvegarde de la configuration persistante de SSOwat : {error:s}. Modifiez le fichier /etc/ssowat/conf.json.persistent pour réparer la syntaxe JSON",
"domain_cannot_remove_main": "Impossible de supprimer le domaine principal. Commencez par définir un nouveau domaine principal",
"domain_cannot_remove_main": "Impossible de supprimer le domaine principal. Définissez d'abord un nouveau domaine principal",
"certmanager_self_ca_conf_file_not_found": "Le fichier de configuration pour lautorité du certificat auto-signé est introuvable (fichier : {file:s})",
"certmanager_unable_to_parse_self_CA_name": "Impossible danalyser le nom de lautorité du certificat auto-signé (fichier : {file:s})",
"mailbox_used_space_dovecot_down": "Le service mail Dovecot doit être démarré, si vous souhaitez voir lespace disque occupé par la messagerie",
"mailbox_used_space_dovecot_down": "Le service de courriel Dovecot doit être démarré, si vous souhaitez voir lespace disque occupé par la messagerie",
"domains_available": "Domaines disponibles :",
"backup_archive_broken_link": "Impossible daccéder à larchive de sauvegarde (lien invalide vers {path:s})",
"certmanager_acme_not_configured_for_domain": "Le certificat du domaine {domain:s} ne semble pas être correctement installé. Veuillez d'abord exécuter cert-install.",
"certmanager_domain_not_resolved_locally": "Le domaine {domain:s} ne peut être résolu depuis votre serveur YunoHost. Cela peut se produire si vous avez récemment modifié votre enregistrement DNS. Si c'est le cas, merci dattendre quelques heures quil se propage. Si le problème persiste, envisager dajouter {domain:s} au fichier /etc/hosts. (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces vérifications.)",
"certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant l'adresse IP public {ip:s} du domaine {domain:s}. Vous rencontrez peut-être un problème dhairpinning ou alors le pare-feu/routeur en amont de votre serveur est mal configuré.",
"certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Lets Encrypt. Linstallation ou le renouvellement du certificat a été annulé - veuillez réessayer plus tard.",
"certmanager_couldnt_fetch_intermediate_cert": "Expiration du délai lors de la tentative de récupération du certificat intermédiaire depuis Lets Encrypt. Linstallation ou le renouvellement du certificat a été annulé. Veuillez réessayer plus tard.",
"appslist_retrieve_bad_format": "Le fichier récupéré pour la liste dapplications {appslist:s} nest pas valide",
"domain_hostname_failed": "Échec de la création dun nouveau nom dhôte",
"yunohost_ca_creation_success": "Lautorité de certification locale a été créée.",
@ -288,39 +288,39 @@
"appslist_migrating": "Migration de la liste dapplications {appslist:s} …",
"appslist_could_not_migrate": "Impossible de migrer la liste {appslist:s} ! Impossible dexploiter lURL. Lancienne tâche programmée a été conservée dans {bkp_file:s}.",
"appslist_corrupted_json": "Impossible de charger la liste dapplications. Il semble que {filename:s} soit corrompu.",
"app_already_installed_cant_change_url": "Cette application est déjà installée. LURL ne peut pas être changé simplement par cette fonction. Regardez avec `app changeurl` si cest disponible.",
"app_already_installed_cant_change_url": "Cette application est déjà installée. LURL ne peut pas être changé simplement par cette fonction. Regardez si cela est disponible avec `app changeurl`.",
"app_change_no_change_url_script": "Lapplication {app_name:s} ne prend pas encore en charge le changement dURL, vous pourriez avoir besoin de la mettre à jour.",
"app_change_url_failed_nginx_reload": "Le redémarrage de nginx a échoué. Voici la sortie de `nginx -t` :\n{nginx_errors:s}",
"app_change_url_identical_domains": "Lancien et le nouveau couple domaine/chemin_de_l'URL sont identiques pour (`{domain:s}{path:s}`), rien à faire.",
"app_change_url_no_script": "Lapplication `{app_name:s}` ne prend pas encore en charge le changement dURL. Vous devriez peut-être la mettre à jour.",
"app_change_url_failed_nginx_reload": "Le redémarrage de Nginx a échoué. Voici la sortie de 'nginx -t' :\n{nginx_errors:s}",
"app_change_url_identical_domains": "Lancien et le nouveau couple domaine/chemin_de_l'URL sont identiques pour ('{domain:s}{path:s}'), rien à faire.",
"app_change_url_no_script": "Lapplication '{app_name:s}' ne prend pas encore en charge le changement dURL. Vous devriez peut-être la mettre à jour.",
"app_change_url_success": "LURL de lapplication {app:s} a été changée en {domain:s}{path:s}",
"app_location_unavailable": "Cette URL nest pas disponible ou est en conflit avec une application existante\n{apps:s}",
"app_location_unavailable": "Cette URL nest pas disponible ou est en conflit avec une application existante :\n{apps:s}",
"app_already_up_to_date": "{app:s} est déjà à jour",
"invalid_url_format": "Format dURL non valide",
"global_settings_bad_choice_for_enum": "La valeur du paramètre {setting:s} est incorrecte. Reçu : {received_type:s} mais attendu : {expected_type:s}",
"global_settings_bad_type_for_setting": "Le type du paramètre {setting:s} est incorrect. Reçu : {received_type:s} mais attendu : {expected_type:s}",
"global_settings_bad_choice_for_enum": "Valeur du paramètre {setting:s} incorrecte. Reçu : {received_type:s}, mais les valeurs possibles sont : {expected_type:s}",
"global_settings_bad_type_for_setting": "Le type du paramètre {setting:s} est incorrect. Reçu {received_type:s} alors que {expected_type:s} était attendu",
"global_settings_cant_open_settings": "Échec de louverture du ficher de configurations car : {reason:s}",
"global_settings_cant_serialize_setings": "Échec de sérialisation des données de configurations, cause : {reason:s}",
"global_settings_cant_write_settings": "Échec décriture du fichier de configurations car : {reason:s}",
"global_settings_key_doesnt_exists": "La clef '{settings_key:s}' nexiste pas dans les configurations générales, vous pouvez voir toutes les clefs disponibles en saisissant 'yunohost settings list'",
"global_settings_reset_success": "Réussite ! Vos configurations précédentes ont été sauvegardées dans {path:s}",
"global_settings_reset_success": "Super ! Vos configurations précédentes ont été sauvegardées dans {path:s}",
"global_settings_setting_example_bool": "Exemple doption booléenne",
"global_settings_setting_example_int": "Exemple doption de type entier",
"global_settings_setting_example_string": "Exemple doption de type chaîne",
"global_settings_setting_example_enum": "Exemple doption de type énumération",
"global_settings_unknown_type": "Situation inattendue, la configuration {setting:s} semble avoir le type {unknown_type:s} mais celui-ci n'est pas pris en charge par le système.",
"global_settings_unknown_setting_from_settings_file": "Clef inconnue dans les paramètres : '{setting_key:s}', rejet de cette clef et sauvegarde de celle-ci dans /etc/yunohost/unkown_settings.json",
"global_settings_unknown_type": "Situation inattendue : la configuration {setting:s} semble avoir le type {unknown_type:s} mais celui-ci n'est pas pris en charge par le système.",
"global_settings_unknown_setting_from_settings_file": "Clé inconnue dans les paramètres : '{setting_key:s}', rejet de cette clé et sauvegarde de celle-ci dans /etc/yunohost/unkown_settings.json",
"service_conf_new_managed_file": "Le fichier de configuration « {conf} » est désormais géré par le service {service}.",
"service_conf_file_kept_back": "Le fichier de configuration « {conf} » devrait être supprimé par le service {service} mais a été conservé.",
"service_conf_file_kept_back": "Le fichier de configuration '{conf}' devait être supprimé par le service {service} mais a été conservé.",
"backup_abstract_method": "Cette méthode de sauvegarde na pas encore été implémentée",
"backup_applying_method_tar": "Création de larchive tar de la sauvegarde …",
"backup_applying_method_copy": "Copie de tous les fichiers à sauvegarder …",
"backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans de référentiel borg-backup …",
"backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire borg-backup…",
"backup_applying_method_custom": "Appel de la méthode de sauvegarde personnalisée '{method:s}' …",
"backup_archive_system_part_not_available": "La partie '{part:s}' du système nest pas disponible dans cette sauvegarde",
"backup_archive_mount_failed": "Le montage de larchive de sauvegarde a échoué",
"backup_archive_writing_error": "Impossible d'ajouter des fichiers '{source:s}' (nommés dans l'archive : '{dest:s}') à sauvegarder dans l'archive compressée '{archive:s}'",
"backup_ask_for_copying_if_needed": "Certains fichiers nont pas pu être préparés pour être sauvegardés en utilisant la méthode qui évite temporairement de gaspiller de lespace sur le système. Pour mener la sauvegarde, {size:s} Mo doivent être temporairement utilisés. Acceptez-vous ?",
"backup_ask_for_copying_if_needed": "Certains fichiers nont pas pu être préparés pour être sauvegardés en utilisant la méthode qui évite temporairement de gaspiller de lespace sur le système. Pour réaliser la sauvegarde, {size:s} Mo doivent être temporairement utilisés. Acceptez-vous ?",
"backup_borg_not_implemented": "La méthode de sauvegarde Borg nest pas encore implémentée",
"backup_cant_mount_uncompress_archive": "Impossible de monter en lecture seule le dossier de larchive décompressée",
"backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser larchive",
@ -340,67 +340,67 @@
"backup_with_no_restore_script_for_app": "Lapplication {app:s} na pas de script de restauration, vous ne pourrez pas restaurer automatiquement la sauvegarde de cette application.",
"global_settings_cant_serialize_settings": "Échec de la sérialisation des données de paramétrage car : {reason:s}",
"restore_removing_tmp_dir_failed": "Impossible de sauvegarder un ancien dossier temporaire",
"restore_extracting": "Extraction des fichiers nécessaires depuis larchive…",
"restore_mounting_archive": "Montage de larchive dans « {path:s} »",
"restore_may_be_not_enough_disk_space": "Votre système semble ne pas avoir suffisamment despace disponible (libre : {free_space:d} octets, nécessaire : {needed_space:d} octets, marge de sécurité : {margin:d} octets)",
"restore_not_enough_disk_space": "Espace disponible insuffisant (libre : {free_space:d} octets, nécessaire : {needed_space:d} octets, marge de sécurité : {margin:d} octets)",
"restore_system_part_failed": "Impossible de restaurer la partie « {part:s} » du système",
"restore_extracting": "Extraction des fichiers nécessaires depuis larchive …",
"restore_mounting_archive": "Montage de larchive dans '{path:s}'",
"restore_may_be_not_enough_disk_space": "Votre système semble ne pas avoir suffisamment despace disponible (L'espace libre est de {free_space:d} octets. Le besoin d'espace nécessaire est de {needed_space:d} octets. En appliquant une marge de sécurité, la quantité d'espace nécessaire est de {margin:d} octets)",
"restore_not_enough_disk_space": "Espace disponible insuffisant (L'espace libre est de {free_space:d} octets. Le besoin d'espace nécessaire est de {needed_space:d} octets. En appliquant une marge de sécurité, la quantité d'espace nécessaire est de {margin:d} octets)",
"restore_system_part_failed": "Impossible de restaurer la partie '{part:s}' du système",
"backup_couldnt_bind": "Impossible de lier {src:s} avec {dest:s}.",
"domain_dns_conf_is_just_a_recommendation": "Cette page montre la configuration *recommandée*. Elle ne configure *pas* le DNS pour vous. Il est de votre responsabilité que de configurer votre zone DNS chez votre registrar DNS avec cette recommandation.",
"domain_dyndns_dynette_is_unreachable": "Impossible de contacter la dynette YunoHost, soit YunoHost nest pas correctement connecté à internet ou alors le serveur de dynette est arrêté. Erreur : {error}",
"domain_dns_conf_is_just_a_recommendation": "Cette page montre la configuration *recommandée*. Elle ne configure *pas* le DNS pour vous. Il est de votre responsabilité que de configurer votre zone DNS chez votre fournisseur/registrar DNS avec cette recommandation.",
"domain_dyndns_dynette_is_unreachable": "Impossible de contacter la dynette YunoHost. Soit YunoHost nest pas correctement connecté à internet, soit le serveur de dynette est en panne. Erreur : {error}",
"migrations_backward": "Migration en arrière.",
"migrations_bad_value_for_target": "Nombre invalide pour le paramètre « target », les numéros de migration sont ou {}",
"migrations_bad_value_for_target": "Nombre invalide pour le paramètre target, les numéros de migration sont 0 ou {}",
"migrations_cant_reach_migration_file": "Impossible daccéder aux fichiers de migrations avec le chemin %s",
"migrations_current_target": "La cible de migration est {}",
"migrations_error_failed_to_load_migration": "ERREUR : échec du chargement de migration {number} {name}",
"migrations_forward": "Migration en avant",
"migrations_loading_migration": "Chargement de la migration {number} {name}…",
"migrations_migration_has_failed": "La migration {number} {name} a échoué avec lexception {exception}, annulation",
"migrations_loading_migration": "Chargement de la migration {number} {name} …",
"migrations_migration_has_failed": "La migration {number} {name} a échoué avec lexception {exception} : annulation",
"migrations_no_migrations_to_run": "Aucune migration à lancer",
"migrations_show_currently_running_migration": "Application de la migration {number} {name}…",
"migrations_show_currently_running_migration": "Application de la migration {number} {name} …",
"migrations_show_last_migration": "La dernière migration appliquée est {}",
"migrations_skip_migration": "Omission de la migration {number} {name}…",
"server_shutdown": "Le serveur sera éteint",
"server_shutdown_confirm": "Le serveur immédiatement être éteint, le voulez-vous vraiment ? [{answers:s}]",
"migrations_skip_migration": "Ignorer et passer la migration {number} {name}…",
"server_shutdown": "Le serveur va éteindre",
"server_shutdown_confirm": "Le serveur va être éteint immédiatement, le voulez-vous vraiment ? [{answers:s}]",
"server_reboot": "Le serveur va redémarrer",
"server_reboot_confirm": "Le serveur va redémarrer immédiatement, le voulez-vous vraiment ? [{answers:s}]",
"app_upgrade_some_app_failed": "Impossible de mettre à jour certaines applications",
"ask_path": "Chemin",
"dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.",
"dyndns_domain_not_provided": "Le fournisseur DynDNS {provider:s} ne peut pas fournir le domaine {domain:s}.",
"app_make_default_location_already_used": "Impossible de configurer lapplication '{app}' par défaut pour le domaine {domain} car déjà utilisé par l'application '{other_app}'",
"app_make_default_location_already_used": "Impossible de configurer lapplication '{app}' par défaut pour le domaine {domain} car il est déjà utilisé par l'application '{other_app}'",
"app_upgrade_app_name": "Mise à jour de lapplication {app} …",
"backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier darchives '{path:s}'. Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas, vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clef USB.",
"backup_output_symlink_dir_broken": "Vous avez un lien symbolique cassé à la place de votre dossier darchives « {path:s} ». Vous pourriez avoir une configuration personnalisée pour sauvegarder vos données sur un autre système de fichiers, dans ce cas, vous avez probablement oublié de monter ou de connecter votre disque dur ou votre clé USB.",
"migrate_tsig_end": "La migration à hmac-sha512 est terminée",
"migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué, annulation des modifications. Erreur : {error_code} - {error}",
"migrate_tsig_start": "Lalgorithme de génération des clefs nest pas suffisamment sécurisé pour la signature TSIG du domaine « {domain} », lancement de la migration vers hmac-sha512 qui est plus sécurisé",
"migrate_tsig_failed": "La migration du domaine DynDNS {domain} à hmac-sha512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}",
"migrate_tsig_start": "Lalgorithme de génération des clefs nest pas suffisamment sécurisé pour la signature TSIG du domaine '{domain}', lancement de la migration vers hmac-sha512 qui est plus sécurisé",
"migrate_tsig_wait": "Attendre 3 minutes pour que le serveur DynDNS prenne en compte la nouvelle clef …",
"migrate_tsig_wait_2": "2 minutes …",
"migrate_tsig_wait_3": "1 minute …",
"migrate_tsig_wait_4": "30 secondes …",
"migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine DynDNS, donc aucune migration nest nécessaire !",
"app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' est obsolète ! Utilisez 'app register-url' en remplacement !",
"migration_description_0001_change_cert_group_to_sslcert": "Change les permissions de groupe des certificats de 'metronome' à 'ssl-cert'",
"migration_description_0002_migrate_to_tsig_sha256": "Améliore la sécurité de DynDNS TSIG en utilisant SHA512 au lieu de MD5",
"migration_description_0001_change_cert_group_to_sslcert": "Changement des permissions de groupe des certificats de « metronome » à « ssl-cert »",
"migration_description_0002_migrate_to_tsig_sha256": "Amélioration de la sécurité de DynDNS TSIG en utilisant SHA512 au lieu de MD5",
"migration_description_0003_migrate_to_stretch": "Mise à niveau du système vers Debian Stretch et YunoHost 3.0",
"migration_0003_backward_impossible": "La migration Stretch nest pas réversible.",
"migration_0003_start": "Démarrage de la migration vers Stretch. Les journaux seront disponibles dans {logfile}.",
"migration_0003_patching_sources_list": "Modification de sources.lists …",
"migration_0003_patching_sources_list": "Modification du fichier sources.lists …",
"migration_0003_main_upgrade": "Démarrage de la mise à niveau principale …",
"migration_0003_fail2ban_upgrade": "Démarrage de la mise à niveau de fail2ban …",
"migration_0003_restoring_origin_nginx_conf": "Votre fichier /etc/nginx/nginx.conf a été modifié dune manière ou dune autre. La migration va dabords le réinitialiser à son état initial. Le fichier précédent sera disponible en tant que {backup_dest}.",
"migration_0003_yunohost_upgrade": "Démarrage de la mise à niveau du paquet YunoHost. La migration se terminera, mais la mise à jour réelle aura lieu immédiatement après. Après cette opération terminée, vous pourriez avoir à vous reconnecter à ladministration via le panel web.",
"migration_0003_yunohost_upgrade": "Démarrage de la mise à niveau du paquet YunoHost. La migration se terminera, mais la mise à jour réelle aura lieu immédiatement après. Une fois cette opération terminée, vous pourriez avoir à vous reconnecter à ladministration via le panel web.",
"migration_0003_not_jessie": "La distribution Debian actuelle nest pas Jessie !",
"migration_0003_system_not_fully_up_to_date": "Votre système nest pas complètement à jour. Veuillez mener une mise à jour classique avant de lancer à migration à Stretch.",
"migration_0003_still_on_jessie_after_main_upgrade": "Quelque chose sest ma passé pendant la mise à niveau principale : le système est toujours sur Jessie ?!? Pour investiguer le problème, veuillez regarder les journaux {log} 🙁…",
"migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si léquipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus dinformations sur https://yunohost.org/backup ;\n - dêtre patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusquà quelques heures pour que tout soit à niveau.\n\nDe plus, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). Lancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence !",
"migration_0003_problematic_apps_warning": "Veuillez noter que les applications suivantes, éventuellement problématiques, ont été détectées. Il semble quelles naient pas été installées depuis une liste dapplication ou quelles ne soit pas marquées «working ». En conséquence, nous ne pouvons pas garantir quelles fonctionneront après la mise à niveau : {problematic_apps}",
"migration_0003_still_on_jessie_after_main_upgrade": "Quelque chose sest mal passé pendant la mise à niveau principale : le système est toujours sur Jessie !? Pour investiguer sur problème, veuillez regarder les journaux {log}:s…",
"migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si léquipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques. Plus dinformations sur https://yunohost.org/backup ;\n - dêtre patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusquà quelques heures pour que tout soit à niveau.\n\nEn outre, le port SMTP utilisé par les clients de messagerie externes comme (Thunderbird ou K9-Mail) a été changé de 465 (SSL/TLS) à 587 (STARTTLS). Lancien port 465 sera automatiquement fermé et le nouveau port 587 sera ouvert dans le pare-feu. Vous et vos utilisateurs *devront* adapter la configuration de vos clients de messagerie en conséquence !",
"migration_0003_problematic_apps_warning": "Veuillez noter que des applications possiblement problématiques ont été détectées. Il semble quelles naient pas été installées depuis une liste dapplication ou quelles ne soit pas marquées comme « fonctionnelles ». En conséquence, nous ne pouvons pas garantir quelles fonctionneront après la mise à niveau : {problematic_apps}",
"migration_0003_modified_files": "Veuillez noter que les fichiers suivants ont été détectés comme modifiés manuellement et pourraient être écrasés à la fin de la mise à niveau : {manually_modified_files}",
"migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.",
"migrations_to_be_ran_manually": "La migration {number} {name} doit être lancée manuellement. Veuillez aller dans Outils > Migration dans linterface admin, ou lancer `yunohost tools migrations migrate`.",
"migrations_to_be_ran_manually": "La migration {number} {name} doit être lancée manuellement. Veuillez aller dans Outils > Migrations dans linterface admin, ou lancer `yunohost tools migrations migrate`.",
"migrations_need_to_accept_disclaimer": "Pour lancer la migration {number} {name}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec loption --accept-disclaimer.",
"service_description_avahi-daemon": "permet datteindre votre serveur via yunohost.local sur votre réseau local",
"service_description_dnsmasq": "assure la résolution des noms de domaine (DNS)",
"service_description_dnsmasq": "re la résolution des noms de domaine (DNS)",
"service_description_dovecot": "permet aux clients de messagerie daccéder/récupérer les courriels (via IMAP et POP3)",
"service_description_fail2ban": "protège contre les attaques brute-force et autres types dattaques venant dInternet",
"service_description_glances": "surveille les informations système de votre serveur",
@ -410,43 +410,43 @@
"service_description_nslcd": "gère la connexion en ligne de commande des utilisateurs YunoHost",
"service_description_php5-fpm": "exécute des applications écrites en PHP avec nginx",
"service_description_postfix": "utilisé pour envoyer et recevoir des courriels",
"service_description_redis-server": "une base de donnée spécialisée utilisée pour laccès rapide aux données, les files dattentes et la communication inter-programmes",
"service_description_redis-server": "une base de données spécialisée utilisée pour laccès rapide aux données, les files dattentes et la communication entre les programmes",
"service_description_rmilter": "vérifie divers paramètres dans les courriels",
"service_description_rspamd": "filtre le pourriel, et dautres fonctionnalités liées au courriel",
"service_description_slapd": "stocke les utilisateurs, domaines et leurs informations liées",
"service_description_ssh": "vous permet de vous connecter à distance à votre serveur via un terminal (protocole SSH)",
"service_description_yunohost-api": "permet les interactions entre linterface web de YunoHost et le système",
"service_description_yunohost-firewall": "gère les ports de connexion ouverts et fermés aux services",
"service_description_yunohost-firewall": "gère l'ouverture et la fermeture des ports de connexion aux services",
"experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas lutiliser à moins que vous ne sachiez ce que vous faites.",
"log_corrupted_md_file": "Le fichier yaml de metadata associé aux logs est corrompu : {md_file}",
"log_corrupted_md_file": "Le fichier yaml de metadata associé aux logs est corrompu : '{md_file}'",
"log_category_404": "Le journal de la catégorie '{category}' nexiste pas",
"log_link_to_log": "Log complet de cette opération : '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\"> {desc} </a>'",
"log_help_to_get_log": "Pour voir le log de cette opération '{desc}', utiliser la commande 'yunohost log display {name}'",
"log_link_to_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci <a href=\"#/tools/logs/{name}\"> de fournir le log complet de lopération en cliquant ici</a>",
"log_link_to_log": "Journal historisé complet de cette opération : '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\"> {desc} </a>'",
"log_help_to_get_log": "Pour voir le journal historisé de cette opération '{desc}', utilisez la commande 'yunohost log display {name}'",
"log_link_to_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci <a href=\"#/tools/logs/{name}\"> de fournir le journal historisé complet de lopération en cliquant ici</a>",
"backup_php5_to_php7_migration_may_fail": "Impossible de convertir votre archive pour prendre en charge php7, vos applications php pourraient ne pas être restaurées (reason: {error:s})",
"log_help_to_get_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci de partager le log de cette opération en utilisant la commande 'yunohost log display {name} --share'",
"log_does_exists": "Il nexiste pas de log de lopération ayant pour nom '{log}', utiliser 'yunohost log list pour voir tous les fichiers de logs disponibles'",
"log_help_to_get_failed_log": "Lopération '{desc}' a échouée ! Pour avoir de laide, merci de partager le journal historisé de cette opération en utilisant la commande 'yunohost log display {name} --share'",
"log_does_exists": "Il nexiste pas de journal historisé de lopération ayant pour nom '{log}', utiliser 'yunohost log list pour voir tous les fichiers de journaux historisés disponibles'",
"log_operation_unit_unclosed_properly": "Lopération ne sest pas terminée correctement",
"log_app_addaccess": "Ajouter laccès à '{}'",
"log_app_removeaccess": "Enlever laccès à '{}'",
"log_app_addaccess": "Ajouter laccès à '{}'",
"log_app_removeaccess": "Enlever laccès à '{}'",
"log_app_clearaccess": "Retirer tous les accès à '{}'",
"log_app_fetchlist": "Ajouter une liste dapplication",
"log_app_removelist": "Enlever une liste dapplication",
"log_app_change_url": "Changer lurl de lapplication '{}'",
"log_app_change_url": "Changer lURL de lapplication '{}'",
"log_app_install": "Installer lapplication '{}'",
"log_app_remove": "Enlever lapplication '{}'",
"log_app_upgrade": "Mettre à jour lapplication '{}'",
"log_app_makedefault": "Faire de '{}' lapplication par défaut",
"log_available_on_yunopaste": "Le log est désormais disponible via {url}",
"log_app_makedefault": "Faire de '{}' lapplication par défaut",
"log_available_on_yunopaste": "Le journal historisé est désormais disponible via {url}",
"log_backup_restore_system": "Restaurer le système depuis une archive de sauvegarde",
"log_backup_restore_app": "Restaurer '{}' depuis une sauvegarde",
"log_remove_on_failed_restore": "Retirer '{}' après la restauration depuis une sauvegarde qui a échouée",
"log_remove_on_failed_restore": "Retirer '{}' après un échec de restauration depuis une archive de sauvegarde",
"log_remove_on_failed_install": "Enlever '{}' après une installation échouée",
"log_domain_add": "Ajouter le domaine '{}' dans la configuration du système",
"log_domain_remove": "Enlever le domaine '{}' de la configuration du système",
"log_dyndns_subscribe": "Souscrire au sous-domaine YunoHost '{}'",
"log_dyndns_update": "Mettre à jour ladresse IP associée à votre sous-domaine YunoHost '{}'",
"log_letsencrypt_cert_install": "Installer le certificat Lets Encrypt sur le domaine '{}'",
"log_letsencrypt_cert_install": "Installer le certificat Lets Encrypt sur le domaine '{}'",
"log_selfsigned_cert_install": "Installer le certificat auto-signé sur le domaine '{}'",
"log_letsencrypt_cert_renew": "Renouveler le certificat Lets Encrypt de '{}'",
"log_service_enable": "Activer le service '{}'",
@ -461,27 +461,27 @@
"log_tools_upgrade": "Mise à jour des paquets Debian",
"log_tools_shutdown": "Éteindre votre serveur",
"log_tools_reboot": "Redémarrer votre serveur",
"mail_unavailable": "Cette adresse mail est réservée et doit être automatiquement attribuée au tout premier utilisateur",
"migration_description_0004_php5_to_php7_pools": "Reconfigurez le pool PHP pour utiliser PHP 7 au lieu de 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de postgresql 9.4 vers 9.6",
"migration_0005_postgresql_94_not_installed": "Postgresql na pas été installé sur votre système. Rien à faire !",
"migration_0005_postgresql_96_not_installed": "Postgresql 9.4 a été trouvé et installé, mais pas Postgresql 9.6 !? Quelque chose détrange a dû arriver à votre système :( …",
"mail_unavailable": "Cette adresse de courriel est réservée et doit être automatiquement attribuée au tout premier utilisateur",
"migration_description_0004_php5_to_php7_pools": "Reconfiguration des pools PHP pour utiliser PHP 7 au lieu de PHP 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migration des bases de données de PostgreSQL 9.4 vers PostgreSQL 9.6",
"migration_0005_postgresql_94_not_installed": "PostgreSQL na pas été installé sur votre système. Rien à faire !",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 a été trouvé et installé, mais pas PostgreSQL 9.6 !? Quelque chose détrange a dû arriver à votre système :( …",
"migration_0005_not_enough_space": "Il ny a pas assez despace libre de disponible sur {path} pour lancer maintenant la migration :(.",
"recommend_to_add_first_user": "La post-installation est terminée, mais YunoHost a besoin dau moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant « yunohost user create » ou linterface dadministration.",
"service_description_php7.0-fpm": "exécute des applications écrites en PHP avec nginx",
"recommend_to_add_first_user": "La post-installation est terminée. YunoHost a besoin dau moins un utilisateur pour fonctionner correctement. Vous devez en ajouter un en utilisant 'yunohost user create' ou bien via linterface dadministration web.",
"service_description_php7.0-fpm": "exécute des applications écrites en PHP avec Nginx",
"users_available": "Liste des utilisateurs disponibles :",
"good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe dadministration. Le mot de passe doit comporter au moins 8 caractères bien quil soit recommandé dutiliser un mot de passe plus long (cest-à-dire une phrase de chiffrement) et/ou dutiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
"good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères - bien qu'il soit recommandé d'utiliser un mot de passe plus long (c'est-à-dire une phrase secrète) et/ou d'utiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
"good_practices_about_admin_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe dadministration. Le mot de passe doit comporter au moins 8 caractères bien quil soit recommandé dutiliser un mot de passe plus long (cest-à-dire une phrase secrète) et/ou dutiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
"good_practices_about_user_password": "Vous êtes maintenant sur le point de définir un nouveau mot de passe utilisateur. Le mot de passe doit comporter au moins 8 caractères — bien quil soit recommandé dutiliser un mot de passe plus long (cest-à-dire une phrase secrète) et/ou dutiliser différents types de caractères (majuscules, minuscules, chiffres et caractères spéciaux).",
"migration_description_0006_sync_admin_and_root_passwords": "Synchroniser les mots de passe admin et root",
"migration_0006_disclaimer": "Yunohost sattend maintenant à ce que les mots de passe admin et root soient synchronisés. En exécutant cette migration, votre mot de passe root sera remplacé par le mot de passe administrateur.",
"migration_0006_disclaimer": "YunoHost sattendra désormais à ce que les mots de passe admin et root soient synchronisés. En exécutant cette migration, votre mot de passe root sera remplacé par le mot de passe administrateur.",
"migration_0006_done": "Votre mot de passe root a été remplacé par celui de votre adminitrateur.",
"password_listed": "Ce mot de passe est l'un des mots de passe les plus utilisés dans le monde. Veuillez choisir quelque chose d'un peu plus unique.",
"password_listed": "Ce mot de passe est l'un des mots de passe les plus utilisés dans le monde. Veuillez choisir quelque chose d'un peu plus singulier.",
"password_too_simple_1": "Le mot de passe doit comporter au moins 8 caractères",
"password_too_simple_2": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules et des minuscules",
"password_too_simple_3": "Le mot de passe doit comporter au moins 8 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"password_too_simple_4": "Le mot de passe doit comporter au moins 12 caractères et contenir des chiffres, des majuscules, des minuscules et des caractères spéciaux",
"root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager sur le mot de passe root !",
"aborting": "Interruption de la procédure.",
"root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !",
"aborting": "Interruption.",
"app_not_upgraded": "Les applications suivantes n'ont pas été mises à jour : {apps}",
"app_start_install": "Installation de l'application {app} …",
"app_start_remove": "Suppression de l'application {app} …",
@ -492,9 +492,9 @@
"ask_new_path": "Nouveau chemin",
"backup_actually_backuping": "Création d'une archive de sauvegarde à partir des fichiers collectés …",
"backup_mount_archive_for_restore": "Préparation de l'archive pour restauration …",
"confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais n'est pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que l'authentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. L'installer quand même ? [{réponses:s}] ",
"confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais n'est pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que l'authentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. L'installer quand même ? [{answers:s}] ",
"confirm_app_install_danger": "AVERTISSEMENT ! Cette application est encore expérimentale (explicitement, elle ne fonctionne pas) et risque de casser votre système ! Vous ne devriez probablement PAS l'installer sans savoir ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ",
"confirm_app_install_thirdparty": "AVERTISSEMENT ! L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer si vous ne savez pas ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{réponses:s}] ",
"confirm_app_install_thirdparty": "AVERTISSEMENT ! L'installation d'applications tierces peut compromettre l'intégrité et la sécurité de votre système. Vous ne devriez probablement PAS l'installer si vous ne savez pas ce que vous faites. Êtes-vous prêt à prendre ce risque ? [{answers:s}] ",
"dpkg_is_broken": "Vous ne pouvez pas faire ça maintenant car dpkg/apt (le gestionnaire de paquets du système) semble avoir laissé des choses non configurées. Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a'.",
"dyndns_could_not_check_available": "Impossible de vérifier si {domain:s} est disponible chez {provider:s}.",
"file_does_not_exist": "Le fichier dont le chemin est {path:s} n'existe pas.",
@ -516,11 +516,11 @@
"pattern_password_app": "Désolé, les mots de passe ne doivent pas contenir les caractères suivants : {forbidden_chars}",
"root_password_replaced_by_admin_password": "Votre mot de passe root a été remplacé par votre mot de passe administrateur.",
"service_conf_now_managed_by_yunohost": "Le fichier de configuration '{conf}' est maintenant géré par YunoHost.",
"service_reload_failed": "Impossible de recharger le service '{service:s}'.\n\nJournaux récents de ce service : {logs:s}",
"service_reload_failed": "Impossible de recharger le service '{service:s}'.\n\nJournaux historisés récents de ce service : {logs:s}",
"service_reloaded": "Le service '{service:s}' a été rechargé",
"service_restart_failed": "Impossible de redémarrer le service '{service:s}'\n\nJournaux récents de ce service : {logs:s}",
"service_restart_failed": "Impossible de redémarrer le service '{service:s}'\n\nJournaux historisés récents de ce service : {logs:s}",
"service_restarted": "Le service '{service:s}' a été redémarré",
"service_reload_or_restart_failed": "Impossible de recharger ou de redémarrer le service '{service:s}'\n\nJournaux récents de ce service : {logs:s}",
"service_reload_or_restart_failed": "Impossible de recharger ou de redémarrer le service '{service:s}'\n\nJournaux historisés récents de ce service : {logs:s}",
"service_reloaded_or_restarted": "Le service '{service:s}' a été rechargé ou redémarré",
"this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a'."
"this_action_broke_dpkg": "Cette action a laissé des paquets non configurés par dpkg/apt (les gestionnaires de paquets système). Vous pouvez essayer de résoudre ce problème en vous connectant via SSH et en exécutant `sudo dpkg --configure -a`."
}

View file

@ -26,7 +26,7 @@
"service_stop_failed": "Impossibile fermare il servizio '{service:s}'\n\nRegistri di servizio recenti:{logs:s}",
"system_username_exists": "il nome utente esiste già negli utenti del sistema",
"unrestore_app": "L'applicazione '{app:s}' non verrà ripristinata",
"upgrading_packages": "Aggiornamento dei pacchetti...",
"upgrading_packages": "Aggiornamento dei pacchetti",
"user_deleted": "L'utente è stato cancellato",
"admin_password": "Password dell'amministrazione",
"admin_password_change_failed": "Impossibile cambiare la password",
@ -47,7 +47,7 @@
"appslist_fetched": "La lista delle applicazioni {appslist:s} è stata recuperata",
"appslist_removed": "La lista delle applicazioni {appslist:s} è stata rimossa",
"app_package_need_update": "Il pacchetto dell'applicazione {app} deve essere aggiornato per seguire i cambiamenti di YunoHost",
"app_requirements_checking": "Controllo i pacchetti richiesti per {app}...",
"app_requirements_checking": "Controllo i pacchetti richiesti per {app}",
"app_requirements_failed": "Impossibile soddisfare i requisiti per {app}: {error}",
"app_requirements_unmeet": "Requisiti non soddisfatti per {app}, il pacchetto {pkgname} ({version}) deve essere {spec}",
"appslist_unknown": "Lista di applicazioni {appslist:s} sconosciuta.",
@ -72,16 +72,16 @@
"backup_archive_name_unknown": "Archivio di backup locale chiamato '{name:s}' sconosciuto",
"backup_archive_open_failed": "Non è possibile aprire l'archivio di backup",
"backup_cleaning_failed": "Non è possibile pulire la directory temporanea di backup",
"backup_creating_archive": "Creazione del archivio di backup...",
"backup_creating_archive": "Creazione del archivio di backup",
"backup_creation_failed": "La creazione del backup è fallita",
"backup_delete_error": "Impossibile cancellare '{path:s}'",
"backup_deleted": "Il backup è stato cancellato",
"backup_extracting_archive": "Estrazione del archivio di backup...",
"backup_extracting_archive": "Estrazione del archivio di backup",
"backup_hook_unknown": "Hook di backup '{hook:s}' sconosciuto",
"backup_nothings_done": "Non c'è niente da salvare",
"backup_output_directory_forbidden": "Directory di output vietata. I backup non possono esser creati nelle sotto-cartelle /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var o /home/yunohost.backup/archives",
"backup_output_directory_required": "Devi fornire una directory di output per il backup",
"backup_running_hooks": "Esecuzione dei hook di backup...",
"backup_running_hooks": "Esecuzione degli hook di backup…",
"custom_app_url_required": "Devi fornire un URL per essere in grado di aggiornare l'applicazione personalizzata {app:s}",
"custom_appslist_name_required": "Devi fornire un nome per la lista di applicazioni personalizzata",
"diagnosis_debian_version_error": "Impossibile riportare la versione di Debian: {error}",
@ -103,21 +103,21 @@
"domain_zone_not_found": "Il file di zona DNS non è stato trovato per il dominio {:s}",
"done": "Terminato",
"domains_available": "Domini disponibili:",
"downloading": "Scaricamento...",
"downloading": "Scaricamento",
"dyndns_cron_installed": "Il cronjob DynDNS è stato installato",
"dyndns_cron_remove_failed": "Impossibile rimuovere il cronjob DynDNS",
"dyndns_cron_removed": "Il cronjob DynDNS è stato rimosso",
"dyndns_ip_update_failed": "Impossibile aggiornare l'indirizzo IP in DynDNS",
"dyndns_ip_updated": "Il tuo indirizzo IP è stato aggiornato in DynDNS",
"dyndns_key_generating": "La chiave DNS sta generando, potrebbe richiedere del tempo...",
"dyndns_key_generating": "Si sta generando la chiave DNS, potrebbe richiedere del tempo…",
"dyndns_key_not_found": "La chiave DNS non è stata trovata per il dominio",
"dyndns_no_domain_registered": "Nessuno dominio è stato registrato con DynDNS",
"dyndns_registered": "Il dominio DynDNS è stato registrato",
"dyndns_registration_failed": "Non è possibile registrare il dominio DynDNS: {error:s}",
"dyndns_unavailable": "Dominio {domain:s} non disponibile.",
"executing_command": "Esecuzione del comando '{command:s}'...",
"executing_script": "Esecuzione dello script '{script:s}'...",
"extracting": "Estrazione...",
"executing_command": "Esecuzione del comando '{command:s}'",
"executing_script": "Esecuzione dello script '{script:s}'",
"extracting": "Estrazione",
"field_invalid": "Campo '{:s}' non valido",
"firewall_reload_failed": "Impossibile ricaricare il firewall",
"firewall_reloaded": "Il firewall è stato ricaricato",
@ -187,8 +187,8 @@
"package_unexpected_error": "Un'errore inaspettata si è verificata durante il trattamento del pacchetto '{pkgname}'",
"restore_hook_unavailable": "Lo script di ripristino per '{part:s}' non è disponibile per il tuo sistema e non è nemmeno nell'archivio",
"restore_nothings_done": "Non è stato ripristinato nulla",
"restore_running_app_script": "Esecuzione dello script di ripristino dell'applcicazione '{app:s}'...",
"restore_running_hooks": "Esecuzione dei hook di ripristino...",
"restore_running_app_script": "Esecuzione dello script di ripristino dell'applicazione '{app:s}'…",
"restore_running_hooks": "Esecuzione degli hook di ripristino…",
"service_added": "Il servizio '{service:s}' è stato aggiunto",
"service_already_started": "Il servizio '{service:s}' è già stato avviato",
"service_already_stopped": "Il servizio '{service:s}' è già stato fermato",
@ -207,9 +207,9 @@
"service_enable_failed": "Impossibile abilitare il servizio '{service:s}'\n\nRegistri di servizio recenti:{logs:s}",
"service_enabled": "Il servizio '{service:s}' è stato attivato",
"service_no_log": "Nessuno registro da visualizzare per il servizio '{service:s}'",
"service_regenconf_dry_pending_applying": "Verificazione della configurazione in attesa che sarebbe stata applicata per il servizio '{service}'...",
"service_regenconf_dry_pending_applying": "Verifica della configurazione in sospeso che sarebbe stata applicata per il servizio '{service}'…",
"service_regenconf_failed": "Impossibile rigenerare la configurazione per il/i servizio/i: {services}",
"service_regenconf_pending_applying": "Applicazione della configurazione in attesa per il servizio '{service}'...",
"service_regenconf_pending_applying": "Applicazione della configurazione in sospeso per il servizio '{service}'…",
"service_start_failed": "Impossibile eseguire il servizio '{service:s}'\n\nRegistri di servizio recenti:{logs:s}",
"service_started": "Il servizio '{service:s}' è stato avviato",
"service_status_failed": "Impossibile determinare lo stato del servizio '{service:s}'",
@ -225,7 +225,7 @@
"unit_unknown": "Unità '{unit:s}' sconosciuta",
"unlimit": "Nessuna quota",
"update_cache_failed": "Impossibile aggiornare la cache APT",
"updating_apt_cache": "Aggiornamento della lista dei pacchetti disponibili...",
"updating_apt_cache": "Recupero degli aggiornamenti disponibili per i pacchietti di sistema…",
"upgrade_complete": "Aggiornamento completo",
"upnp_dev_not_found": "Nessuno supporto UPnP trovato",
"upnp_disabled": "UPnP è stato disattivato",
@ -241,13 +241,13 @@
"yunohost_already_installed": "YunoHost è già installato",
"yunohost_ca_creation_failed": "Impossibile creare una certificate authority",
"yunohost_configured": "YunoHost è stato configurato",
"yunohost_installing": "Installazione di YunoHost...",
"yunohost_installing": "Installazione di YunoHost",
"yunohost_not_installed": "YunoHost non è o non corretamente installato. Esegui 'yunohost tools postinstall'",
"domain_cert_gen_failed": "Impossibile generare il certificato",
"certmanager_attempt_to_replace_valid_cert": "Stai provando a sovrascrivere un certificato buono e valido per il dominio {domain:s}! (Usa --force per ignorare)",
"certmanager_domain_unknown": "Dominio {domain:s} sconosciuto",
"certmanager_domain_cert_not_selfsigned": "Il ceritifcato per il dominio {domain:s} non è auto-firmato. Sei sicuro di volere sostituirlo? (Usa --force)",
"certmanager_certificate_fetching_or_enabling_failed": "L'attivazione del nuovo certificato per {domain:s} sembra fallita in qualche modo...",
"certmanager_certificate_fetching_or_enabling_failed": "L'attivazione del nuovo certificato per {domain:s} sembra fallita per qualche motivo…",
"certmanager_attempt_to_renew_nonLE_cert": "Il certificato per il dominio {domain:s} non è emesso da Let's Encrypt. Impossibile rinnovarlo automaticamente!",
"certmanager_attempt_to_renew_valid_cert": "Il certificato per il dominio {domain:s} non è a scadere! Usa --force per ignorare",
"certmanager_domain_http_not_working": "Sembra che non sia possibile accedere al dominio {domain:s} attraverso HTTP. Verifica la configurazione del DNS e di nginx",
@ -260,19 +260,19 @@
"app_change_url_success": "URL dell'applicazione {app:s} cambiato con successo in {domain:s}{path:s}",
"app_make_default_location_already_used": "Impostazione dell'applicazione '{app}' come predefinita del dominio {domain} non riuscita perchè è già stata impostata per l'altra applicazione '{other_app}'",
"app_location_unavailable": "Questo URL non è disponibile o va in conflitto con la/le applicazione/i già installata/e:\n{apps:s}",
"app_upgrade_app_name": "Aggiornando l'applicazione {app}...",
"app_upgrade_app_name": "Aggiornando l'applicazione {app}",
"app_upgrade_some_app_failed": "Impossibile aggiornare alcune applicazioni",
"appslist_corrupted_json": "Caricamento della lista delle applicazioni non riuscita. Sembra che {filename:s} sia corrotto.",
"appslist_could_not_migrate": "Migrazione della lista delle applicazioni {appslist:s} non riuscita! Impossibile analizzare l'URL... La vecchia operazione pianificata è stata tenuta in {bkp_file:s}.",
"appslist_migrating": "Migrando la lista di applicazioni {appslist:s} ...",
"appslist_migrating": "Migrando la lista di applicazioni {appslist:s}",
"appslist_name_already_tracked": "C'è già una lista di applicazioni registrata con il nome {name:s}.",
"appslist_url_already_tracked": "C'è già una lista di applicazioni registrata con URL {url:s}.",
"ask_path": "Percorso",
"backup_abstract_method": "Questo metodo di backup non è ancora stato implementato",
"backup_applying_method_borg": "Inviando tutti i file da salvare nel backup nel deposito borg-backup...",
"backup_applying_method_copy": "Copiando tutti i files nel backup...",
"backup_applying_method_custom": "Chiamando il metodo di backup personalizzato '{method:s}'...",
"backup_applying_method_tar": "Creando l'archivio tar del backup...",
"backup_applying_method_borg": "Inviando tutti i file da salvare nel backup nel deposito borg-backup",
"backup_applying_method_copy": "Copiando tutti i files nel backup",
"backup_applying_method_custom": "Chiamando il metodo di backup personalizzato '{method:s}'",
"backup_applying_method_tar": "Creando l'archivio tar del backup",
"backup_archive_mount_failed": "Montaggio dell'archivio del backup non riuscito",
"backup_archive_system_part_not_available": "La parte di sistema '{part:s}' non è disponibile in questo backup",
"backup_archive_writing_error": "Impossibile aggiungere i file al backup nell'archivio compresso",
@ -298,5 +298,28 @@
"backup_with_no_restore_script_for_app": "L'app {app:s} non ha script di ripristino, non sarai in grado di ripristinarla automaticamente dal backup di questa app.",
"certmanager_acme_not_configured_for_domain": "Il certificato per il dominio {domain:s} non sembra essere correttamente installato. Per favore esegui cert-install per questo dominio prima.",
"certmanager_cannot_read_cert": "Qualcosa è andato storto nel tentativo di aprire il certificato attuale per il dominio {domain:s} (file: {file:s}), motivo: {reason:s}",
"certmanager_cert_install_success": "Certificato Let's Encrypt per il dominio {domain:s} installato con successo!"
"certmanager_cert_install_success": "Certificato Let's Encrypt per il dominio {domain:s} installato con successo!",
"aborting": "Annullamento.",
"admin_password_too_long": "Per favore scegli una password più corta di 127 caratteri",
"app_not_upgraded": "Le seguenti app non sono state aggiornate: {apps}",
"app_start_install": "Installando l'applicazione {app}…",
"app_start_remove": "Rimuovendo l'applicazione {app}…",
"app_start_backup": "Raccogliendo file da salvare nel backup per {app}…",
"app_start_restore": "Ripristinando l'applicazione {app}…",
"app_upgrade_several_apps": "Le seguenti app saranno aggiornate : {apps}",
"ask_new_domain": "Nuovo dominio",
"ask_new_path": "Nuovo percorso",
"backup_actually_backuping": "Creando un archivio di backup con i file raccolti…",
"backup_mount_archive_for_restore": "Preparando l'archivio per il ripristino…",
"certmanager_cert_install_success_selfsigned": "Certificato autofirmato installato con successo per il dominio {domain:s}!",
"certmanager_cert_renew_success": "Certificato di Let's Encrypt rinnovato con successo per il dominio {domain:s}!",
"certmanager_cert_signing_failed": "Firma del nuovo certificato fallita",
"good_practices_about_user_password": "Ora stai per impostare una nuova password utente. La password dovrebbe essere di almeno 8 caratteri - anche se è buona pratica utilizzare password più lunghe (es. una sequenza di parole) e/o utilizzare vari tipi di caratteri (maiuscole, minuscole, numeri e simboli).",
"password_listed": "Questa password è una tra le più utilizzate al mondo. Per favore scegline una più unica.",
"password_too_simple_1": "La password deve essere lunga almeno 8 caratteri",
"password_too_simple_2": "La password deve essere lunga almeno 8 caratteri e contenere numeri, maiuscole e minuscole",
"password_too_simple_3": "La password deve essere lunga almeno 8 caratteri e contenere numeri, maiuscole e minuscole e simboli",
"password_too_simple_4": "La password deve essere lunga almeno 12 caratteri e contenere numeri, maiuscole e minuscole",
"users_available": "Utenti disponibili:",
"yunohost_ca_creation_success": "L'autorità di certificazione locale è stata creata."
}

View file

@ -13,7 +13,7 @@
"app_not_properly_removed": "{app:s} es pas estat corrèctament suprimit",
"app_removed": "{app:s} es estat suprimit",
"app_unknown": "Aplicacion desconeguda",
"app_upgrade_app_name": "Mesa a jorn de laplicacion {app}...",
"app_upgrade_app_name": "Mesa a jorn de laplicacion {app}",
"app_upgrade_failed": "Impossible de metre a jorn {app:s}",
"app_upgrade_some_app_failed": "Daplicacions se pòdon pas metre a jorn",
"app_upgraded": "{app:s} es estat mes a jorn",
@ -52,7 +52,7 @@
"app_location_already_used": "Laplicacion « {app}»es ja installada a aqueste emplaçament ({path})",
"app_manifest_invalid": "Manifest daplicacion incorrècte: {error}",
"app_package_need_update": "Lo paquet de laplicacion {app} deu èsser mes a jorn per seguir los cambiaments de YunoHost",
"app_requirements_checking": "Verificacion dels paquets requesida per {app}...",
"app_requirements_checking": "Verificacion dels paquets requesits per {app}…",
"app_sources_fetch_failed": "Recuperacion dels fichièrs fonts impossibla",
"app_unsupported_remote_type": "Lo tipe alonhat utilizat per laplicacion es pas suportat",
"appslist_retrieve_error": "Impossible de recuperar la lista daplicacions alonhadas {appslist:s}: {error:s}",
@ -64,7 +64,7 @@
"backup_cleaning_failed": "Impossible de netejar lo repertòri temporari de salvagarda",
"backup_copying_to_organize_the_archive": "Còpia de {size:s} Mio per organizar larchiu",
"backup_created": "Salvagarda acabada",
"backup_creating_archive": "Creacion de larchiu de salvagarda...",
"backup_creating_archive": "Creacion de larchiu de salvagarda",
"backup_creation_failed": "Impossible de crear la salvagarda",
"app_already_installed_cant_change_url": "Aquesta aplicacion es ja installada. Aquesta foncion pòt pas simplament cambiar lURL. Agachatz «app changeurl »ses disponible.",
"app_change_no_change_url_script": "Laplicacion {app_name:s} pren pas en compte lo cambiament dURL, poiretz aver de la metre a jorn.",
@ -83,7 +83,7 @@
"backup_output_directory_not_empty": "Lo dorsièr de sortida es pas void",
"backup_output_directory_required": "Vos cal especificar un dorsièr de sortida per la salvagarda",
"backup_running_app_script": "Lançament de lescript de salvagarda de laplicacion « {app:s} »...",
"backup_running_hooks": "Execucion dels scripts de salvagarda...",
"backup_running_hooks": "Execucion dels scripts de salvagarda",
"backup_system_part_failed": "Impossible de salvagardar la part « {part:s} »del sistèma",
"app_requirements_failed": "Impossible de complir las condicions requesidas per {app}: {error}",
"app_requirements_unmeet": "Las condicions requesidas per {app} son pas complidas, lo paquet {pkgname} ({version}) deu èsser {spec}",
@ -112,7 +112,7 @@
"upnp_port_open_failed": "Impossible de dobrir los pòrts amb UPnP",
"yunohost_already_installed": "YunoHost es ja installat",
"yunohost_configured": "YunoHost es estat configurat",
"yunohost_installing": "Installacion de YunoHost...",
"yunohost_installing": "Installacion de YunoHost",
"backup_applying_method_borg": "Mandadís de totes los fichièrs a la salvagarda dins lo repertòri borg-backup…",
"backup_csv_creation_failed": "Creacion impossibla del fichièr CSV necessari a las operacions futuras de restauracion",
"backup_extracting_archive": "Extraccion de larchiu de salvagarda…",
@ -161,7 +161,7 @@
"dyndns_cron_removed": "La tasca cron pel domeni DynDNS es levada",
"dyndns_ip_update_failed": "Impossible dactualizar ladreça IP sul domeni DynDNS",
"dyndns_ip_updated": "Vòstra adreça IP es estada actualizada pel domeni DynDNS",
"dyndns_key_generating": "La clau DNS es a se generar, pòt trigar una estona...",
"dyndns_key_generating": "La clau DNS es a se generar, pòt trigar una estona",
"dyndns_key_not_found": "Clau DNS introbabla pel domeni",
"dyndns_no_domain_registered": "Cap de domeni pas enregistrat amb DynDNS",
"dyndns_registered": "Lo domeni DynDNS es enregistrat",
@ -313,7 +313,7 @@
"service_conf_would_be_updated": "La configuracion del servici « {service} »seriá estada actualizada",
"service_description_avahi-daemon": "permet daténher vòstre servidor via yunohost.local sus vòstre ret local",
"service_description_dnsmasq": "gerís la resolucion dels noms de domeni (DNS)",
"updating_apt_cache": "Actualizacion de la lista dels paquets disponibles...",
"updating_apt_cache": "Actualizacion de la lista dels paquets disponibles",
"service_conf_file_backed_up": "Lo fichièr de configuracion « {conf} »es salvagardat dins « {backup} »",
"service_conf_file_copy_failed": "Còpia impossibla del nòu fichièr de configuracion « {new} »cap a « {conf} »",
"server_reboot_confirm": "Lo servidor es per reaviar sul pic, o volètz vertadièrament? {answers:s}",
@ -372,7 +372,7 @@
"migrate_tsig_start": "Lalgorisme de generacion de claus es pas pro securizat per la signatura TSIG del domeni « {domain} », lançament de la migracion cap a hmac-sha512 ques mai securizat",
"migration_description_0001_change_cert_group_to_sslcert": "Càmbia las permissions de grop dels certificats de «metronome »per «ssl-cert »",
"migration_0003_restoring_origin_nginx_conf": "Vòstre fichièr /etc/nginx/nginx.conf es estat modificat manualament. La migracion reïnicializarà den primièr son estat origina… Lo fichièr precedent serà disponible coma {backup_dest}.",
"migration_0003_still_on_jessie_after_main_upgrade": "Quicòm a trucat pendent la mesa a nivèl màger: lo sistèma es encara jos Jessie?!? Per trobar lo problèma, agachatz {log} …",
"migration_0003_still_on_jessie_after_main_upgrade": "Quicòm a trucat pendent la mesa a nivèl màger: lo sistèma es encara jos Jessie?!? Per trobar lo problèma, agachatz {log}…",
"migration_0003_general_warning": "Notatz quaquesta migracion es una operacion delicata. Encara que la còla YunoHost aguèsse fach çò melhor per la tornar legir e provar, la migracion poiriá copar de parts del sistèma o de las aplicacions.\n\nEn consequéncia, vos recomandam:\n· · · · - de lançar una salvagarda de vòstras donadas o aplicacions criticas. Mai dinformacions a https://yunohost.org/backup ;\n· · · · - dèsser pacient aprèp aver lançat la migracion: segon vòstra connexion Internet e material, pòt trigar qualques oras per que tot siá mes al nivèl.\n\nEn mai, lo pòrt per SMTP, utilizat pels clients de corrièls extèrns (coma Thunderbird o K9-Mail per exemple) foguèt cambiat de 465 (SSL/TLS) per 587 (STARTTLS). Lancian pòrt 465 serà automaticament tampat e lo nòu pòrt 587 serà dobèrt dins lo parafuòc. Vosautres e vòstres utilizaires *auretz* dadaptar la configuracion de vòstre client de corrièl segon aqueles cambiaments!",
"migration_0003_problematic_apps_warning": "Notatz que las aplicacions seguentas, saique problematicas, son estadas desactivadas. Semblan daver estadas installadas duna lista daplicacions o que son pas marcadas coma «working ». En consequéncia, podèm pas assegurar que tendràn de foncionar aprèp la mesa a nivèl: {problematic_apps}",
"migrations_bad_value_for_target": "Nombre invalid pel paramètre «target », los numèros de migracion son 0 o {}",
@ -457,14 +457,34 @@
"service_description_php7.0-fpm": "executa daplicacions escrichas en PHP amb nginx",
"users_available": "Lista dels utilizaires disponibles:",
"good_practices_about_admin_password": "Sètz per definir un nòu senhal per ladministracion. Lo senhal deu almens conténer 8 caractèrs - encara que siá de bon far dutilizar un senhal mai long quaquò (ex. una passafrasa) e/o dutilizar mantun tipes de caractèrs (majuscula, minuscula, nombre e caractèrs especials).",
"good_practices_about_user_password": "Sètz per definir un nòu senhal dutilizaire. Lo senhal deu almens conténer 8 caractèrs - encara que siá de bon far dutilizar un senhal mai long quaquò (ex. una passafrasa) e/o dutilizar mantun tipes de caractèrs (majuscula, minuscula, nombre e caractèrs especials).",
"good_practices_about_user_password": "Sètz a mand de definir un nòu senhal dutilizaire. Lo nòu senhal deu conténer almens 8 caractèrs, es de bon far dutilizar un senhal mai long (es a dire una frasa de senhal) e/o utilizar mantuns tipes de caractèrs (majusculas, minusculas, nombres e caractèrs especials).",
"migration_description_0006_sync_admin_and_root_passwords": "Sincronizar los senhals admin e root",
"migration_0006_disclaimer": "Ara YunoHost sespèra que los senhals admin e root sián sincronizats. En lançant aquesta migracion, vòstre senhal root serà remplaçat pel senhal admin.",
"migration_0006_done": "Lo senhal root es estat remplaçat pel senhal admin.",
"password_listed": "Aqueste senhal fa part dels senhals mai utilizats del monde. Volgatz ben ne causir un mai unic.",
"password_listed": "Aqueste senhal es un dels mai utilizats al monde. Se vos plai utilizatz-ne un mai unic.",
"password_too_simple_1": "Lo senhal deu conténer almens 8 caractèrs",
"password_too_simple_2": "Lo senhal deu conténer almens 8 caractèrs amb de nombres, de majusculas e de minusculas",
"password_too_simple_3": "Lo senhal deu conténer almens 8 caractèrs amb de nombres, de majusculas, de minusculas e de caractèrs especials",
"password_too_simple_4": "Lo senhal deu conténer almens 12 caractèrs amb de nombres, de majusculas, de minusculas e de caractèrs especials",
"root_password_desynchronized": "Lo senhal de ladministrator es estat cambiat, mas YunoHost a pas pogut lespandir al senhal root!"
"password_too_simple_2": "Lo senhal deu conténer almens 8 caractèrs e numbres, majusculas e minusculas",
"password_too_simple_3": "Lo senhal deu conténer almens 8 caractèrs e nombres, majusculas e minusculas e caractèrs especials",
"password_too_simple_4": "Lo senhal deu conténer almens 12 caractèrs, de nombre, majusculas, minisculas e caractèrs specials",
"root_password_desynchronized": "Lo senhal de ladministrator es estat cambiat, mas YunoHost a pas pogut lespandir al senhal root!",
"aborting": "Interrupcion.",
"app_not_upgraded": "Las aplicacions seguentas son pas estadas actualizadas: {apps}",
"app_start_install": "Installacion de laplicacion {app}…",
"app_start_remove": "Supression de laplicacion {app}…",
"app_start_backup": "Recuperacion dels fichièrs de salvagardar per {app}…",
"app_start_restore": "Restauracion de laplicacion {app}…",
"app_upgrade_several_apps": "Las aplicacions seguentas seràn mesas a jorn: {apps}",
"ask_new_domain": "Nòu domeni",
"ask_new_path": "Nòu camin",
"backup_actually_backuping": "Creacion dun archiu de seguretat a partir dels fichièrs recuperats…",
"backup_mount_archive_for_restore": "Preparacion de larchiu per restauracion…",
"dyndns_could_not_check_available": "Verificacion impossibla de la disponibilitat de {domain:s} sus {provider:s}.",
"file_does_not_exist": "Lo camin {path:s} existís pas.",
"global_settings_setting_security_password_admin_strength": "Fòrça del senhal administrator",
"global_settings_setting_security_password_user_strength": "Fòrça del senhal utilizaire",
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "La configuracion SSH serà gerada per YunoHost (etapa 1, automatica)",
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Daissar YunoHost gerir la configuracion SSH (etapa 2, manuala)",
"migration_0007_cancelled": "YunoHost a pas reüssit a melhorar lo biais de gerir la configuracion SSH.",
"root_password_replaced_by_admin_password": "Lo senhal root es estat remplaçat pel senhal administrator.",
"service_restarted": "Lo servici '{service:s}' es estat reaviat"
}

View file

@ -42,7 +42,7 @@ from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json
from yunohost.service import service_log, _run_service_command
from yunohost.service import service_log, service_status, _run_service_command
from yunohost.utils import packages
from yunohost.log import is_unit_operation, OperationLogger
@ -623,6 +623,7 @@ def app_upgrade(auth, app=[], url=None, file=None):
# Check requirements
_check_manifest_requirements(manifest, app_instance_name=app_instance_name)
_check_services_status_for_app(manifest.get("services", []))
app_setting_path = APPS_SETTING_PATH + '/' + app_instance_name
@ -778,6 +779,7 @@ def app_install(operation_logger, auth, app, label=None, args=None, no_remove_on
# Check requirements
_check_manifest_requirements(manifest, app_id)
_check_services_status_for_app(manifest.get("services", []))
# Check if app can be forked
instance_number = _installed_instance_number(app_id, last=True) + 1
@ -1627,7 +1629,7 @@ def app_config_show_panel(app):
args=["show"],
env=env,
stdout_callback=parse_stdout,
)
)[0]
if return_code != 0:
raise Exception("script/config show return value code: %s (considered as an error)", return_code)
@ -1713,7 +1715,7 @@ def app_config_apply(app, args):
return_code = hook_exec(config_script,
args=["apply"],
env=env,
)
)[0]
if return_code != 0:
raise Exception("'script/config apply' return value code: %s (considered as an error)", return_code)
@ -1771,12 +1773,18 @@ def _get_app_status(app_id, format_date=False):
raise YunohostError('app_unknown')
status = {}
regen_status = True
try:
with open(app_setting_path + '/status.json') as f:
status = json.loads(str(f.read()))
regen_status = False
except IOError:
logger.debug("status file not found for '%s'", app_id,
exc_info=1)
except Exception as e:
logger.warning("could not open or decode %s : %s ... regenerating.", app_setting_path + '/status.json', str(e))
if regen_status:
# Create app status
status = {
'installed_at': app_setting(app_id, 'install_time'),
@ -2204,7 +2212,7 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
# do not print for webadmin
if arg_type == 'display_text' and msettings.get('interface') != 'api':
print(arg["text"])
print(_value_for_locale(arg['ask']))
continue
# Attempt to retrieve argument value
@ -2257,13 +2265,17 @@ def _parse_action_args_in_yunohost_format(args, action_args, auth=None):
elif arg_default is not None:
arg_value = arg_default
# Validate argument value
if (arg_value is None or arg_value == '') \
and not arg.get('optional', False):
raise YunohostError('app_argument_required', name=arg_name)
elif arg_value is None:
args_dict[arg_name] = ''
continue
# If the value is empty (none or '')
# then check if arg is optional or not
if arg_value is None or arg_value == '':
if arg.get("optional", False):
# Argument is optional, keep an empty value
# and that's all for this arg !
args_dict[arg_name] = ''
continue
else:
# The argument is required !
raise YunohostError('app_argument_required', name=arg_name)
# Validate argument choice
if arg_choices and arg_value not in arg_choices:
@ -2582,6 +2594,31 @@ def unstable_apps():
return output
def _check_services_status_for_app(services):
logger.debug("Checking that required services are up and running...")
# Some apps use php-fpm or php5-fpm which is now php7.0-fpm
def replace_alias(service):
if service in ["php-fpm", "php5-fpm"]:
return "php7.0-fpm"
else:
return service
services = [replace_alias(s) for s in services]
# We only check those, mostly to ignore "custom" services
# (added by apps) and because those are the most popular
# services
service_filter = ["nginx", "php7.0-fpm", "mysql", "postfix"]
services = [str(s) for s in services if s in service_filter]
# List services currently down and raise an exception if any are found
faulty_services = [s for s in services if service_status(s)["active"] != "active"]
if faulty_services:
raise YunohostError('app_action_cannot_be_ran_because_required_services_down',
services=', '.join(faulty_services))
def _patch_php5(app_folder):
files_to_patch = []

View file

@ -50,7 +50,7 @@ from yunohost.hook import (
)
from yunohost.monitor import binary_to_human
from yunohost.tools import tools_postinstall
from yunohost.service import service_regen_conf
from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger
from functools import reduce
@ -1212,7 +1212,7 @@ class RestoreManager():
else:
operation_logger.success()
service_regen_conf()
regen_conf()
def _restore_apps(self):
"""Restore all apps targeted"""

View file

@ -43,7 +43,8 @@ from yunohost.utils.network import get_public_ip
from moulinette import m18n
from yunohost.app import app_ssowatconf
from yunohost.service import _run_service_command, service_regen_conf
from yunohost.service import _run_service_command
from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger
logger = getActionLogger('yunohost.certmanager')
@ -806,7 +807,7 @@ def _enable_certificate(domain, new_cert_folder):
if os.path.isfile('/etc/yunohost/installed'):
# regen nginx conf to be sure it integrates OCSP Stapling
# (We don't do this yet if postinstall is not finished yet)
service_regen_conf(names=['nginx'])
regen_conf(names=['nginx'])
_run_service_command("reload", "nginx")
@ -924,7 +925,7 @@ def _regen_dnsmasq_if_needed():
break
if do_regen:
service_regen_conf(["dnsmasq"])
regen_conf(["dnsmasq"])
def _name_self_CA():

View file

@ -10,9 +10,9 @@ from moulinette.utils.filesystem import read_file
from yunohost.tools import Migration
from yunohost.app import unstable_apps
from yunohost.service import (_run_service_command,
manually_modified_files,
manually_modified_files_compared_to_debian_default)
from yunohost.service import _run_service_command
from yunohost.regenconf import (manually_modified_files,
manually_modified_files_compared_to_debian_default)
from yunohost.utils.filesystem import free_space_in_directory
from yunohost.utils.packages import get_installed_version
from yunohost.utils.network import get_network_interfaces

View file

@ -3,15 +3,12 @@ import re
from shutil import copyfile
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import mkdir, rm
from yunohost.tools import Migration
from yunohost.service import service_regen_conf, \
_get_conf_hashes, \
_calculate_hash, \
_run_service_command
from yunohost.service import _run_service_command
from yunohost.regenconf import regen_conf
from yunohost.settings import settings_set
from yunohost.utils.error import YunohostError
@ -60,7 +57,7 @@ class MyMigration(Migration):
if os.path.exists('/etc/yunohost/from_script'):
rm('/etc/yunohost/from_script')
copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp')
service_regen_conf(names=['ssh'], force=True)
regen_conf(names=['ssh'], force=True)
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
# Restart ssh and backward if it fail

View file

@ -6,9 +6,8 @@ from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import chown
from yunohost.tools import Migration
from yunohost.service import service_regen_conf, \
_get_conf_hashes, \
_calculate_hash
from yunohost.regenconf import _get_conf_hashes, _calculate_hash
from yunohost.regenconf import regen_conf
from yunohost.settings import settings_set, settings_get
from yunohost.utils.error import YunohostError
from yunohost.backup import ARCHIVES_PATH
@ -36,7 +35,7 @@ class MyMigration(Migration):
def migrate(self):
settings_set("service.ssh.allow_deprecated_dsa_hostkey", False)
service_regen_conf(names=['ssh'], force=True)
regen_conf(names=['ssh'], force=True)
# Update local archives folder permissions, so that
# admin can scp archives out of the server

View file

@ -0,0 +1,42 @@
import os
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file
from yunohost.service import _get_services, _save_services
from yunohost.regenconf import _update_conf_hashes, REGEN_CONF_FILE
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"""
Decouple the regen conf mechanism from the concept of services
"""
def migrate(self):
if "conffiles" not in read_file("/etc/yunohost/services.yml") \
or os.path.exists(REGEN_CONF_FILE):
logger.warning(m18n.n("migration_0009_not_needed"))
return
# For all services
services = _get_services()
for service, infos in services.items():
# If there are some conffiles (file hashes)
if "conffiles" in infos.keys():
# Save them using the new regen conf thingy
_update_conf_hashes(service, infos["conffiles"])
# And delete the old conffile key from the service infos
del services[service]["conffiles"]
# (Actually save the modification of services)
_save_services(services)
def backward(self):
pass

View file

@ -0,0 +1,42 @@
import os
from moulinette.utils.log import getActionLogger
from yunohost.app import app_fetchlist, app_removelist, _read_appslist_list, APPSLISTS_JSON
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
BASE_CONF_PATH = '/home/yunohost.conf'
BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup')
APPSLISTS_BACKUP = os.path.join(BACKUP_CONF_DIR, "appslist_before_migration_to_unified_list.json")
class MyMigration(Migration):
"Migrate from official.json to apps.json"
def migrate(self):
# Backup current app list json
os.system("cp %s %s" % (APPSLISTS_JSON, APPSLISTS_BACKUP))
# Remove all the deprecated lists
lists_to_remove = [
"https://app.yunohost.org/official.json",
"https://app.yunohost.org/community.json",
"https://labriqueinter.net/apps/labriqueinternet.json"
]
appslists = _read_appslist_list()
for appslist, infos in appslists.items():
if infos["url"] in lists_to_remove:
app_removelist(name=appslist)
# Replace by apps.json list
app_fetchlist(name="yunohost",
url="https://app.yunohost.org/apps.json")
def backward(self):
if os.path.exists(APPSLISTS_BACKUP):
os.system("cp %s %s" % (APPSLISTS_BACKUP, APPSLISTS_JSON))

View file

@ -34,9 +34,10 @@ from moulinette.utils.log import getActionLogger
import yunohost.certificate
from yunohost.service import service_regen_conf
from yunohost.regenconf import regen_conf
from yunohost.utils.network import get_public_ip
from yunohost.log import is_unit_operation
from yunohost.hook import hook_callback
logger = getActionLogger('yunohost.domain')
@ -111,7 +112,7 @@ def domain_add(operation_logger, auth, domain, dyndns=False):
# Don't regen these conf if we're still in postinstall
if os.path.exists('/etc/yunohost/installed'):
service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix', 'rspamd'])
regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix', 'rspamd'])
app_ssowatconf(auth)
except Exception:
@ -164,7 +165,7 @@ def domain_remove(operation_logger, auth, domain, force=False):
else:
raise YunohostError('domain_deletion_failed')
service_regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
regen_conf(names=['nginx', 'metronome', 'dnsmasq', 'postfix'])
app_ssowatconf(auth)
hook_callback('post_domain_remove', args=[domain])
@ -201,12 +202,19 @@ def domain_dns_conf(domain, ttl=None):
result += "; Mail"
for record in dns_conf["mail"]:
result += "\n{name} {ttl} IN {type} {value}".format(**record)
result += "\n\n"
result += "; Extra"
for record in dns_conf["extra"]:
result += "\n{name} {ttl} IN {type} {value}".format(**record)
for name, record_list in dns_conf.items():
if name not in ("basic", "xmpp", "mail", "extra") and record_list:
result += "\n\n"
result += "; " + name
for record in record_list:
result += "\n{name} {ttl} IN {type} {value}".format(**record)
is_cli = True if msettings.get('interface') == 'cli' else False
if is_cli:
logger.info(m18n.n("domain_dns_conf_is_just_a_recommendation"))
@ -338,6 +346,9 @@ def _build_dns_conf(domain, ttl=3600):
"extra": [
{"type": "CAA", "name": "@", "value": "128 issue \"letsencrypt.org\"", "ttl": 3600},
],
"example_of_a_custom_rule": [
{"type": "SRV", "name": "_matrix", "value": "domain.tld.", "ttl": 3600}
],
}
"""
@ -396,13 +407,49 @@ def _build_dns_conf(domain, ttl=3600):
["@", ttl, "CAA", '128 issue "letsencrypt.org"']
]
return {
# Official record
records = {
"basic": [{"name": name, "ttl": ttl, "type": type_, "value": value} for name, ttl, type_, value in basic],
"xmpp": [{"name": name, "ttl": ttl, "type": type_, "value": value} for name, ttl, type_, value in xmpp],
"mail": [{"name": name, "ttl": ttl, "type": type_, "value": value} for name, ttl, type_, value in mail],
"extra": [{"name": name, "ttl": ttl, "type": type_, "value": value} for name, ttl, type_, value in extra],
}
# Custom records
hook_results = hook_callback('custom_dns_rules', args=[domain])
for hook_name, results in hook_results.items():
#
# There can be multiple results per hook name, so results look like
# {'/some/path/to/hook1':
# { 'state': 'succeed',
# 'stdreturn': [{'type': 'SRV',
# 'name': 'stuff.foo.bar.',
# 'value': 'yoloswag',
# 'ttl': 3600}]
# },
# '/some/path/to/hook2':
# { ... },
# [...]
#
# Loop over the sub-results
custom_records = [v['stdreturn'] for v in results.values()
if v and v['stdreturn']]
records[hook_name] = []
for record_list in custom_records:
# Check that record_list is indeed a list of dict
# with the required keys
if not isinstance(record_list, list) \
or any(not isinstance(record, dict) for record in record_list) \
or any(key not in record for record in record_list for key in ["name", "ttl", "type", "value"]):
# Display an error, mainly for app packagers trying to implement a hook
logger.warning("Ignored custom record from hook '%s' because the data is not a *list* of dict with keys name, ttl, type and value. Raw data : %s" % (hook_name, record_list))
continue
records[hook_name].extend(record_list)
return records
def _get_DKIM(domain):
DKIM_file = '/etc/dkim/{domain}.mail.txt'.format(domain=domain)

555
src/yunohost/regenconf.py Normal file
View file

@ -0,0 +1,555 @@
# -*- coding: utf-8 -*-
""" License
Copyright (C) 2019 YunoHost
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses
"""
import os
import yaml
import json
import subprocess
import shutil
import hashlib
from difflib import unified_diff
from datetime import datetime
from moulinette import m18n
from moulinette.utils import log, filesystem
from moulinette.utils.filesystem import read_file
from yunohost.utils.error import YunohostError
from yunohost.log import is_unit_operation
from yunohost.hook import hook_callback, hook_list
BASE_CONF_PATH = '/home/yunohost.conf'
BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup')
PENDING_CONF_DIR = os.path.join(BASE_CONF_PATH, 'pending')
REGEN_CONF_FILE = '/etc/yunohost/regenconf.yml'
logger = log.getActionLogger('yunohost.regenconf')
# FIXME : those ain't just services anymore ... what are we supposed to do with this ...
# FIXME : check for all reference of 'service' close to operation_logger stuff
@is_unit_operation([('names', 'configuration')])
def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run=False,
list_pending=False):
"""
Regenerate the configuration file(s)
Keyword argument:
names -- Categories to regenerate configuration of
with_diff -- Show differences in case of configuration changes
force -- Override all manual modifications in configuration files
dry_run -- Show what would have been regenerated
list_pending -- List pending configuration files and exit
"""
# Legacy code to automatically run the migration
# This is required because regen_conf is called before the migration call
# in debian's postinst script
if os.path.exists("/etc/yunohost/installed") \
and ("conffiles" in read_file("/etc/yunohost/services.yml") \
or not os.path.exists(REGEN_CONF_FILE)):
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("decouple_regenconf_from_services")
migration.migrate()
result = {}
# Return the list of pending conf
if list_pending:
pending_conf = _get_pending_conf(names)
if not with_diff:
return pending_conf
for category, conf_files in pending_conf.items():
for system_path, pending_path in conf_files.items():
pending_conf[category][system_path] = {
'pending_conf': pending_path,
'diff': _get_files_diff(
system_path, pending_path, True),
}
return pending_conf
if not dry_run:
operation_logger.related_to = [('configuration', x) for x in names]
if not names:
operation_logger.name_parameter_override = 'all'
elif len(names) != 1:
operation_logger.name_parameter_override = str(len(operation_logger.related_to)) + '_categories'
operation_logger.start()
# Clean pending conf directory
if os.path.isdir(PENDING_CONF_DIR):
if not names:
shutil.rmtree(PENDING_CONF_DIR, ignore_errors=True)
else:
for name in names:
shutil.rmtree(os.path.join(PENDING_CONF_DIR, name),
ignore_errors=True)
else:
filesystem.mkdir(PENDING_CONF_DIR, 0o755, True)
# Format common hooks arguments
common_args = [1 if force else 0, 1 if dry_run else 0]
# Execute hooks for pre-regen
pre_args = ['pre', ] + common_args
def _pre_call(name, priority, path, args):
# create the pending conf directory for the category
category_pending_path = os.path.join(PENDING_CONF_DIR, name)
filesystem.mkdir(category_pending_path, 0o755, True, uid='root')
# return the arguments to pass to the script
return pre_args + [category_pending_path, ]
# Don't regen SSH if not specifically specified
if not names:
names = hook_list('conf_regen', list_by='name',
show_info=False)['hooks']
names.remove('ssh')
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
# Keep only the hook names with at least one success
names = [hook for hook, infos in pre_result.items()
if any(result["state"] == "succeed" for result in infos.values())]
# FIXME : what do in case of partial success/failure ...
if not names:
ret_failed = [hook for hook, infos in pre_result.items()
if any(result["state"] == "failed" for result in infos.values())]
raise YunohostError('regenconf_failed',
categories=', '.join(ret_failed))
# Set the processing method
_regen = _process_regen_conf if not dry_run else lambda *a, **k: True
operation_logger.related_to = []
# Iterate over categories and process pending conf
for category, conf_files in _get_pending_conf(names).items():
if not dry_run:
operation_logger.related_to.append(('configuration', category))
logger.debug(m18n.n(
'regenconf_pending_applying' if not dry_run else
'regenconf_dry_pending_applying',
category=category))
conf_hashes = _get_conf_hashes(category)
succeed_regen = {}
failed_regen = {}
for system_path, pending_path in conf_files.items():
logger.debug("processing pending conf '%s' to system conf '%s'",
pending_path, system_path)
conf_status = None
regenerated = False
# Get the diff between files
conf_diff = _get_files_diff(
system_path, pending_path, True) if with_diff else None
# Check if the conf must be removed
to_remove = True if os.path.getsize(pending_path) == 0 else False
# Retrieve and calculate hashes
system_hash = _calculate_hash(system_path)
saved_hash = conf_hashes.get(system_path, None)
new_hash = None if to_remove else _calculate_hash(pending_path)
# -> system conf does not exists
if not system_hash:
if to_remove:
logger.debug("> system conf is already removed")
os.remove(pending_path)
continue
if not saved_hash or force:
if force:
logger.debug("> system conf has been manually removed")
conf_status = 'force-created'
else:
logger.debug("> system conf does not exist yet")
conf_status = 'created'
regenerated = _regen(
system_path, pending_path, save=False)
else:
logger.info(m18n.n(
'regenconf_file_manually_removed',
conf=system_path))
conf_status = 'removed'
# -> system conf is not managed yet
elif not saved_hash:
logger.debug("> system conf is not managed yet")
if system_hash == new_hash:
logger.debug("> no changes to system conf has been made")
conf_status = 'managed'
regenerated = True
elif not to_remove:
# If the conf exist but is not managed yet, and is not to be removed,
# we assume that it is safe to regen it, since the file is backuped
# anyway (by default in _regen), as long as we warn the user
# appropriately.
logger.info(m18n.n('regenconf_now_managed_by_yunohost',
conf=system_path, category=category))
regenerated = _regen(system_path, pending_path)
conf_status = 'new'
elif force:
regenerated = _regen(system_path)
conf_status = 'force-removed'
else:
logger.info(m18n.n('regenconf_file_kept_back',
conf=system_path, category=category))
conf_status = 'unmanaged'
# -> system conf has not been manually modified
elif system_hash == saved_hash:
if to_remove:
regenerated = _regen(system_path)
conf_status = 'removed'
elif system_hash != new_hash:
regenerated = _regen(system_path, pending_path)
conf_status = 'updated'
else:
logger.debug("> system conf is already up-to-date")
os.remove(pending_path)
continue
else:
logger.debug("> system conf has been manually modified")
if system_hash == new_hash:
logger.debug("> new conf is as current system conf")
conf_status = 'managed'
regenerated = True
elif force:
regenerated = _regen(system_path, pending_path)
conf_status = 'force-updated'
else:
logger.warning(m18n.n(
'regenconf_file_manually_modified',
conf=system_path))
conf_status = 'modified'
# Store the result
conf_result = {'status': conf_status}
if conf_diff is not None:
conf_result['diff'] = conf_diff
if regenerated:
succeed_regen[system_path] = conf_result
conf_hashes[system_path] = new_hash
if os.path.isfile(pending_path):
os.remove(pending_path)
else:
failed_regen[system_path] = conf_result
# Check for category conf changes
if not succeed_regen and not failed_regen:
logger.debug(m18n.n('regenconf_up_to_date', category=category))
continue
elif not failed_regen:
logger.success(m18n.n(
'regenconf_updated' if not dry_run else
'regenconf_would_be_updated',
category=category))
if succeed_regen and not dry_run:
_update_conf_hashes(category, conf_hashes)
# Append the category results
result[category] = {
'applied': succeed_regen,
'pending': failed_regen
}
# Return in case of dry run
if dry_run:
return result
# Execute hooks for post-regen
post_args = ['post', ] + common_args
def _pre_call(name, priority, path, args):
# append coma-separated applied changes for the category
if name in result and result[name]['applied']:
regen_conf_files = ','.join(result[name]['applied'].keys())
else:
regen_conf_files = ''
return post_args + [regen_conf_files, ]
hook_callback('conf_regen', names, pre_callback=_pre_call)
operation_logger.success()
return result
def _get_regenconf_infos():
"""
Get a dict of regen conf informations
"""
try:
with open(REGEN_CONF_FILE, 'r') as f:
return yaml.load(f)
except:
return {}
def _save_regenconf_infos(infos):
"""
Save the regen conf informations
Keyword argument:
categories -- A dict containing the regenconf infos
"""
try:
with open(REGEN_CONF_FILE, 'w') as f:
yaml.safe_dump(infos, f, default_flow_style=False)
except Exception as e:
logger.warning('Error while saving regenconf infos, exception: %s', e, exc_info=1)
raise
def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True):
"""Compare two files and return the differences
Read and compare two files. The differences are returned either as a delta
in unified diff format or a formatted string if as_string is True. The
header can also be removed if skip_header is True.
"""
if os.path.exists(orig_file):
with open(orig_file, 'r') as orig_file:
orig_file = orig_file.readlines()
else:
orig_file = []
if os.path.exists(new_file):
with open(new_file, 'r') as new_file:
new_file = new_file.readlines()
else:
new_file = []
# Compare files and format output
diff = unified_diff(orig_file, new_file)
if skip_header:
try:
next(diff)
next(diff)
except:
pass
if as_string:
return ''.join(diff).rstrip()
return diff
def _calculate_hash(path):
"""Calculate the MD5 hash of a file"""
if not os.path.exists(path):
return None
hasher = hashlib.md5()
try:
with open(path, 'rb') as f:
hasher.update(f.read())
return hasher.hexdigest()
except IOError as e:
logger.warning("Error while calculating file '%s' hash: %s", path, e, exc_info=1)
return None
def _get_pending_conf(categories=[]):
"""Get pending configuration for categories
Iterate over the pending configuration directory for given categories - or
all if empty - and look for files inside. Each file is considered as a
pending configuration file and therefore must be in the same directory
tree than the system file that it replaces.
The result is returned as a dict of categories with pending configuration as
key and a dict of `system_conf_path` => `pending_conf_path` as value.
"""
result = {}
if not os.path.isdir(PENDING_CONF_DIR):
return result
if not categories:
categories = os.listdir(PENDING_CONF_DIR)
for name in categories:
category_pending_path = os.path.join(PENDING_CONF_DIR, name)
if not os.path.isdir(category_pending_path):
continue
path_index = len(category_pending_path)
category_conf = {}
for root, dirs, files in os.walk(category_pending_path):
for filename in files:
pending_path = os.path.join(root, filename)
category_conf[pending_path[path_index:]] = pending_path
if category_conf:
result[name] = category_conf
else:
# remove empty directory
shutil.rmtree(category_pending_path, ignore_errors=True)
return result
def _get_conf_hashes(category):
"""Get the registered conf hashes for a category"""
categories = _get_regenconf_infos()
if category not in categories:
logger.debug("category %s is not in categories.yml yet.", category)
return {}
elif categories[category] is None or 'conffiles' not in categories[category]:
logger.debug("No configuration files for category %s.", category)
return {}
else:
return categories[category]['conffiles']
def _update_conf_hashes(category, hashes):
"""Update the registered conf hashes for a category"""
logger.debug("updating conf hashes for '%s' with: %s",
category, hashes)
categories = _get_regenconf_infos()
category_conf = categories.get(category, {})
# Handle the case where categories[category] is set to null in the yaml
if category_conf is None:
category_conf = {}
category_conf['conffiles'] = hashes
categories[category] = category_conf
_save_regenconf_infos(categories)
def _process_regen_conf(system_conf, new_conf=None, save=True):
"""Regenerate a given system configuration file
Replace a given system configuration file by a new one or delete it if
new_conf is None. A backup of the file - keeping its directory tree - will
be done in the backup conf directory before any operation if save is True.
"""
if save:
backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format(
system_conf.lstrip('/'), datetime.utcnow().strftime("%Y%m%d.%H%M%S")))
backup_dir = os.path.dirname(backup_path)
if not os.path.isdir(backup_dir):
filesystem.mkdir(backup_dir, 0o755, True)
shutil.copy2(system_conf, backup_path)
logger.debug(m18n.n('regenconf_file_backed_up',
conf=system_conf, backup=backup_path))
try:
if not new_conf:
os.remove(system_conf)
logger.debug(m18n.n('regenconf_file_removed',
conf=system_conf))
else:
system_dir = os.path.dirname(system_conf)
if not os.path.isdir(system_dir):
filesystem.mkdir(system_dir, 0o755, True)
shutil.copyfile(new_conf, system_conf)
logger.debug(m18n.n('regenconf_file_updated',
conf=system_conf))
except Exception as e:
logger.warning("Exception while trying to regenerate conf '%s': %s", system_conf, e, exc_info=1)
if not new_conf and os.path.exists(system_conf):
logger.warning(m18n.n('regenconf_file_remove_failed',
conf=system_conf),
exc_info=1)
return False
elif new_conf:
try:
# From documentation:
# Raise an exception if an os.stat() call on either pathname fails.
# (os.stats returns a series of information from a file like type, size...)
copy_succeed = os.path.samefile(system_conf, new_conf)
except:
copy_succeed = False
finally:
if not copy_succeed:
logger.warning(m18n.n('regenconf_file_copy_failed',
conf=system_conf, new=new_conf),
exc_info=1)
return False
return True
def manually_modified_files():
# We do this to have --quiet, i.e. don't throw a whole bunch of logs
# just to fetch this...
# Might be able to optimize this by looking at what the regen conf does
# and only do the part that checks file hashes...
cmd = "yunohost tools regen-conf --dry-run --output-as json --quiet"
j = json.loads(subprocess.check_output(cmd.split()))
# j is something like :
# {"postfix": {"applied": {}, "pending": {"/etc/postfix/main.cf": {"status": "modified"}}}
output = []
for app, actions in j.items():
for action, files in actions.items():
for filename, infos in files.items():
if infos["status"] == "modified":
output.append(filename)
return output
def manually_modified_files_compared_to_debian_default():
# from https://serverfault.com/a/90401
r = subprocess.check_output("dpkg-query -W -f='${Conffiles}\n' '*' \
| awk 'OFS=\" \"{print $2,$1}' \
| md5sum -c 2>/dev/null \
| awk -F': ' '$2 !~ /OK/{print $1}'", shell=True)
return r.strip().split("\n")

View file

@ -26,12 +26,9 @@
import os
import time
import yaml
import json
import subprocess
import shutil
import hashlib
from difflib import unified_diff
from glob import glob
from datetime import datetime
from moulinette import m18n
@ -39,11 +36,7 @@ from yunohost.utils.error import YunohostError
from moulinette.utils import log, filesystem
from yunohost.log import is_unit_operation
from yunohost.hook import hook_callback, hook_list
BASE_CONF_PATH = '/home/yunohost.conf'
BACKUP_CONF_DIR = os.path.join(BASE_CONF_PATH, 'backup')
PENDING_CONF_DIR = os.path.join(BASE_CONF_PATH, 'pending')
MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock"
logger = log.getActionLogger('yunohost.service')
@ -325,11 +318,17 @@ def service_status(names=[]):
result[name] = {
'status': str(status.get("SubState", "unknown")),
'loaded': "enabled" if str(status.get("LoadState", "unknown")) == "loaded" else str(status.get("LoadState", "unknown")),
'loaded': str(status.get("UnitFileState", "unknown")),
'active': str(status.get("ActiveState", "unknown")),
'description': description,
'service_file_path': str(status.get("FragmentPath", "unknown")),
}
# Fun stuff™ : to obtain the enabled/disabled status for sysv services,
# gotta do this ... cf code of /lib/systemd/systemd-sysv-install
if result[name]["loaded"] == "generated":
result[name]["loaded"] = "enabled" if glob("/etc/rc[S5].d/S??"+name) else "disabled"
if "ActiveEnterTimestamp" in status:
result[name]['active_at'] = datetime.utcfromtimestamp(status["ActiveEnterTimestamp"] / 1000000)
else:
@ -343,26 +342,25 @@ def service_status(names=[]):
def _get_service_information_from_systemd(service):
"this is the equivalent of 'systemctl status $service'"
import dbus
from dbus.exceptions import DBusException
d = dbus.SystemBus()
systemd = d.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1')
manager = dbus.Interface(systemd, 'org.freedesktop.systemd1.Manager')
try:
service_path = manager.GetUnit(service + ".service")
except DBusException as exception:
if exception.get_dbus_name() == 'org.freedesktop.systemd1.NoSuchUnit':
return None
raise
service_proxy = d.get_object('org.freedesktop.systemd1', service_path)
# unit_proxy = dbus.Interface(service_proxy, 'org.freedesktop.systemd1.Unit',)
# c.f. https://zignar.net/2014/09/08/getting-started-with-dbus-python-systemd/
# Very interface, much intuitive, wow
service_unit = manager.LoadUnit(service + '.service')
service_proxy = d.get_object('org.freedesktop.systemd1', str(service_unit))
properties_interface = dbus.Interface(service_proxy, 'org.freedesktop.DBus.Properties')
return properties_interface.GetAll('org.freedesktop.systemd1.Unit')
properties = properties_interface.GetAll('org.freedesktop.systemd1.Unit')
if properties.get("LoadState", "not-found") == "not-found":
# Service doesn't really exist
return None
else:
return properties
def service_log(name, number=50):
@ -418,253 +416,25 @@ def service_log(name, number=50):
return result
@is_unit_operation([('names', 'service')])
def service_regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run=False,
def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
list_pending=False):
"""
Regenerate the configuration file(s) for a service
Keyword argument:
names -- Services name to regenerate configuration of
with_diff -- Show differences in case of configuration changes
force -- Override all manual modifications in configuration files
dry_run -- Show what would have been regenerated
list_pending -- List pending configuration files and exit
services = _get_services()
"""
result = {}
if isinstance(names, str):
names = [names]
# Return the list of pending conf
if list_pending:
pending_conf = _get_pending_conf(names)
for name in names:
if name not in services.keys():
raise YunohostError('service_unknown', service=name)
if not with_diff:
return pending_conf
if names is []:
names = services.keys()
for service, conf_files in pending_conf.items():
for system_path, pending_path in conf_files.items():
logger.warning(m18n.n("service_regen_conf_is_deprecated"))
pending_conf[service][system_path] = {
'pending_conf': pending_path,
'diff': _get_files_diff(
system_path, pending_path, True),
}
return pending_conf
if not dry_run:
operation_logger.related_to = [('service', x) for x in names]
if not names:
operation_logger.name_parameter_override = 'all'
elif len(names) != 1:
operation_logger.name_parameter_override = str(len(operation_logger.related_to)) + '_services'
operation_logger.start()
# Clean pending conf directory
if os.path.isdir(PENDING_CONF_DIR):
if not names:
shutil.rmtree(PENDING_CONF_DIR, ignore_errors=True)
else:
for name in names:
shutil.rmtree(os.path.join(PENDING_CONF_DIR, name),
ignore_errors=True)
else:
filesystem.mkdir(PENDING_CONF_DIR, 0o755, True)
# Format common hooks arguments
common_args = [1 if force else 0, 1 if dry_run else 0]
# Execute hooks for pre-regen
pre_args = ['pre', ] + common_args
def _pre_call(name, priority, path, args):
# create the pending conf directory for the service
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
filesystem.mkdir(service_pending_path, 0o755, True, uid='root')
# return the arguments to pass to the script
return pre_args + [service_pending_path, ]
# Don't regen SSH if not specifically specified
if not names:
names = hook_list('conf_regen', list_by='name',
show_info=False)['hooks']
names.remove('ssh')
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
# Keep only the hook names with at least one success
names = [hook for hook, infos in pre_result.items()
if any(result["state"] == "succeed" for result in infos.values())]
# FIXME : what do in case of partial success/failure ...
if not names:
ret_failed = [hook for hook, infos in pre_result.items()
if any(result["state"] == "failed" for result in infos.values())]
raise YunohostError('service_regenconf_failed',
services=', '.join(ret_failed))
# Set the processing method
_regen = _process_regen_conf if not dry_run else lambda *a, **k: True
operation_logger.related_to = []
# Iterate over services and process pending conf
for service, conf_files in _get_pending_conf(names).items():
if not dry_run:
operation_logger.related_to.append(('service', service))
logger.debug(m18n.n(
'service_regenconf_pending_applying' if not dry_run else
'service_regenconf_dry_pending_applying',
service=service))
conf_hashes = _get_conf_hashes(service)
succeed_regen = {}
failed_regen = {}
for system_path, pending_path in conf_files.items():
logger.debug("processing pending conf '%s' to system conf '%s'",
pending_path, system_path)
conf_status = None
regenerated = False
# Get the diff between files
conf_diff = _get_files_diff(
system_path, pending_path, True) if with_diff else None
# Check if the conf must be removed
to_remove = True if os.path.getsize(pending_path) == 0 else False
# Retrieve and calculate hashes
system_hash = _calculate_hash(system_path)
saved_hash = conf_hashes.get(system_path, None)
new_hash = None if to_remove else _calculate_hash(pending_path)
# -> system conf does not exists
if not system_hash:
if to_remove:
logger.debug("> system conf is already removed")
os.remove(pending_path)
continue
if not saved_hash or force:
if force:
logger.debug("> system conf has been manually removed")
conf_status = 'force-created'
else:
logger.debug("> system conf does not exist yet")
conf_status = 'created'
regenerated = _regen(
system_path, pending_path, save=False)
else:
logger.info(m18n.n(
'service_conf_file_manually_removed',
conf=system_path))
conf_status = 'removed'
# -> system conf is not managed yet
elif not saved_hash:
logger.debug("> system conf is not managed yet")
if system_hash == new_hash:
logger.debug("> no changes to system conf has been made")
conf_status = 'managed'
regenerated = True
elif not to_remove:
# If the conf exist but is not managed yet, and is not to be removed,
# we assume that it is safe to regen it, since the file is backuped
# anyway (by default in _regen), as long as we warn the user
# appropriately.
logger.info(m18n.n('service_conf_now_managed_by_yunohost',
conf=system_path))
regenerated = _regen(system_path, pending_path)
conf_status = 'new'
elif force:
regenerated = _regen(system_path)
conf_status = 'force-removed'
else:
logger.info(m18n.n('service_conf_file_kept_back',
conf=system_path, service=service))
conf_status = 'unmanaged'
# -> system conf has not been manually modified
elif system_hash == saved_hash:
if to_remove:
regenerated = _regen(system_path)
conf_status = 'removed'
elif system_hash != new_hash:
regenerated = _regen(system_path, pending_path)
conf_status = 'updated'
else:
logger.debug("> system conf is already up-to-date")
os.remove(pending_path)
continue
else:
logger.debug("> system conf has been manually modified")
if system_hash == new_hash:
logger.debug("> new conf is as current system conf")
conf_status = 'managed'
regenerated = True
elif force:
regenerated = _regen(system_path, pending_path)
conf_status = 'force-updated'
else:
logger.warning(m18n.n(
'service_conf_file_manually_modified',
conf=system_path))
conf_status = 'modified'
# Store the result
conf_result = {'status': conf_status}
if conf_diff is not None:
conf_result['diff'] = conf_diff
if regenerated:
succeed_regen[system_path] = conf_result
conf_hashes[system_path] = new_hash
if os.path.isfile(pending_path):
os.remove(pending_path)
else:
failed_regen[system_path] = conf_result
# Check for service conf changes
if not succeed_regen and not failed_regen:
logger.debug(m18n.n('service_conf_up_to_date', service=service))
continue
elif not failed_regen:
logger.success(m18n.n(
'service_conf_updated' if not dry_run else
'service_conf_would_be_updated',
service=service))
if succeed_regen and not dry_run:
_update_conf_hashes(service, conf_hashes)
# Append the service results
result[service] = {
'applied': succeed_regen,
'pending': failed_regen
}
# Return in case of dry run
if dry_run:
return result
# Execute hooks for post-regen
post_args = ['post', ] + common_args
def _pre_call(name, priority, path, args):
# append coma-separated applied changes for the service
if name in result and result[name]['applied']:
regen_conf_files = ','.join(result[name]['applied'].keys())
else:
regen_conf_files = ''
return post_args + [regen_conf_files, ]
hook_callback('conf_regen', names, pre_callback=_pre_call)
operation_logger.success()
return result
from yunohost.regenconf import regen_conf
return regen_conf(names, with_diff, force, dry_run, list_pending)
def _run_service_command(action, service):
@ -864,231 +634,9 @@ def _find_previous_log_file(file):
return None
def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True):
"""Compare two files and return the differences
Read and compare two files. The differences are returned either as a delta
in unified diff format or a formatted string if as_string is True. The
header can also be removed if skip_header is True.
"""
if os.path.exists(orig_file):
with open(orig_file, 'r') as orig_file:
orig_file = orig_file.readlines()
else:
orig_file = []
if os.path.exists(new_file):
with open(new_file, 'r') as new_file:
new_file = new_file.readlines()
else:
new_file = []
# Compare files and format output
diff = unified_diff(orig_file, new_file)
if skip_header:
try:
next(diff)
next(diff)
except:
pass
if as_string:
return ''.join(diff).rstrip()
return diff
def _calculate_hash(path):
"""Calculate the MD5 hash of a file"""
if not os.path.exists(path):
return None
hasher = hashlib.md5()
try:
with open(path, 'rb') as f:
hasher.update(f.read())
return hasher.hexdigest()
except IOError as e:
logger.warning("Error while calculating file '%s' hash: %s", path, e, exc_info=1)
return None
def _get_pending_conf(services=[]):
"""Get pending configuration for service(s)
Iterate over the pending configuration directory for given service(s) - or
all if empty - and look for files inside. Each file is considered as a
pending configuration file and therefore must be in the same directory
tree than the system file that it replaces.
The result is returned as a dict of services with pending configuration as
key and a dict of `system_conf_path` => `pending_conf_path` as value.
"""
result = {}
if not os.path.isdir(PENDING_CONF_DIR):
return result
if not services:
services = os.listdir(PENDING_CONF_DIR)
for name in services:
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
if not os.path.isdir(service_pending_path):
continue
path_index = len(service_pending_path)
service_conf = {}
for root, dirs, files in os.walk(service_pending_path):
for filename in files:
pending_path = os.path.join(root, filename)
service_conf[pending_path[path_index:]] = pending_path
if service_conf:
result[name] = service_conf
else:
# remove empty directory
shutil.rmtree(service_pending_path, ignore_errors=True)
return result
def _get_conf_hashes(service):
"""Get the registered conf hashes for a service"""
services = _get_services()
if service not in services:
logger.debug("Service %s is not in services.yml yet.", service)
return {}
elif services[service] is None or 'conffiles' not in services[service]:
logger.debug("No configuration files for service %s.", service)
return {}
else:
return services[service]['conffiles']
def _update_conf_hashes(service, hashes):
"""Update the registered conf hashes for a service"""
logger.debug("updating conf hashes for '%s' with: %s",
service, hashes)
services = _get_services()
service_conf = services.get(service, {})
# Handle the case where services[service] is set to null in the yaml
if service_conf is None:
service_conf = {}
service_conf['conffiles'] = hashes
services[service] = service_conf
_save_services(services)
def _process_regen_conf(system_conf, new_conf=None, save=True):
"""Regenerate a given system configuration file
Replace a given system configuration file by a new one or delete it if
new_conf is None. A backup of the file - keeping its directory tree - will
be done in the backup conf directory before any operation if save is True.
"""
if save:
backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format(
system_conf.lstrip('/'), datetime.utcnow().strftime("%Y%m%d.%H%M%S")))
backup_dir = os.path.dirname(backup_path)
if not os.path.isdir(backup_dir):
filesystem.mkdir(backup_dir, 0o755, True)
shutil.copy2(system_conf, backup_path)
logger.debug(m18n.n('service_conf_file_backed_up',
conf=system_conf, backup=backup_path))
try:
if not new_conf:
os.remove(system_conf)
logger.debug(m18n.n('service_conf_file_removed',
conf=system_conf))
else:
system_dir = os.path.dirname(system_conf)
if not os.path.isdir(system_dir):
filesystem.mkdir(system_dir, 0o755, True)
shutil.copyfile(new_conf, system_conf)
logger.debug(m18n.n('service_conf_file_updated',
conf=system_conf))
except Exception as e:
logger.warning("Exception while trying to regenerate conf '%s': %s", system_conf, e, exc_info=1)
if not new_conf and os.path.exists(system_conf):
logger.warning(m18n.n('service_conf_file_remove_failed',
conf=system_conf),
exc_info=1)
return False
elif new_conf:
try:
# From documentation:
# Raise an exception if an os.stat() call on either pathname fails.
# (os.stats returns a series of information from a file like type, size...)
copy_succeed = os.path.samefile(system_conf, new_conf)
except:
copy_succeed = False
finally:
if not copy_succeed:
logger.warning(m18n.n('service_conf_file_copy_failed',
conf=system_conf, new=new_conf),
exc_info=1)
return False
return True
def manually_modified_files():
# We do this to have --quiet, i.e. don't throw a whole bunch of logs
# just to fetch this...
# Might be able to optimize this by looking at what service_regenconf does
# and only do the part that checks file hashes...
cmd = "yunohost service regen-conf --dry-run --output-as json --quiet"
j = json.loads(subprocess.check_output(cmd.split()))
# j is something like :
# {"postfix": {"applied": {}, "pending": {"/etc/postfix/main.cf": {"status": "modified"}}}
output = []
for app, actions in j.items():
for action, files in actions.items():
for filename, infos in files.items():
if infos["status"] == "modified":
output.append(filename)
return output
def _get_journalctl_logs(service, number="all"):
try:
return subprocess.check_output("journalctl -xn -u {0} -n{1}".format(service, number), shell=True)
except:
import traceback
return "error while get services logs from journalctl:\n%s" % traceback.format_exc()
def manually_modified_files_compared_to_debian_default():
# from https://serverfault.com/a/90401
r = subprocess.check_output("dpkg-query -W -f='${Conffiles}\n' '*' \
| awk 'OFS=\" \"{print $2,$1}' \
| md5sum -c 2>/dev/null \
| awk -F': ' '$2 !~ /OK/{print $1}'", shell=True)
return r.strip().split("\n")

View file

@ -7,6 +7,7 @@ from collections import OrderedDict
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.service import service_regen_conf
logger = getActionLogger('yunohost.settings')
@ -39,6 +40,10 @@ DEFAULTS = OrderedDict([
("security.password.admin.strength", {"type": "int", "default": 1}),
("security.password.user.strength", {"type": "int", "default": 1}),
("service.ssh.allow_deprecated_dsa_hostkey", {"type": "bool", "default": False}),
("security.ssh.compatibility", {"type": "enum", "default": "modern",
"choices": ["intermediate", "modern"]}),
("security.nginx.compatibility", {"type": "enum", "default": "intermediate",
"choices": ["intermediate", "modern"]}),
])
@ -109,8 +114,8 @@ def settings_set(key, value):
elif key_type == "enum":
if value not in settings[key]["choices"]:
raise YunohostError('global_settings_bad_choice_for_enum', setting=key,
received_type=type(value).__name__,
expected_type=", ".join(settings[key]["choices"]))
choice=value,
available_choices=", ".join(settings[key]["choices"]))
else:
raise YunohostError('global_settings_unknown_type', setting=key,
unknown_type=key_type)
@ -278,10 +283,12 @@ def trigger_post_change_hook(setting_name, old_value, new_value):
#
# ===========================================
@post_change_hook("security.nginx.compatibility")
def reconfigure_nginx(setting_name, old_value, new_value):
if old_value != new_value:
service_regen_conf(names=['nginx'])
#@post_change_hook("example.int")
#def myfunc(setting_name, old_value, new_value):
# print("In hook")
# print(setting_name)
# print(old_value)
# print(new_value)
@post_change_hook("security.ssh.compatibility")
def reconfigure_ssh(setting_name, old_value, new_value):
if old_value != new_value:
service_regen_conf(names=['ssh'])

View file

@ -30,6 +30,7 @@ import json
import subprocess
import pwd
import socket
from glob import glob
from xmlrpclib import Fault
from importlib import import_module
from collections import OrderedDict
@ -48,7 +49,8 @@ from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, a
from yunohost.domain import domain_add, domain_list, _get_maindomain, _set_maindomain
from yunohost.dyndns import _dyndns_available, _dyndns_provides
from yunohost.firewall import firewall_upnp
from yunohost.service import service_status, service_regen_conf, service_log, service_start, service_enable
from yunohost.service import service_status, service_log, service_start, service_enable
from yunohost.regenconf import regen_conf
from yunohost.monitor import monitor_disk, monitor_system
from yunohost.utils.packages import ynh_packages_version
from yunohost.utils.network import get_public_ip
@ -133,6 +135,11 @@ def tools_adminpw(auth, new_password, check_strength=True):
if check_strength:
assert_password_is_strong_enough("admin", new_password)
# UNIX seems to not like password longer than 127 chars ...
# e.g. SSH login gets broken (or even 'su admin' when entering the password)
if len(new_password) >= 127:
raise YunohostError('admin_password_too_long')
new_hash = _hash_user_password(new_password)
try:
@ -208,7 +215,7 @@ def tools_maindomain(operation_logger, auth, new_domain=None):
# Regen configurations
try:
with open('/etc/yunohost/installed', 'r'):
service_regen_conf()
regen_conf()
except IOError:
pass
@ -326,7 +333,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
operation_logger.start()
logger.info(m18n.n('yunohost_installing'))
service_regen_conf(['nslcd', 'nsswitch'], force=True)
regen_conf(['nslcd', 'nsswitch'], force=True)
# Initialize LDAP for YunoHost
# TODO: Improve this part by integrate ldapinit into conf_regen hook
@ -377,7 +384,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
os.system('chmod 644 /etc/ssowat/conf.json.persistent')
# Create SSL CA
service_regen_conf(['ssl'], force=True)
regen_conf(['ssl'], force=True)
ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
# (Update the serial so that it's specific to this very instance)
os.system("openssl rand -hex 19 > %s/serial" % ssl_dir)
@ -406,7 +413,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
logger.success(m18n.n('yunohost_ca_creation_success'))
# New domain config
service_regen_conf(['nsswitch'], force=True)
regen_conf(['nsswitch'], force=True)
domain_add(auth, domain, dyndns)
tools_maindomain(auth, domain)
@ -416,10 +423,10 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
# Enable UPnP silently and reload firewall
firewall_upnp('enable', no_refresh=True)
# Setup the default official app list with cron job
# Setup the default apps list with cron job
try:
app_fetchlist(name="yunohost",
url="https://app.yunohost.org/official.json")
url="https://app.yunohost.org/apps.json")
except Exception as e:
logger.warning(str(e))
@ -434,7 +441,7 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
service_enable("yunohost-firewall")
service_start("yunohost-firewall")
service_regen_conf(force=True)
regen_conf(force=True)
# Restore original ssh conf, as chosen by the
# admin during the initial install
@ -451,13 +458,18 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
else:
# We need to explicitly ask the regen conf to regen ssh
# (by default, i.e. first argument = None, it won't because it's too touchy)
service_regen_conf(names=["ssh"], force=True)
regen_conf(names=["ssh"], force=True)
logger.success(m18n.n('yunohost_configured'))
logger.warning(m18n.n('recommend_to_add_first_user'))
def tools_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
list_pending=False):
return regen_conf(names, with_diff, force, dry_run, list_pending)
def tools_update(ignore_apps=False, ignore_packages=False):
"""
Update apps & package cache, then display changelog
@ -476,12 +488,21 @@ def tools_update(ignore_apps=False, ignore_packages=False):
command = "LC_ALL=C apt update"
# TODO : add @is_unit_operation to tools_update so that the
# debug output can be fetched when there's an issue...
# Filter boring message about "apt not having a stable CLI interface"
# Also keep track of wether or not we encountered a warning...
warnings = []
def is_legit_warning(m):
legit_warning = m.rstrip() and "apt does not have a stable CLI interface" not in m.rstrip()
if legit_warning:
warnings.append(m)
return legit_warning
callbacks = (
# stdout goes to debug
lambda l: logger.debug(l.rstrip()),
# stderr goes to warning
# FIXME : filter the damn "CLI interface not stable" from apt >.>
lambda l: logger.warning(l.rstrip()),
# stderr goes to warning except for the boring apt messages
lambda l: logger.warning(l.rstrip()) if is_legit_warning(l) else logger.debug(l.rstrip())
)
logger.info(m18n.n('updating_apt_cache'))
@ -489,12 +510,9 @@ def tools_update(ignore_apps=False, ignore_packages=False):
returncode = call_async_output(command, callbacks, shell=True)
if returncode != 0:
# TODO : here, we should run something like a
# `cat /etc/apt/sources.list /etc/apt/sources.list.d/*`
# and append it to the error message to improve debugging
raise YunohostError('update_cache_failed')
raise YunohostError('update_apt_cache_failed', sourceslist='\n'.join(_dump_sources_list()))
elif warnings:
logger.error(m18n.n('update_apt_cache_warning', sourceslist='\n'.join(_dump_sources_list())))
packages = list(_list_upgradable_apt_packages())
logger.debug(m18n.n('done'))
@ -552,6 +570,17 @@ def _list_upgradable_apt_packages():
}
def _dump_sources_list():
filenames = glob("/etc/apt/sources.list") + glob("/etc/apt/sources.list.d/*")
for filename in filenames:
with open(filename, "r") as f:
for line in f.readlines():
if line.startswith("#") or not line.strip():
continue
yield filename.replace("/etc/apt/", "") + ":" + line.strip()
@is_unit_operation()
def tools_upgrade(operation_logger, auth, ignore_apps=False, ignore_packages=False):
"""
@ -769,12 +798,13 @@ def tools_diagnosis(auth, private=False):
}
# nginx -t
try:
diagnosis['nginx'] = check_output("nginx -t").strip().split("\n")
except Exception as e:
import traceback
traceback.print_exc()
logger.warning("Unable to check 'nginx -t', exception: %s" % e)
p = subprocess.Popen("nginx -t".split(),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, _ = p.communicate()
diagnosis["nginx"] = out.strip().split("\n")
if p.returncode != 0:
logger.error(out)
# Services status
services = service_status()
@ -806,7 +836,7 @@ def tools_diagnosis(auth, private=False):
# Domains
diagnosis['private']['domains'] = domain_list(auth)['domains']
diagnosis['private']['regen_conf'] = service_regen_conf(with_diff=True, dry_run=True)
diagnosis['private']['regen_conf'] = regen_conf(with_diff=True, dry_run=True)
try:
diagnosis['security'] = {