Merge branch 'dev' into refactoring-yunohost-format-arguments-parsing

This commit is contained in:
Kayou 2020-09-08 18:05:02 +02:00 committed by GitHub
commit ddb2be23a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
130 changed files with 3356 additions and 3297 deletions

View file

@ -13,10 +13,3 @@
## How to test ## How to test
... ...
## Validation
- [ ] Principle agreement 0/2 :
- [ ] Quick review 0/1 :
- [ ] Simple test 0/1 :
- [ ] Deep review 0/1 :

View file

@ -38,7 +38,8 @@ build-ssowat:
variables: variables:
PACKAGE: "ssowat" PACKAGE: "ssowat"
script: script:
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?" | head -n 1)
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE
- *build_script - *build_script
@ -47,6 +48,7 @@ build-moulinette:
variables: variables:
PACKAGE: "moulinette" PACKAGE: "moulinette"
script: script:
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE - DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9]+([.][0-9]+)?" | head -n 1)
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE - DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE
- *build_script - *build_script

View file

@ -16,6 +16,7 @@ upgrade:
extends: .install-stage extends: .install-stage
image: "after-install" image: "after-install"
script: script:
- apt-get update -o Acquire::Retries=3
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
@ -23,5 +24,6 @@ install-postinstall:
extends: .install-stage extends: .install-stage
image: "before-install" image: "before-install"
script: script:
- apt-get update -o Acquire::Retries=3
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
- yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns

View file

@ -1,24 +1,43 @@
######################################## ########################################
# LINTER # LINTER
######################################## ########################################
# later we must fix lint and format-check jobs and remove "allow_failure"
lint: lint27:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
allow_failure: true allow_failure: true
script: script:
- tox -e lint - tox -e py27-lint
invalidcode: lint37:
stage: lint
image: "before-install"
needs: []
allow_failure: true
script:
- tox -e py37-lint
invalidcode27:
stage: lint stage: lint
image: "before-install" image: "before-install"
needs: [] needs: []
script: script:
- tox -e invalidcode - tox -e py27-invalidcode
# Disabled, waiting for buster invalidcode37:
#format-check: stage: lint
# extends: .lint-stage image: "before-install"
# script: allow_failure: true
# - black --check --diff needs: []
script:
- tox -e py37-invalidcode
format-check:
stage: lint
image: "before-install"
needs: []
allow_failure: true
script:
- tox -e py37-black

View file

@ -1,4 +1,5 @@
.install_debs: &install_debs .install_debs: &install_debs
- apt-get update -o Acquire::Retries=3
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb - DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
.test-stage: .test-stage:
@ -35,7 +36,7 @@ full-tests:
- *install_debs - *install_debs
- yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns - yunohost tools postinstall -d domain.tld -p the_password --ignore-dyndns
script: script:
- pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml - python -m pytest --cov=yunohost tests/ src/yunohost/tests/ --junitxml=report.xml
needs: needs:
- job: build-yunohost - job: build-yunohost
artifacts: true artifacts: true
@ -50,70 +51,70 @@ full-tests:
root-tests: root-tests:
extends: .test-stage extends: .test-stage
script: script:
- py.test tests - python -m pytest tests
test-apps: test-apps:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_apps.py - python -m pytest tests/test_apps.py
test-appscatalog: test-appscatalog:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_appscatalog.py - python -m pytest tests/test_appscatalog.py
test-appurl: test-appurl:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_appurl.py - python -m pytest tests/test_appurl.py
test-apps-arguments-parsing: test-apps-arguments-parsing:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_apps_arguments_parsing.py - python -m pytest tests/test_apps_arguments_parsing.py
test-backuprestore: test-backuprestore:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_backuprestore.py - python -m pytest tests/test_backuprestore.py
test-changeurl: test-changeurl:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_changeurl.py - python -m pytest tests/test_changeurl.py
test-permission: test-permission:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_permission.py - python -m pytest tests/test_permission.py
test-settings: test-settings:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_settings.py - python -m pytest tests/test_settings.py
test-user-group: test-user-group:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_user-group.py - python -m pytest tests/test_user-group.py
test-regenconf: test-regenconf:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_regenconf.py - python -m pytest tests/test_regenconf.py
test-service: test-service:
extends: .test-stage extends: .test-stage
script: script:
- cd src/yunohost - cd src/yunohost
- py.test tests/test_service.py - python -m pytest tests/test_service.py

View file

@ -2,12 +2,18 @@ language: python
matrix: matrix:
allow_failures: allow_failures:
- env: TOXENV=lint - env: TOXENV=py27-lint
- env: TOXENV=py37-lint
- env: TOXENV=py37-invalidcode
include: include:
- python: 2.7 - python: 2.7
env: TOXENV=py27 env: TOXENV=py27-lint
- python: 2.7 - python: 2.7
env: TOXENV=lint env: TOXENV=py27-invalidcode
- python: 3.7
env: TOXENV=py37-lint
- python: 3.7
env: TOXENV=py37-invalidcode
install: install:
- pip install tox - pip install tox

View file

@ -5,70 +5,28 @@ import os
import sys import sys
import argparse import argparse
# Either we are in a development environment or not sys.path.insert(0, "/usr/lib/moulinette/")
IN_DEVEL = False import yunohost
# Level for which loggers will log
LOGGERS_LEVEL = 'DEBUG'
TTY_LOG_LEVEL = 'INFO'
# Handlers that will be used by loggers
# - file: log to the file LOG_DIR/LOG_FILE
# - tty: log to current tty
LOGGERS_HANDLERS = ['file', 'tty']
# Directory and file to be used by logging
LOG_DIR = '/var/log/yunohost'
LOG_FILE = 'yunohost-cli.log'
# Check and load - as needed - development environment
if not __file__.startswith('/usr/'):
IN_DEVEL = True
if IN_DEVEL:
basedir = os.path.abspath('%s/../' % os.path.dirname(__file__))
if os.path.isdir(os.path.join(basedir, 'moulinette')):
sys.path.insert(0, basedir)
LOG_DIR = os.path.join(basedir, 'log')
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize, get_locale
# Initialization & helpers functions -----------------------------------
def _die(message, title='Error:'):
"""Print error message and exit"""
print('%s %s' % (colorize(title, 'red'), message))
sys.exit(1)
def _parse_cli_args(): def _parse_cli_args():
"""Parse additional arguments for the cli""" """Parse additional arguments for the cli"""
parser = argparse.ArgumentParser(add_help=False) parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('--no-cache',
action='store_false', default=True, dest='use_cache',
help="Don't use actions map cache",
)
parser.add_argument('--output-as', parser.add_argument('--output-as',
choices=['json', 'plain', 'none'], default=None, choices=['json', 'plain', 'none'], default=None,
help="Output result in another format", help="Output result in another format"
) )
parser.add_argument('--debug', parser.add_argument('--debug',
action='store_true', default=False, action='store_true', default=False,
help="Log and print debug messages", help="Log and print debug messages"
) )
parser.add_argument('--quiet', parser.add_argument('--quiet',
action='store_true', default=False, action='store_true', default=False,
help="Don't produce any output", help="Don't produce any output"
) )
parser.add_argument('--timeout', parser.add_argument('--timeout',
type=int, default=None, type=int, default=None,
help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock", help="Number of seconds before this command will timeout because it can't acquire the lock (meaning that another command is currently running), by default there is no timeout and the command will wait until it can get the lock"
)
parser.add_argument('--admin-password',
default=None, dest='password', metavar='PASSWORD',
help="The admin password to use to authenticate",
) )
# deprecated arguments # deprecated arguments
parser.add_argument('--plain', parser.add_argument('--plain',
@ -88,96 +46,6 @@ def _parse_cli_args():
return (parser, opts, args) return (parser, opts, args)
def _init_moulinette(debug=False, quiet=False):
"""Configure logging and initialize the moulinette"""
# Define loggers handlers
handlers = set(LOGGERS_HANDLERS)
if quiet and 'tty' in handlers:
handlers.remove('tty')
elif 'tty' not in handlers:
handlers.append('tty')
root_handlers = set(handlers)
if not debug and 'tty' in root_handlers:
root_handlers.remove('tty')
# Define loggers level
level = LOGGERS_LEVEL
tty_level = TTY_LOG_LEVEL
if debug:
tty_level = 'DEBUG'
# Custom logging configuration
logging = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'tty-debug': {
'format': '%(relativeCreated)-4d %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'tty': {
'level': tty_level,
'class': 'moulinette.interfaces.cli.TTYHandler',
'formatter': 'tty-debug' if debug else '',
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'precise',
'filename': '%s/%s' % (LOG_DIR, LOG_FILE),
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': level,
'handlers': handlers,
'propagate': False,
},
'moulinette': {
'level': level,
'handlers': [],
'propagate': True,
},
'moulinette.interface': {
'level': level,
'handlers': handlers,
'propagate': False,
},
},
'root': {
'level': level,
'handlers': root_handlers,
},
}
# Create log directory
if not os.path.isdir(LOG_DIR):
try:
os.makedirs(LOG_DIR, 0750)
except os.error as e:
_die(str(e))
# Initialize moulinette
moulinette.init(logging_config=logging, _from_source=IN_DEVEL)
def _retrieve_namespaces():
"""Return the list of namespaces to load"""
ret = ['yunohost']
for n in ActionsMap.get_namespaces():
# Append YunoHost modules
if n.startswith('ynh_'):
ret.append(n)
return ret
# Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ... # Stupid PATH management because sometimes (e.g. some cron job) PATH is only /usr/bin:/bin ...
default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" default_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
@ -188,33 +56,18 @@ if os.environ["PATH"] != default_path:
if __name__ == '__main__': if __name__ == '__main__':
if os.geteuid() != 0: if os.geteuid() != 0:
# since moulinette isn't initialized, we can't use m18n here sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be "
sys.stderr.write("\033[1;31mError:\033[0m yunohost command must be " \
"run as root or with sudo.\n") "run as root or with sudo.\n")
sys.exit(1) sys.exit(1)
parser, opts, args = _parse_cli_args() parser, opts, args = _parse_cli_args()
_init_moulinette(opts.debug, opts.quiet)
# Check that YunoHost is installed
if not os.path.isfile('/etc/yunohost/installed') and \
(len(args) < 2 or (args[0] +' '+ args[1] != 'tools postinstall' and \
args[0] +' '+ args[1] != 'backup restore' and \
args[0] +' '+ args[1] != 'log display')):
from moulinette import m18n
# Init i18n
m18n.load_namespace('yunohost')
m18n.set_locale(get_locale())
# Print error and exit
_die(m18n.n('yunohost_not_installed'), m18n.g('error'))
# Execute the action # Execute the action
ret = moulinette.cli( yunohost.cli(
_retrieve_namespaces(), args, debug=opts.debug,
use_cache=opts.use_cache, output_as=opts.output_as, quiet=opts.quiet,
password=opts.password, parser_kwargs={'top_parser': parser}, output_as=opts.output_as,
timeout=opts.timeout, timeout=opts.timeout,
args=args,
parser=parser
) )
sys.exit(ret)

View file

@ -1,52 +1,16 @@
#! /usr/bin/python #! /usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
import sys import sys
import argparse import argparse
# Either we are in a development environment or not sys.path.insert(0, "/usr/lib/moulinette/")
IN_DEVEL = False import yunohost
# Default server configuration # Default server configuration
DEFAULT_HOST = 'localhost' DEFAULT_HOST = 'localhost'
DEFAULT_PORT = 6787 DEFAULT_PORT = 6787
# Level for which loggers will log
LOGGERS_LEVEL = 'DEBUG'
API_LOGGER_LEVEL = 'INFO'
# Handlers that will be used by loggers
# - file: log to the file LOG_DIR/LOG_FILE
# - api: serve logs through the api
# - console: log to stderr
LOGGERS_HANDLERS = ['file', 'api']
# Directory and file to be used by logging
LOG_DIR = '/var/log/yunohost'
LOG_FILE = 'yunohost-api.log'
# Check and load - as needed - development environment
if not __file__.startswith('/usr/'):
IN_DEVEL = True
if IN_DEVEL:
basedir = os.path.abspath('%s/../' % os.path.dirname(__file__))
if os.path.isdir(os.path.join(basedir, 'moulinette')):
sys.path.insert(0, basedir)
LOG_DIR = os.path.join(basedir, 'log')
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize
# Initialization & helpers functions -----------------------------------
def _die(message, title='Error:'):
"""Print error message and exit"""
print('%s %s' % (colorize(title, 'red'), message))
sys.exit(1)
def _parse_api_args(): def _parse_api_args():
"""Parse main arguments for the api""" """Parse main arguments for the api"""
@ -62,149 +26,19 @@ def _parse_api_args():
action='store', default=DEFAULT_PORT, type=int, action='store', default=DEFAULT_PORT, type=int,
help="Port to listen on (default: %d)" % DEFAULT_PORT, help="Port to listen on (default: %d)" % DEFAULT_PORT,
) )
srv_group.add_argument('--no-websocket',
action='store_true', default=True, dest='use_websocket',
help="Serve without WebSocket support, used to handle "
"asynchronous responses such as the messages",
)
glob_group = parser.add_argument_group('global arguments') glob_group = parser.add_argument_group('global arguments')
glob_group.add_argument('--no-cache',
action='store_false', default=True, dest='use_cache',
help="Don't use actions map cache",
)
glob_group.add_argument('--debug', glob_group.add_argument('--debug',
action='store_true', default=False, action='store_true', default=False,
help="Set log level to DEBUG", help="Set log level to DEBUG",
) )
glob_group.add_argument('--verbose',
action='store_true', default=False,
help="Be verbose in the output",
)
glob_group.add_argument('--help', glob_group.add_argument('--help',
action='help', help="Show this help message and exit", action='help', help="Show this help message and exit",
) )
return parser.parse_args() return parser.parse_args()
def _init_moulinette(use_websocket=True, debug=False, verbose=False):
"""Configure logging and initialize the moulinette"""
# Define loggers handlers
handlers = set(LOGGERS_HANDLERS)
if not use_websocket and 'api' in handlers:
handlers.remove('api')
if verbose and 'console' not in handlers:
handlers.add('console')
root_handlers = handlers - set(['api'])
# Define loggers level
level = LOGGERS_LEVEL
api_level = API_LOGGER_LEVEL
if debug:
level = 'DEBUG'
api_level = 'DEBUG'
# Custom logging configuration
logging = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'console': {
'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'api': {
'level': api_level,
'class': 'moulinette.interfaces.api.APIQueueHandler',
},
'file': {
'class': 'logging.handlers.WatchedFileHandler',
'formatter': 'precise',
'filename': '%s/%s' % (LOG_DIR, LOG_FILE),
'filters': ['action'],
},
'console': {
'class': 'logging.StreamHandler',
'formatter': 'console',
'stream': 'ext://sys.stdout',
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': level,
'handlers': handlers,
'propagate': False,
},
'moulinette': {
'level': level,
'handlers': [],
'propagate': True,
},
'gnupg': {
'level': 'INFO',
'handlers': [],
'propagate': False,
},
},
'root': {
'level': level,
'handlers': root_handlers,
},
}
# Create log directory
if not os.path.isdir(LOG_DIR):
try:
os.makedirs(LOG_DIR, 0750)
except os.error as e:
_die(str(e))
# Initialize moulinette
moulinette.init(logging_config=logging, _from_source=IN_DEVEL)
def _retrieve_namespaces():
"""Return the list of namespaces to load"""
ret = ['yunohost']
for n in ActionsMap.get_namespaces():
# Append YunoHost modules
if n.startswith('ynh_'):
ret.append(n)
return ret
# Callbacks for additional routes --------------------------------------
def is_installed():
"""
Check whether YunoHost is installed or not
"""
installed = False
if os.path.isfile('/etc/yunohost/installed'):
installed = True
return { 'installed': installed }
# Main action ----------------------------------------------------------
if __name__ == '__main__': if __name__ == '__main__':
opts = _parse_api_args() opts = _parse_api_args()
_init_moulinette(opts.use_websocket, opts.debug, opts.verbose)
# Run the server # Run the server
ret = moulinette.api( yunohost.api(debug=opts.debug, host=opts.host, port=opts.port)
_retrieve_namespaces(),
host=opts.host, port=opts.port, routes={
('GET', '/installed'): is_installed,
}, use_cache=opts.use_cache, use_websocket=opts.use_websocket
)
sys.exit(ret)

View file

@ -99,13 +99,7 @@ user:
- "pattern_lastname" - "pattern_lastname"
-m: -m:
full: --mail full: --mail
help: Main unique email address help: (Deprecated, see --domain) Main unique email address
extra:
ask: ask_email
required: True
pattern: &pattern_email
- !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$
- "pattern_email"
-p: -p:
full: --password full: --password
help: User password help: User password
@ -116,6 +110,13 @@ user:
- !!str ^.{3,}$ - !!str ^.{3,}$
- "pattern_password" - "pattern_password"
comment: good_practices_about_user_password comment: good_practices_about_user_password
-d:
full: --domain
help: Domain for the email address and xmpp account
extra:
pattern: &pattern_domain
- !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$
- "pattern_domain"
-q: -q:
full: --mailbox-quota full: --mailbox-quota
help: Mailbox size quota help: Mailbox size quota
@ -157,7 +158,9 @@ user:
-m: -m:
full: --mail full: --mail
extra: extra:
pattern: *pattern_email pattern: &pattern_email
- !!str ^[\w.-]+@([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+((xn--)?[^\W_]{2,})$
- "pattern_email"
-p: -p:
full: --change-password full: --change-password
help: New password to set help: New password to set
@ -419,9 +422,7 @@ domain:
domain: domain:
help: Domain name to add help: Domain name to add
extra: extra:
pattern: &pattern_domain pattern: *pattern_domain
- !!str ^([^\W_A-Z]+([-]*[^\W_A-Z]+)*\.)+([^\W\d_]{2,})$
- "pattern_domain"
-d: -d:
full: --dyndns full: --dyndns
help: Subscribe to the DynDNS service help: Subscribe to the DynDNS service
@ -660,6 +661,10 @@ app:
-f: -f:
full: --file full: --file
help: Folder or tarball for upgrade help: Folder or tarball for upgrade
-F:
full: --force
help: Force the update, even though the app is up to date
action: store_true
### app_change_url() ### app_change_url()
change-url: change-url:
@ -847,10 +852,6 @@ backup:
-o: -o:
full: --output-directory full: --output-directory
help: Output directory for the backup help: Output directory for the backup
-r:
full: --no-compress
help: Do not create an archive file
action: store_true
--methods: --methods:
help: List of backup methods to apply (copy or tar by default) help: List of backup methods to apply (copy or tar by default)
nargs: "*" nargs: "*"
@ -1631,17 +1632,19 @@ log:
action_help: List logs action_help: List logs
api: GET /logs api: GET /logs
arguments: arguments:
category:
help: Log category to display (default operations), could be operation, history, package, system, access, service or app
nargs: "*"
-l: -l:
full: --limit full: --limit
help: Maximum number of logs help: Maximum number of operations to list (default to 50)
type: int type: int
default: 50
-d: -d:
full: --with-details full: --with-details
help: Show additional infos (e.g. operation success) but may significantly increase command time. Consider using --limit in combination with this. help: Show additional infos (e.g. operation success) but may significantly increase command time. Consider using --limit in combination with this.
action: store_true action: store_true
-s:
full: --with-suboperations
help: Include metadata about operations that are not the main operation but are sub-operations triggered by another ongoing operation... (e.g. initializing groups/permissions when installing an app)
action: store_true
### log_display() ### log_display()
display: display:
@ -1662,6 +1665,10 @@ log:
full: --filter-irrelevant full: --filter-irrelevant
help: Do not show some lines deemed not relevant (like set +x or helper argument parsing) help: Do not show some lines deemed not relevant (like set +x or helper argument parsing)
action: store_true action: store_true
-s:
full: --with-suboperations
help: Include metadata about sub-operations of this operation... (e.g. initializing groups/permissions when installing an app)
action: store_true
############################# #############################

View file

@ -15,6 +15,7 @@ THIS_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ACTIONSMAP_FILE = THIS_SCRIPT_DIR + '/yunohost.yml' ACTIONSMAP_FILE = THIS_SCRIPT_DIR + '/yunohost.yml'
BASH_COMPLETION_FILE = THIS_SCRIPT_DIR + '/../bash-completion.d/yunohost' BASH_COMPLETION_FILE = THIS_SCRIPT_DIR + '/../bash-completion.d/yunohost'
def get_dict_actions(OPTION_SUBTREE, category): def get_dict_actions(OPTION_SUBTREE, category):
ACTIONS = [action for action in OPTION_SUBTREE[category]["actions"].keys() ACTIONS = [action for action in OPTION_SUBTREE[category]["actions"].keys()
if not action.startswith('_')] if not action.startswith('_')]
@ -24,6 +25,7 @@ def get_dict_actions(OPTION_SUBTREE, category):
return DICT return DICT
with open(ACTIONSMAP_FILE, 'r') as stream: with open(ACTIONSMAP_FILE, 'r') as stream:
# Getting the dictionary containning what actions are possible per category # Getting the dictionary containning what actions are possible per category

View file

@ -1,7 +1,8 @@
# -*- shell-script -*- # -*- shell-script -*-
# TODO : use --regex to validate against a namespace set +x
for helper in $(run-parts --list /usr/share/yunohost/helpers.d 2>/dev/null) ; do for helper in $(run-parts --list /usr/share/yunohost/helpers.d 2>/dev/null) ; do
[ -r $helper ] && . $helper || true [ -r $helper ] && . $helper || true
done done
set -x

View file

@ -100,7 +100,7 @@ ynh_package_version() {
# Requires YunoHost version 2.4.0.3 or higher. # Requires YunoHost version 2.4.0.3 or higher.
ynh_apt() { ynh_apt() {
ynh_wait_dpkg_free ynh_wait_dpkg_free
LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get --assume-yes --quiet -o=Dpkg::Use-Pty=0 $@ LC_ALL=C DEBIAN_FRONTEND=noninteractive apt-get --assume-yes --quiet -o=Acquire::Retries=3 -o=Dpkg::Use-Pty=0 $@
} }
# Update package index files # Update package index files
@ -193,37 +193,17 @@ ynh_package_install_from_equivs () {
LC_ALL=C equivs-build ./control 1> /dev/null LC_ALL=C equivs-build ./control 1> /dev/null
dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1) dpkg --force-depends --install "./${pkgname}_${pkgversion}_all.deb" 2>&1)
# Let's try to see if install will work using dry-run. It it fails, ynh_package_install --fix-broken || \
# it could be because the pinning of sury is blocking some package install
# c.f. for example: https://github.com/YunoHost/issues/issues/1563#issuecomment-623406509
# ... In that case, we use an ugly hack were we'll use a tweaked
# preferences.d directory with looser contrains for sury...
if ! ynh_package_install --fix-broken --dry-run >/dev/null 2>&1 && [ -e /etc/apt/preferences.d/extra_php_version ]
then
cp -r /etc/apt/preferences.d/ /etc/apt/preferences.d.tmp/
sed 's/^Pin-Priority: .*/Pin-Priority: 600/g' -i /etc/apt/preferences.d.tmp/extra_php_version
local apt_tweaks='--option Dir::Etc::preferencesparts=preferences.d.tmp'
# Try a dry-run again to see if that fixes the issue ...
# If it did not, then that's probably not related to sury.
ynh_package_install $apt_tweaks --fix-broken --dry-run >/dev/null 2>&1 || apt_tweaks=""
else
local apt_tweaks=""
fi
# Try to install for real
ynh_package_install $apt_tweaks --fix-broken || \
{ # If the installation failed { # If the installation failed
# (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process) # (the following is ran inside { } to not start a subshell otherwise ynh_die wouldnt exit the original process)
rm --recursive --force /etc/apt/preferences.d.tmp/
# Get the list of dependencies from the deb # Get the list of dependencies from the deb
local dependencies="$(dpkg --info "$TMPDIR/${pkgname}_${pkgversion}_all.deb" | grep Depends | \ local dependencies="$(dpkg --info "$TMPDIR/${pkgname}_${pkgversion}_all.deb" | grep Depends | \
sed 's/^ Depends: //' | sed 's/,//g')" sed 's/^ Depends: //' | sed 's/,//g')"
# Fake an install of those dependencies to see the errors # Fake an install of those dependencies to see the errors
# The sed command here is, Print only from '--fix-broken' to the end. # The sed command here is, Print only from '--fix-broken' to the end.
ynh_package_install $apt_tweaks $dependencies --dry-run | sed --quiet '/--fix-broken/,$p' >&2 ynh_package_install $dependencies --dry-run | sed --quiet '/--fix-broken/,$p' >&2
ynh_die --message="Unable to install dependencies"; } ynh_die --message="Unable to install dependencies"; }
[[ -n "$TMPDIR" ]] && rm --recursive --force $TMPDIR # Remove the temp dir. [[ -n "$TMPDIR" ]] && rm --recursive --force $TMPDIR # Remove the temp dir.
rm --recursive --force /etc/apt/preferences.d.tmp/
# check if the package is actually installed # check if the package is actually installed
ynh_package_is_installed "$pkgname" ynh_package_is_installed "$pkgname"
@ -280,15 +260,11 @@ ynh_install_app_dependencies () {
# And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian) # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian)
if dpkg --list | grep "php7.0" | grep --quiet --invert-match "7.0.33-0+deb9" if dpkg --list | grep "php7.0" | grep --quiet --invert-match "7.0.33-0+deb9"
then then
# And sury ain't already installed # And sury ain't already in sources.lists
if ! grep --line-number --recursive --quiet "sury" /etc/apt/sources.list* if ! grep --recursive --quiet "^ *deb.*sury" /etc/apt/sources.list*
then then
# Re-add sury # Re-add sury
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --name=extra_php_version --priority=600
# Pin this sury repository to prevent sury of doing shit
ynh_pin_repo --package="*" --pin="origin \"packages.sury.org\"" --priority=200 --name=extra_php_version
ynh_pin_repo --package="php${YNH_DEFAULT_PHP_VERSION}*" --pin="origin \"packages.sury.org\"" --priority=600 --name=extra_php_version --append
fi fi
fi fi
fi fi
@ -485,8 +461,8 @@ ynh_remove_extra_repo () {
ynh_secure_remove "/etc/apt/sources.list.d/$name.list" ynh_secure_remove "/etc/apt/sources.list.d/$name.list"
ynh_secure_remove "/etc/apt/preferences.d/$name" ynh_secure_remove "/etc/apt/preferences.d/$name"
ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.gpg" ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.gpg" > /dev/null
ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.asc" ynh_secure_remove "/etc/apt/trusted.gpg.d/$name.asc" > /dev/null
# Update the list of package to exclude the old repo # Update the list of package to exclude the old repo
ynh_package_update ynh_package_update

View file

@ -80,10 +80,10 @@ ynh_handle_getopts_args () {
arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}-/--${args_array[$option_flag]}\\TOBEREMOVED\\-}" arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}-/--${args_array[$option_flag]}\\TOBEREMOVED\\-}"
# And replace long option (value of the option_flag) by the short option, the option_flag itself # And replace long option (value of the option_flag) by the short option, the option_flag itself
# (e.g. for [u]=user, --user will be -u) # (e.g. for [u]=user, --user will be -u)
# Replace long option with = # Replace long option with = (match the beginning of the argument)
arguments[arg]="${arguments[arg]//--${args_array[$option_flag]}/-${option_flag} }" arguments[arg]="$(echo "${arguments[arg]}" | sed "s/^--${args_array[$option_flag]}/-${option_flag} /")"
# And long option without = # And long option without = (match the whole line)
arguments[arg]="${arguments[arg]//--${args_array[$option_flag]%=}/-${option_flag}}" arguments[arg]="$(echo "${arguments[arg]}" | sed "s/^--${args_array[$option_flag]%=}$/-${option_flag} /")"
done done
done done

View file

@ -44,8 +44,13 @@ ynh_mysql_execute_as_root() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
database="${database:-}" database="${database:-}"
if [ -n "$database" ]
then
database="--database=$database"
fi
ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \ ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \
--database="$database" <<< "$sql" $database <<< "$sql"
} }
# Execute a command from a file as root user # Execute a command from a file as root user
@ -65,8 +70,14 @@ ynh_mysql_execute_file_as_root() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
database="${database:-}" database="${database:-}"
if [ -n "$database" ]
then
database="--database=$database"
fi
ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \ ynh_mysql_connect_as --user="root" --password="$(cat $MYSQL_ROOT_PWD_FILE)" \
--database="$database" < "$file" $database < "$file"
} }
# Create a database and grant optionnaly privilegies to a user # Create a database and grant optionnaly privilegies to a user

View file

@ -1,5 +1,6 @@
#!/bin/bash #!/bin/bash
n_version=6.5.1
n_install_dir="/opt/node_n" n_install_dir="/opt/node_n"
node_version_path="$n_install_dir/n/versions/node" node_version_path="$n_install_dir/n/versions/node"
# N_PREFIX is the directory of n, it needs to be loaded as a environment variable. # N_PREFIX is the directory of n, it needs to be loaded as a environment variable.
@ -16,8 +17,8 @@ ynh_install_n () {
ynh_print_info --message="Installation of N - Node.js version management" ynh_print_info --message="Installation of N - Node.js version management"
# Build an app.src for n # Build an app.src for n
mkdir --parents "../conf" mkdir --parents "../conf"
echo "SOURCE_URL=https://github.com/tj/n/archive/v4.1.0.tar.gz echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz
SOURCE_SUM=3983fa3f00d4bf85ba8e21f1a590f6e28938093abe0bb950aeea52b1717471fc" > "../conf/n.src" SOURCE_SUM=5833f15893b9951a9ed59487e87b6c181d96b83a525846255872c4f92f0d25dd" > "../conf/n.src"
# Download and extract n # Download and extract n
ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n
# Install n # Install n
@ -125,7 +126,10 @@ ynh_install_nodejs () {
test -x /usr/bin/npm && mv /usr/bin/npm /usr/bin/npm_n test -x /usr/bin/npm && mv /usr/bin/npm /usr/bin/npm_n
# If n is not previously setup, install it # If n is not previously setup, install it
if ! test $(n --version > /dev/null 2>&1) if ! $n_install_dir/bin/n --version > /dev/null 2>&1
then
ynh_install_n
elif dpkg --compare-versions "$($n_install_dir/bin/n --version)" lt $n_version
then then
ynh_install_n ynh_install_n
fi fi

View file

@ -1,37 +1,37 @@
#!/bin/bash #!/bin/bash
readonly YNH_DEFAULT_PHP_VERSION=7.0 readonly YNH_DEFAULT_PHP_VERSION=7.3
# Declare the actual php version to use. # Declare the actual PHP version to use.
# A packager willing to use another version of php can override the variable into its _common.sh. # A packager willing to use another version of PHP can override the variable into its _common.sh.
YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION} YNH_PHP_VERSION=${YNH_PHP_VERSION:-$YNH_DEFAULT_PHP_VERSION}
# Create a dedicated php-fpm config # Create a dedicated PHP-FPM config
# #
# usage 1: ynh_add_fpm_config [--phpversion=7.X] [--use_template] [--package=packages] [--dedicated_service] # usage 1: ynh_add_fpm_config [--phpversion=7.X] [--use_template] [--package=packages] [--dedicated_service]
# | arg: -v, --phpversion= - Version of php to use. # | arg: -v, --phpversion= - Version of PHP to use.
# | arg: -t, --use_template - Use this helper in template mode. # | arg: -t, --use_template - Use this helper in template mode.
# | arg: -p, --package= - Additionnal php packages to install # | arg: -p, --package= - Additionnal PHP packages to install
# | arg: -d, --dedicated_service - Use a dedicated php-fpm service instead of the common one. # | arg: -d, --dedicated_service - Use a dedicated PHP-FPM service instead of the common one.
# #
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# #
# usage 2: ynh_add_fpm_config [--phpversion=7.X] --usage=usage --footprint=footprint [--package=packages] [--dedicated_service] # usage 2: ynh_add_fpm_config [--phpversion=7.X] --usage=usage --footprint=footprint [--package=packages] [--dedicated_service]
# | arg: -v, --phpversion= - Version of php to use. # | arg: -v, --phpversion= - Version of PHP to use.
# | arg: -f, --footprint= - Memory footprint of the service (low/medium/high). # | arg: -f, --footprint= - Memory footprint of the service (low/medium/high).
# low - Less than 20Mb of ram by pool. # low - Less than 20 MB of RAM by pool.
# medium - Between 20Mb and 40Mb of ram by pool. # medium - Between 20 MB and 40 MB of RAM by pool.
# high - More than 40Mb of ram by pool. # high - More than 40 MB of RAM by pool.
# Or specify exactly the footprint, the load of the service as Mb by pool instead of having a standard value. # Or specify exactly the footprint, the load of the service as MB by pool instead of having a standard value.
# To have this value, use the following command and stress the service. # To have this value, use the following command and stress the service.
# watch -n0.5 ps -o user,cmd,%cpu,rss -u APP # watch -n0.5 ps -o user,cmd,%cpu,rss -u APP
# #
# | arg: -u, --usage= - Expected usage of the service (low/medium/high). # | arg: -u, --usage= - Expected usage of the service (low/medium/high).
# low - Personal usage, behind the sso. # low - Personal usage, behind the SSO.
# medium - Low usage, few people or/and publicly accessible. # medium - Low usage, few people or/and publicly accessible.
# high - High usage, frequently visited website. # high - High usage, frequently visited website.
# #
# | arg: -p, --package= - Additionnal php packages to install for a specific version of php # | arg: -p, --package= - Additionnal PHP packages to install for a specific version of PHP
# | arg: -d, --dedicated_service - Use a dedicated php-fpm service instead of the common one. # | arg: -d, --dedicated_service - Use a dedicated PHP-FPM service instead of the common one.
# #
# #
# The footprint of the service will be used to defined the maximum footprint we can allow, which is half the maximum RAM. # The footprint of the service will be used to defined the maximum footprint we can allow, which is half the maximum RAM.
@ -85,7 +85,7 @@ ynh_add_fpm_config () {
# Set the default PHP-FPM version by default # Set the default PHP-FPM version by default
phpversion="${phpversion:-$YNH_PHP_VERSION}" phpversion="${phpversion:-$YNH_PHP_VERSION}"
# If the requested php version is not the default version for YunoHost # If the requested PHP version is not the default version for YunoHost
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]
then then
# If the argument --package is used, add the packages to ynh_install_php to install them from sury # If the argument --package is used, add the packages to ynh_install_php to install them from sury
@ -95,7 +95,7 @@ ynh_add_fpm_config () {
else else
local additionnal_packages="" local additionnal_packages=""
fi fi
# Install this specific version of php. # Install this specific version of PHP.
ynh_install_php --phpversion="$phpversion" "$additionnal_packages" ynh_install_php --phpversion="$phpversion" "$additionnal_packages"
elif [ -n "$package" ] elif [ -n "$package" ]
then then
@ -118,7 +118,7 @@ ynh_add_fpm_config () {
fpm_service="php5-fpm" fpm_service="php5-fpm"
fi fi
# Create the directory for fpm pools # Create the directory for FPM pools
mkdir --parents "$fpm_config_dir/pool.d" mkdir --parents "$fpm_config_dir/pool.d"
ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir" ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir"
@ -127,7 +127,7 @@ ynh_add_fpm_config () {
ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion ynh_app_setting_set --app=$app --key=phpversion --value=$phpversion
finalphpconf="$fpm_config_dir/pool.d/$app.conf" finalphpconf="$fpm_config_dir/pool.d/$app.conf"
# Migrate from mutual php service to dedicated one. # Migrate from mutual PHP service to dedicated one.
if [ $dedicated_service -eq 1 ] if [ $dedicated_service -eq 1 ]
then then
local old_fpm_config_dir="/etc/php/$phpversion/fpm" local old_fpm_config_dir="/etc/php/$phpversion/fpm"
@ -137,9 +137,9 @@ ynh_add_fpm_config () {
ynh_print_info --message="Migrate to a dedicated php-fpm service for $app." ynh_print_info --message="Migrate to a dedicated php-fpm service for $app."
# Create a backup of the old file before migration # Create a backup of the old file before migration
ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf" ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf"
# Remove the old php config file # Remove the old PHP config file
ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf" ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf"
# Reload php to release the socket and allow the dedicated service to use it # Reload PHP to release the socket and allow the dedicated service to use it
ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload
fi fi
fi fi
@ -148,21 +148,27 @@ ynh_add_fpm_config () {
if [ $use_template -eq 1 ] if [ $use_template -eq 1 ]
then then
# Usage 1, use the template in ../conf/php-fpm.conf # Usage 1, use the template in conf/php-fpm.conf
cp ../conf/php-fpm.conf "$finalphpconf" local phpfpm_path="../conf/php-fpm.conf"
if [ ! -e "$phpfpm_path" ]; then
phpfpm_path="../settings/conf/php-fpm.conf" # Into the restore script, the PHP-FPM template is not at the same place
fi
# Make sure now that the template indeed exists
[ -e "$phpfpm_path" ] || ynh_die --message="Unable to find template to configure PHP-FPM."
cp "$phpfpm_path" "$finalphpconf"
ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf" 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="__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="__USER__" --replace_string="$app" --target_file="$finalphpconf"
ynh_replace_string --match_string="__PHPVERSION__" --replace_string="$phpversion" --target_file="$finalphpconf" ynh_replace_string --match_string="__PHPVERSION__" --replace_string="$phpversion" --target_file="$finalphpconf"
else else
# Usage 2, generate a php-fpm config file with ynh_get_scalable_phpfpm # Usage 2, generate a PHP-FPM config file with ynh_get_scalable_phpfpm
# Store settings # Store settings
ynh_app_setting_set --app=$app --key=fpm_footprint --value=$footprint ynh_app_setting_set --app=$app --key=fpm_footprint --value=$footprint
ynh_app_setting_set --app=$app --key=fpm_usage --value=$usage ynh_app_setting_set --app=$app --key=fpm_usage --value=$usage
# Define the values to use for the configuration of php. # Define the values to use for the configuration of PHP.
ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint
# Copy the default file # Copy the default file
@ -175,7 +181,7 @@ ynh_add_fpm_config () {
ynh_replace_string --match_string="^group = .*" --replace_string="group = $app" --target_file="$finalphpconf" ynh_replace_string --match_string="^group = .*" --replace_string="group = $app" --target_file="$finalphpconf"
ynh_replace_string --match_string=".*chdir = .*" --replace_string="chdir = $final_path" --target_file="$finalphpconf" ynh_replace_string --match_string=".*chdir = .*" --replace_string="chdir = $final_path" --target_file="$finalphpconf"
# Configure fpm children # Configure FPM children
ynh_replace_string --match_string=".*pm = .*" --replace_string="pm = $php_pm" --target_file="$finalphpconf" ynh_replace_string --match_string=".*pm = .*" --replace_string="pm = $php_pm" --target_file="$finalphpconf"
ynh_replace_string --match_string=".*pm.max_children = .*" --replace_string="pm.max_children = $php_max_children" --target_file="$finalphpconf" ynh_replace_string --match_string=".*pm.max_children = .*" --replace_string="pm.max_children = $php_max_children" --target_file="$finalphpconf"
ynh_replace_string --match_string=".*pm.max_requests = .*" --replace_string="pm.max_requests = 500" --target_file="$finalphpconf" ynh_replace_string --match_string=".*pm.max_requests = .*" --replace_string="pm.max_requests = 500" --target_file="$finalphpconf"
@ -232,7 +238,7 @@ ynh_add_fpm_config () {
ynh_replace_string --match_string="^[; ]*syslog.ident *=.*" --replace_string="syslog.ident = php-fpm-$app" --target_file="$globalphpconf" ynh_replace_string --match_string="^[; ]*syslog.ident *=.*" --replace_string="syslog.ident = php-fpm-$app" --target_file="$globalphpconf"
ynh_replace_string --match_string="^[; ]*include *=.*" --replace_string="include = $finalphpconf" --target_file="$globalphpconf" ynh_replace_string --match_string="^[; ]*include *=.*" --replace_string="include = $finalphpconf" --target_file="$globalphpconf"
# Create a config for a dedicated php-fpm service for the app # Create a config for a dedicated PHP-FPM service for the app
echo "[Unit] echo "[Unit]
Description=PHP $phpversion FastCGI Process Manager for $app Description=PHP $phpversion FastCGI Process Manager for $app
After=network.target After=network.target
@ -247,7 +253,7 @@ ExecReload=/bin/kill -USR2 \$MAINPID
WantedBy=multi-user.target WantedBy=multi-user.target
" > ../conf/$fpm_service " > ../conf/$fpm_service
# Create this dedicated php-fpm service # Create this dedicated PHP-FPM service
ynh_add_systemd_config --service=$fpm_service --template=$fpm_service ynh_add_systemd_config --service=$fpm_service --template=$fpm_service
# Integrate the service in YunoHost admin panel # Integrate the service in YunoHost admin panel
yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --log_type file --description "Php-fpm dedicated to $app" yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --log_type file --description "Php-fpm dedicated to $app"
@ -256,12 +262,12 @@ WantedBy=multi-user.target
# Restart the service, as this service is either stopped or only for this app # Restart the service, as this service is either stopped or only for this app
ynh_systemd_action --service_name=$fpm_service --action=restart ynh_systemd_action --service_name=$fpm_service --action=restart
else else
# Reload php, to not impact other parts of the system using php # Reload PHP, to not impact other parts of the system using PHP
ynh_systemd_action --service_name=$fpm_service --action=reload ynh_systemd_action --service_name=$fpm_service --action=reload
fi fi
} }
# Remove the dedicated php-fpm config # Remove the dedicated PHP-FPM config
# #
# usage: ynh_remove_fpm_config # usage: ynh_remove_fpm_config
# #
@ -271,13 +277,13 @@ ynh_remove_fpm_config () {
local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service) local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service)
local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service) local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service)
dedicated_service=${dedicated_service:-0} dedicated_service=${dedicated_service:-0}
# Get the version of php used by this app # Get the version of PHP used by this app
local phpversion=$(ynh_app_setting_get $app phpversion) local phpversion=$(ynh_app_setting_get $app phpversion)
# Assume default PHP-FPM version by default # Assume default PHP-FPM version by default
phpversion="${phpversion:-$YNH_DEFAULT_PHP_VERSION}" phpversion="${phpversion:-$YNH_DEFAULT_PHP_VERSION}"
# Assume default php files if not set # Assume default PHP files if not set
if [ -z "$fpm_config_dir" ] if [ -z "$fpm_config_dir" ]
then then
fpm_config_dir="/etc/php/$YNH_DEFAULT_PHP_VERSION/fpm" fpm_config_dir="/etc/php/$YNH_DEFAULT_PHP_VERSION/fpm"
@ -286,11 +292,11 @@ ynh_remove_fpm_config () {
if [ $dedicated_service -eq 1 ] if [ $dedicated_service -eq 1 ]
then then
# Remove the dedicated service php-fpm service for the app # Remove the dedicated service PHP-FPM service for the app
ynh_remove_systemd_config --service=$fpm_service ynh_remove_systemd_config --service=$fpm_service
# Remove the global php-fpm conf # Remove the global PHP-FPM conf
ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf" ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf"
# Remove the service from the list of services known by Yunohost # Remove the service from the list of services known by YunoHost
yunohost service remove $fpm_service yunohost service remove $fpm_service
elif ynh_package_is_installed --package="php${phpversion}-fpm"; then elif ynh_package_is_installed --package="php${phpversion}-fpm"; then
ynh_systemd_action --service_name=$fpm_service --action=reload ynh_systemd_action --service_name=$fpm_service --action=reload
@ -302,21 +308,21 @@ ynh_remove_fpm_config () {
ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini"
fi fi
# If the php version used is not the default version for YunoHost # If the PHP version used is not the default version for YunoHost
if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ]
then then
# Remove this specific version of php # Remove this specific version of PHP
ynh_remove_php ynh_remove_php
fi fi
} }
# Install another version of php. # Install another version of PHP.
# #
# [internal] # [internal]
# #
# usage: ynh_install_php --phpversion=phpversion [--package=packages] # usage: ynh_install_php --phpversion=phpversion [--package=packages]
# | arg: -v, --phpversion= - Version of php to install. # | arg: -v, --phpversion= - Version of PHP to install.
# | arg: -p, --package= - Additionnal php packages to install # | arg: -p, --package= - Additionnal PHP packages to install
# #
# Requires YunoHost version 3.8.1 or higher. # Requires YunoHost version 3.8.1 or higher.
ynh_install_php () { ynh_install_php () {
@ -343,30 +349,26 @@ ynh_install_php () {
# Do not add twice the same line # Do not add twice the same line
if ! grep --quiet "$YNH_APP_INSTANCE_NAME:" "/etc/php/ynh_app_version" if ! grep --quiet "$YNH_APP_INSTANCE_NAME:" "/etc/php/ynh_app_version"
then then
# Store the ID of this app and the version of php requested for it # Store the ID of this app and the version of PHP requested for it
echo "$YNH_APP_INSTANCE_NAME:$phpversion" | tee --append "/etc/php/ynh_app_version" echo "$YNH_APP_INSTANCE_NAME:$phpversion" | tee --append "/etc/php/ynh_app_version"
fi fi
# Add an extra repository for those packages # Add an extra repository for those packages
ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --priority=995 --name=extra_php_version ynh_install_extra_repo --repo="https://packages.sury.org/php/ $(ynh_get_debian_release) main" --key="https://packages.sury.org/php/apt.gpg" --priority=995 --name=extra_php_version --priority=600
# Install requested dependencies from this extra repository. # Install requested dependencies from this extra repository.
# Install php-fpm first, otherwise php will install apache as a dependency. # Install PHP-FPM first, otherwise PHP will install apache as a dependency.
ynh_add_app_dependencies --package="php${phpversion}-fpm" ynh_add_app_dependencies --package="php${phpversion}-fpm"
ynh_add_app_dependencies --package="php$phpversion php${phpversion}-common $package" ynh_add_app_dependencies --package="php$phpversion php${phpversion}-common $package"
# Set the default php version back as the default version for php-cli. # Set the default PHP version back as the default version for php-cli.
update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION update-alternatives --set php /usr/bin/php$YNH_DEFAULT_PHP_VERSION
# Pin this extra repository after packages are installed to prevent sury of doing shit
ynh_pin_repo --package="*" --pin="origin \"packages.sury.org\"" --priority=200 --name=extra_php_version
ynh_pin_repo --package="php${YNH_DEFAULT_PHP_VERSION}*" --pin="origin \"packages.sury.org\"" --priority=600 --name=extra_php_version --append
# Advertise service in admin panel # Advertise service in admin panel
yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log" yunohost service add php${phpversion}-fpm --log "/var/log/php${phpversion}-fpm.log"
} }
# Remove the specific version of php used by the app. # Remove the specific version of PHP used by the app.
# #
# [internal] # [internal]
# #
@ -374,7 +376,7 @@ ynh_install_php () {
# #
# Requires YunoHost version 3.8.1 or higher. # Requires YunoHost version 3.8.1 or higher.
ynh_remove_php () { ynh_remove_php () {
# Get the version of php used by this app # Get the version of PHP used by this app
local phpversion=$(ynh_app_setting_get $app phpversion) local phpversion=$(ynh_app_setting_get $app phpversion)
if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] || [ -z "$phpversion" ] if [ "$phpversion" == "$YNH_DEFAULT_PHP_VERSION" ] || [ -z "$phpversion" ]
@ -392,7 +394,7 @@ ynh_remove_php () {
# Remove the line for this app # Remove the line for this app
sed --in-place "/$YNH_APP_INSTANCE_NAME:$phpversion/d" "/etc/php/ynh_app_version" sed --in-place "/$YNH_APP_INSTANCE_NAME:$phpversion/d" "/etc/php/ynh_app_version"
# If no other app uses this version of php, remove it. # If no other app uses this version of PHP, remove it.
if ! grep --quiet "$phpversion" "/etc/php/ynh_app_version" if ! grep --quiet "$phpversion" "/etc/php/ynh_app_version"
then then
# Remove the service from the admin panel # Remove the service from the admin panel
@ -400,26 +402,26 @@ ynh_remove_php () {
yunohost service remove php${phpversion}-fpm yunohost service remove php${phpversion}-fpm
fi fi
# Purge php dependencies for this version. # Purge PHP dependencies for this version.
ynh_package_autopurge "php$phpversion php${phpversion}-fpm php${phpversion}-common" ynh_package_autopurge "php$phpversion php${phpversion}-fpm php${phpversion}-common"
fi fi
} }
# Define the values to configure php-fpm # Define the values to configure PHP-FPM
# #
# [internal] # [internal]
# #
# usage: ynh_get_scalable_phpfpm --usage=usage --footprint=footprint [--print] # usage: ynh_get_scalable_phpfpm --usage=usage --footprint=footprint [--print]
# | arg: -f, --footprint= - Memory footprint of the service (low/medium/high). # | arg: -f, --footprint= - Memory footprint of the service (low/medium/high).
# low - Less than 20Mb of ram by pool. # low - Less than 20 MB of RAM by pool.
# medium - Between 20Mb and 40Mb of ram by pool. # medium - Between 20 MB and 40 MB of RAM by pool.
# high - More than 40Mb of ram by pool. # high - More than 40 MB of RAM by pool.
# Or specify exactly the footprint, the load of the service as Mb by pool instead of having a standard value. # Or specify exactly the footprint, the load of the service as MB by pool instead of having a standard value.
# To have this value, use the following command and stress the service. # To have this value, use the following command and stress the service.
# watch -n0.5 ps -o user,cmd,%cpu,rss -u APP # watch -n0.5 ps -o user,cmd,%cpu,rss -u APP
# #
# | arg: -u, --usage= - Expected usage of the service (low/medium/high). # | arg: -u, --usage= - Expected usage of the service (low/medium/high).
# low - Personal usage, behind the sso. # low - Personal usage, behind the SSO.
# medium - Low usage, few people or/and publicly accessible. # medium - Low usage, few people or/and publicly accessible.
# high - High usage, frequently visited website. # high - High usage, frequently visited website.
# #
@ -490,7 +492,7 @@ ynh_get_scalable_phpfpm () {
# Define pm.max_children # Define pm.max_children
# The value of pm.max_children is the total amount of ram divide by 2 and divide again by the footprint of a pool for this app. # The value of pm.max_children is the total amount of ram divide by 2 and divide again by the footprint of a pool for this app.
# So if php-fpm start the maximum of children, it won't exceed half of the ram. # So if PHP-FPM start the maximum of children, it won't exceed half of the ram.
php_max_children=$(( $max_ram / 2 / $footprint )) php_max_children=$(( $max_ram / 2 / $footprint ))
# If process manager is set as static, use half less children. # If process manager is set as static, use half less children.
# Used as static, there's always as many children as the value of pm.max_children # Used as static, there's always as many children as the value of pm.max_children

View file

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
PSQL_ROOT_PWD_FILE=/etc/yunohost/psql PSQL_ROOT_PWD_FILE=/etc/yunohost/psql
PSQL_VERSION=9.6 PSQL_VERSION=11
# Open a connection as a user # Open a connection as a user
# #
@ -46,8 +46,13 @@ ynh_psql_execute_as_root() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
database="${database:-}" database="${database:-}"
if [ -n "$database" ]
then
database="--database=$database"
fi
ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \ ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \
--database="$database" <<<"$sql" $database <<<"$sql"
} }
# Execute a command from a file as root user # Execute a command from a file as root user
@ -67,8 +72,13 @@ ynh_psql_execute_file_as_root() {
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
database="${database:-}" database="${database:-}"
if [ -n "$database" ]
then
database="--database=$database"
fi
ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \ ynh_psql_connect_as --user="postgres" --password="$(cat $PSQL_ROOT_PWD_FILE)" \
--database="$database" <"$file" $database <"$file"
} }
# Create a database and grant optionnaly privilegies to a user # Create a database and grant optionnaly privilegies to a user
@ -213,7 +223,7 @@ ynh_psql_drop_user() {
# usage: ynh_psql_setup_db --db_user=user --db_name=name [--db_pwd=pwd] # usage: ynh_psql_setup_db --db_user=user --db_name=name [--db_pwd=pwd]
# | arg: -u, --db_user= - Owner of the database # | arg: -u, --db_user= - Owner of the database
# | arg: -n, --db_name= - Name 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
# #
# After executing this helper, the password of the created database will be available in $db_pwd # After executing this helper, the password of the created database will be available in $db_pwd
# It will also be stored as "psqlpwd" into the app settings. # It will also be stored as "psqlpwd" into the app settings.
@ -231,7 +241,7 @@ ynh_psql_setup_db() {
if ! ynh_psql_user_exists --user=$db_user; then if ! ynh_psql_user_exists --user=$db_user; then
local new_db_pwd=$(ynh_string_random) # Generate a random password 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}" db_pwd="${db_pwd:-$new_db_pwd}"
ynh_psql_create_user "$db_user" "$db_pwd" ynh_psql_create_user "$db_user" "$db_pwd"

View file

@ -144,8 +144,13 @@ ynh_setup_source () {
then # Use the local source file if it is present then # Use the local source file if it is present
cp $local_src $src_filename cp $local_src $src_filename
else # If not, download the source else # If not, download the source
# NB. we have to declare the var as local first,
# otherwise 'local foo=$(false) || echo 'pwet'" does'nt work
# because local always return 0 ...
local out
# Timeout option is here to enforce the timeout on dns query and tcp connect (c.f. man wget) # Timeout option is here to enforce the timeout on dns query and tcp connect (c.f. man wget)
local out=`wget --timeout 900 --no-verbose --output-document=$src_filename $src_url 2>&1` || ynh_print_err --message="$out" out=$(wget --tries 3 --no-dns-cache --timeout 900 --no-verbose --output-document=$src_filename $src_url 2>&1) \
|| ynh_die --message="$out"
fi fi
# Check the control sum # Check the control sum
@ -431,9 +436,8 @@ ynh_app_upstream_version () {
local manifest local manifest
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
manifest="${manifest:-../manifest.json}"
version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") version_key=$YNH_APP_MANIFEST_VERSION
echo "${version_key/~ynh*/}" echo "${version_key/~ynh*/}"
} }
@ -456,9 +460,8 @@ ynh_app_package_version () {
local manifest local manifest
# Manage arguments with getopts # Manage arguments with getopts
ynh_handle_getopts_args "$@" ynh_handle_getopts_args "$@"
manifest="${manifest:-../manifest.json}"
version_key=$(ynh_read_manifest --manifest="$manifest" --manifest_key="version") version_key=$YNH_APP_MANIFEST_VERSION
echo "${version_key/*~ynh/}" echo "${version_key/*~ynh/}"
} }
@ -511,3 +514,49 @@ ynh_check_app_version_changed () {
fi fi
echo $return_value echo $return_value
} }
# Compare the current package version against another version given as an argument.
# This is really useful when we need to do some actions only for some old package versions.
#
# example: ynh_compare_current_package_version --comparison lt --version 2.3.2~ynh1
# This example will check if the installed version is lower than (lt) the version 2.3.2~ynh1
#
# Generally you might probably use it as follow in the upgrade script
#
# if ynh_compare_current_package_version --comparaison lt --version 2.3.2~ynh1
# then
# # Do something that is needed for the package version older than 2.3.2~ynh1
# fi
#
# usage: ynh_compare_current_package_version --comparison lt|le|eq|ne|ge|gt
# | arg: --comparison - Comparison type. Could be : lt (lower than), le (lower or equal),
# | eq (equal), ne (not equal), ge (greater or equal), gt (greater than)
# | arg: --version - The version to compare. Need to be a version in the yunohost package version type (like 2.3.1~ynh4)
#
# Return 0 if the evaluation is true. 1 if false.
#
# Requires YunoHost version 3.8.0 or higher.
ynh_compare_current_package_version() {
local legacy_args=cv
declare -Ar args_array=( [c]=comparison= [v]=version= )
local version
local comparison
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local current_version=$YNH_APP_CURRENT_VERSION
# Check the syntax of the versions
if [[ ! $version =~ '~ynh' ]] || [[ ! $current_version =~ '~ynh' ]]
then
ynh_die "Invalid argument for version."
fi
# Check validity of the comparator
if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then
ynh_die "Invialid comparator must be : lt, le, eq, ne, ge, gt"
fi
# Return the return value of dpkg --compare-versions
dpkg --compare-versions $current_version $comparison $version
}

View file

@ -10,7 +10,8 @@ source /usr/share/yunohost/helpers
backup_dir="${1}/conf/ldap" backup_dir="${1}/conf/ldap"
# Backup the configuration # Backup the configuration
ynh_backup "/etc/ldap/slapd.conf" "${backup_dir}/slapd.conf" ynh_backup "/etc/ldap/ldap.conf" "${backup_dir}/ldap.conf"
ynh_backup "/etc/ldap/slapd.ldif" "${backup_dir}/slapd.ldif"
slapcat -b cn=config -l "${backup_dir}/cn=config.master.ldif" slapcat -b cn=config -l "${backup_dir}/cn=config.master.ldif"
# Backup the database # Backup the database

View file

@ -1,13 +0,0 @@
#!/bin/bash
# Exit hook on subcommand error or unset variable
set -eu
# Source YNH helpers
source /usr/share/yunohost/helpers
# Backup destination
backup_dir="${1}/conf/ynh/mysql"
# Save MySQL root password
ynh_backup "/etc/yunohost/mysql" "${backup_dir}/root_pwd"

View file

@ -55,14 +55,36 @@ do_pre_regen() {
fi fi
# add cron job for diagnosis to be ran at 7h and 19h + a random delay between # add cron job for diagnosis to be ran at 7h and 19h + a random delay between
# 0 and 10min, meant to avoid every instances running their diagnosis at # 0 and 20min, meant to avoid every instances running their diagnosis at
# exactly the same time, which may overload the diagnosis server. # exactly the same time, which may overload the diagnosis server.
mkdir -p $pending_dir/etc/cron.d/ mkdir -p $pending_dir/etc/cron.d/
cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF
SHELL=/bin/bash SHELL=/bin/bash
0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%600)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably" 0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%1200)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably"
EOF EOF
# legacy stuff to avoid yunohost reporting etckeeper as manually modified
# (this make sure that the hash is null / file is flagged as to-delete)
mkdir -p $pending_dir/etc/etckeeper
touch $pending_dir/etc/etckeeper/etckeeper.conf
# Skip ntp if inside a container (inspired from the conf of systemd-timesyncd)
mkdir -p ${pending_dir}/etc/systemd/system/ntp.service.d/
echo "
[Unit]
ConditionCapability=CAP_SYS_TIME
ConditionVirtualization=!container
" > ${pending_dir}/etc/systemd/system/ntp.service.d/ynh-override.conf
# Make nftable conflict with yunohost-firewall
mkdir -p ${pending_dir}/etc/systemd/system/nftables.service.d/
cat > ${pending_dir}/etc/systemd/system/nftables.service.d/ynh-override.conf << EOF
[Unit]
# yunohost-firewall and nftables conflict with each other
Conflicts=yunohost-firewall.service
ConditionFileIsExecutable=!/etc/init.d/yunohost-firewall
ConditionPathExists=!/etc/systemd/system/multi-user.target.wants/yunohost-firewall.service
EOF
} }
do_post_regen() { do_post_regen() {
@ -87,6 +109,9 @@ do_post_regen() {
[[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d) [[ ! -e /etc/yunohost/hooks.d ]] || (chown root /etc/yunohost/hooks.d && chmod 700 /etc/yunohost/hooks.d)
[[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps) [[ ! -e /etc/yunohost/apps ]] || (chown root /etc/yunohost/apps && chmod 700 /etc/yunohost/apps)
# Propagates changes in systemd service config overrides
[[ ! "$regen_conf_files" =~ "ntp.service.d/ynh-override.conf" ]] || { systemctl daemon-reload; systemctl restart ntp; }
[[ ! "$regen_conf_files" =~ "nftables.service.d/ynh-override.conf" ]] || systemctl daemon-reload
} }
_update_services() { _update_services() {

View file

@ -69,12 +69,11 @@ do_init_regen() {
-out "${ssl_dir}/certs/yunohost_crt.pem" \ -out "${ssl_dir}/certs/yunohost_crt.pem" \
-batch >>$LOGFILE 2>&1 -batch >>$LOGFILE 2>&1
last_cert=$(ls $ssl_dir/newcerts/*.pem | sort -V | tail -n 1)
chmod 640 "${ssl_dir}/certs/yunohost_key.pem" chmod 640 "${ssl_dir}/certs/yunohost_key.pem"
chmod 640 "$last_cert" chmod 640 "${ssl_dir}/certs/yunohost_crt.pem"
cp "${ssl_dir}/certs/yunohost_key.pem" "$ynh_key" cp "${ssl_dir}/certs/yunohost_key.pem" "$ynh_key"
cp "$last_cert" "$ynh_crt" cp "${ssl_dir}/certs/yunohost_crt.pem" "$ynh_crt"
ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem ln -sf "$ynh_crt" /etc/ssl/certs/yunohost_crt.pem
ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem ln -sf "$ynh_key" /etc/ssl/private/yunohost_key.pem
fi fi

View file

@ -12,27 +12,52 @@ do_init_regen() {
do_pre_regen "" do_pre_regen ""
# fix some permissions systemctl daemon-reload
chown root:openldap /etc/ldap/slapd.conf
_regenerate_slapd_conf
# Enforce permissions
chown root:openldap /etc/ldap/slapd.ldif
chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/schema/
usermod -aG ssl-cert openldap usermod -aG ssl-cert openldap
# check the slapd config file at first
slaptest -Q -u -f /etc/ldap/slapd.conf
# regenerate LDAP config directory from slapd.conf
rm -Rf /etc/ldap/slapd.d
mkdir /etc/ldap/slapd.d
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
chown -R openldap:openldap /etc/ldap/slapd.d/
service slapd restart service slapd restart
} }
_regenerate_slapd_conf() {
# Validate the new slapd config
# To do so, we have to use the .ldif to generate the config directory
# so we use a temporary directory slapd_new.d
rm -Rf /etc/ldap/slapd_new.d
mkdir /etc/ldap/slapd_new.d
slapadd -n0 -l /etc/ldap/slapd.ldif -F /etc/ldap/slapd_new.d/ 2>&1
# Actual validation (-Q is for quiet, -u is for dry-run)
slaptest -Q -u -F /etc/ldap/slapd_new.d
# "Commit" / apply the new config (meaning we delete the old one and replace
# it with the new one)
rm -Rf /etc/ldap/slapd.d
mv /etc/ldap/slapd_new.d /etc/ldap/slapd.d
chown -R openldap:openldap /etc/ldap/slapd.d/
}
do_pre_regen() { do_pre_regen() {
pending_dir=$1 pending_dir=$1
cd /usr/share/yunohost/templates/slapd # remove temporary backup file
rm -f "$tmp_backup_dir_file"
# Define if we need to migrate from hdb to mdb
curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}')
if [ -e /etc/ldap/slapd.conf ] && [ -n "$curr_backend" ] && \
[ $curr_backend != 'mdb' ]; then
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
mkdir -p "$backup_dir"
slapcat -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
echo "$backup_dir" > "$tmp_backup_dir_file"
fi
# create needed directories # create needed directories
ldap_dir="${pending_dir}/etc/ldap" ldap_dir="${pending_dir}/etc/ldap"
@ -40,28 +65,15 @@ do_pre_regen() {
mkdir -p "$ldap_dir" "$schema_dir" mkdir -p "$ldap_dir" "$schema_dir"
# remove legacy configuration file # remove legacy configuration file
[ ! -f /etc/ldap/slapd-yuno.conf ] \ [ ! -f /etc/ldap/slapd-yuno.conf ] || touch "${ldap_dir}/slapd-yuno.conf"
|| touch "${pending_dir}/etc/ldap/slapd-yuno.conf" [ ! -f /etc/ldap/slapd.conf ] || touch "${ldap_dir}/slapd.conf"
[ ! -f /etc/ldap/schema/yunohost.schema ] || touch "${schema_dir}/yunohost.schema"
# remove temporary backup file cd /usr/share/yunohost/templates/slapd
rm -f "$tmp_backup_dir_file"
# retrieve current and new backends
curr_backend=$(grep '^database' /etc/ldap/slapd.conf 2>/dev/null | awk '{print $2}')
new_backend=$(grep '^database' slapd.conf | awk '{print $2}')
# save current database before any conf changes
if [[ -n "$curr_backend" && "$curr_backend" != "$new_backend" ]]; then
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
mkdir -p "$backup_dir"
slapcat -b dc=yunohost,dc=org \
-l "${backup_dir}/dc=yunohost-dc=org.ldif"
echo "$backup_dir" > "$tmp_backup_dir_file"
fi
# copy configuration files # copy configuration files
cp -a ldap.conf slapd.conf "$ldap_dir" cp -a ldap.conf slapd.ldif "$ldap_dir"
cp -a sudo.schema mailserver.schema yunohost.schema "$schema_dir" cp -a sudo.ldif mailserver.ldif permission.ldif "$schema_dir"
mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/ mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/
cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf
@ -72,14 +84,11 @@ do_pre_regen() {
do_post_regen() { do_post_regen() {
regen_conf_files=$1 regen_conf_files=$1
# ensure that slapd.d exists
mkdir -p /etc/ldap/slapd.d
# fix some permissions # fix some permissions
echo "Making sure we have the right permissions needed ..." echo "Enforce permissions on ldap/slapd directories and certs ..."
# penldap user should be in the ssl-cert group to let it access the certificate for TLS # penldap user should be in the ssl-cert group to let it access the certificate for TLS
usermod -aG ssl-cert openldap usermod -aG ssl-cert openldap
chown root:openldap /etc/ldap/slapd.conf chown root:openldap /etc/ldap/slapd.ldif
chown -R openldap:openldap /etc/ldap/schema/ chown -R openldap:openldap /etc/ldap/schema/
chown -R openldap:openldap /etc/ldap/slapd.d/ chown -R openldap:openldap /etc/ldap/slapd.d/
@ -92,29 +101,17 @@ do_post_regen() {
[ -z "$regen_conf_files" ] && exit 0 [ -z "$regen_conf_files" ] && exit 0
# check the slapd config file at first # regenerate LDAP config directory from slapd.conf
slaptest -Q -u -f /etc/ldap/slapd.conf echo "Regenerate LDAP config directory from slapd.ldif"
_regenerate_slapd_conf
# check if a backup should be restored # If there's a backup, re-import its data
backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true) backup_dir=$(cat "$tmp_backup_dir_file" 2>/dev/null || true)
if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then if [[ -n "$backup_dir" && -f "${backup_dir}/dc=yunohost-dc=org.ldif" ]]; then
# regenerate LDAP config directory and import database as root # regenerate LDAP config directory and import database as root
# since the admin user may be unavailable echo "Import the database using slapadd"
echo "Regenerate LDAP config directory and import the database using slapadd" slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org -l "${backup_dir}/dc=yunohost-dc=org.ldif"
sh -c "rm -Rf /etc/ldap/slapd.d; chown -R openldap:openldap /var/lib/ldap 2>&1
mkdir /etc/ldap/slapd.d;
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d;
chown -R openldap:openldap /etc/ldap/slapd.d;
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \
-l '${backup_dir}/dc=yunohost-dc=org.ldif';
chown -R openldap:openldap /var/lib/ldap" 2>&1
else
# regenerate LDAP config directory from slapd.conf
echo "Regenerate LDAP config directory from slapd.conf"
rm -Rf /etc/ldap/slapd.d
mkdir /etc/ldap/slapd.d
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
chown -R openldap:openldap /etc/ldap/slapd.d/
fi fi
echo "Running slapdindex" echo "Running slapdindex"
@ -154,6 +151,9 @@ case "$1" in
init) init)
do_init_regen do_init_regen
;; ;;
apply_config)
do_post_regen /etc/ldap/slapd.ldif
;;
*) *)
echo "hook called with unknown argument \`$1'" >&2 echo "hook called with unknown argument \`$1'" >&2
exit 1 exit 1

39
data/hooks/conf_regen/10-apt Executable file
View file

@ -0,0 +1,39 @@
#!/bin/bash
set -e
do_pre_regen() {
pending_dir=$1
mkdir --parents "${pending_dir}/etc/apt/preferences.d"
for package in "php" "php-fpm" "php-mysql" "php-xml" "php-zip" "php-mbstring" "php-ldap" "php-gd" "php-curl" "php-bz2" "php-json" "php-sqlite3" "php-intl" "openssl" "libssl1.1" "libssl-dev"
do
echo "
Package: $package
Pin: origin \"packages.sury.org\"
Pin-Priority: -1" >> "/etc/apt/preferences.d/extra_php_version"
done
}
do_post_regen() {
regen_conf_files=$1
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -26,6 +26,9 @@ do_init_regen() {
ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc" ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc"
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf" ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
# Restart nginx if conf looks good, otherwise display error and exit unhappy # Restart nginx if conf looks good, otherwise display error and exit unhappy
nginx -t 2>/dev/null || { nginx -t; exit 1; } nginx -t 2>/dev/null || { nginx -t; exit 1; }
systemctl restart nginx || { journalctl --no-pager --lines=10 -u nginx >&2; exit 1; } systemctl restart nginx || { journalctl --no-pager --lines=10 -u nginx >&2; exit 1; }
@ -77,6 +80,8 @@ do_pre_regen() {
done done
ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf" ynh_render_template "yunohost_admin.conf" "${nginx_conf_dir}/yunohost_admin.conf"
mkdir -p $nginx_conf_dir/default.d/
cp "redirect_to_admin.conf" $nginx_conf_dir/default.d/
# remove old domain conf files # remove old domain conf files
conf_files=$(ls -1 /etc/nginx/conf.d \ conf_files=$(ls -1 /etc/nginx/conf.d \

View file

@ -42,6 +42,8 @@ do_post_regen() {
chown _rspamd /etc/dkim/*.mail.key chown _rspamd /etc/dkim/*.mail.key
chmod 400 /etc/dkim/*.mail.key chmod 400 /etc/dkim/*.mail.key
[ ! -e /var/log/rspamd ] || chown -R _rspamd:_rspamd /var/log/rspamd
regen_conf_files=$1 regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0 [ -z "$regen_conf_files" ] && exit 0

View file

@ -15,6 +15,18 @@ do_pre_regen() {
do_post_regen() { do_post_regen() {
regen_conf_files=$1 regen_conf_files=$1
# mysql is supposed to be an alias to mariadb... but in some weird case is not
# c.f. https://forum.yunohost.org/t/mysql-ne-fonctionne-pas/11661
# Playing with enable/disable allows to recreate the proper symlinks.
if [ ! -e /etc/systemd/system/mysql.service ]
then
systemctl stop mysql -q
systemctl disable mysql -q
systemctl disable mariadb -q
systemctl enable mariadb -q
systemctl is-active mariadb -q || systemctl start mariadb
fi
if [ ! -f /etc/yunohost/mysql ]; then if [ ! -f /etc/yunohost/mysql ]; then
# ensure that mysql is running # ensure that mysql is running

View file

@ -1,36 +0,0 @@
#!/bin/bash
set -e
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/templates/glances
install -D -m 644 glances.default "${pending_dir}/etc/default/glances"
}
do_post_regen() {
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| service glances restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -27,14 +27,13 @@ do_pre_regen() {
ipv6=$(curl -s -6 https://ip6.yunohost.org 2>/dev/null || true) ipv6=$(curl -s -6 https://ip6.yunohost.org 2>/dev/null || true)
ynh_validate_ip6 "$ipv6" || ipv6='' ynh_validate_ip6 "$ipv6" || ipv6=''
export ipv4
export ipv6
# add domain conf files # add domain conf files
for domain in $YNH_DOMAINS; do for domain in $YNH_DOMAINS; do
cat domain.tpl \ export domain
| sed "s/{{ domain }}/${domain}/g" \ ynh_render_template "domain.tpl" "${dnsmasq_dir}/${domain}"
| sed "s/{{ ip }}/${ipv4}/g" \
> "${dnsmasq_dir}/${domain}"
[[ -n $ipv6 ]] \
&& echo "address=/${domain}/${ipv6}" >> "${dnsmasq_dir}/${domain}"
done done
# remove old domain conf files # remove old domain conf files
@ -69,8 +68,16 @@ do_post_regen() {
short_hostname=$(hostname -s) short_hostname=$(hostname -s)
grep -q "127.0.0.1.*$short_hostname" /etc/hosts || echo -e "\n127.0.0.1\t$short_hostname" >>/etc/hosts grep -q "127.0.0.1.*$short_hostname" /etc/hosts || echo -e "\n127.0.0.1\t$short_hostname" >>/etc/hosts
[[ -z "$regen_conf_files" ]] \ [[ -n "$regen_conf_files" ]] || return
|| service dnsmasq restart
# Remove / disable services likely to conflict with dnsmasq
for SERVICE in systemd-resolved bind9
do
systemctl is-enabled $SERVICE &>/dev/null && systemctl disable $SERVICE 2>/dev/null
systemctl is-active $SERVICE &>/dev/null && systemctl stop $SERVICE
done
systemctl restart dnsmasq
} }
FORCE=${2:-0} FORCE=${2:-0}

View file

@ -75,7 +75,6 @@ class BaseSystemDiagnoser(Diagnoser):
summary="diagnosis_basesystem_ynh_main_version" if consistent_versions else "diagnosis_basesystem_ynh_inconsistent_versions", summary="diagnosis_basesystem_ynh_main_version" if consistent_versions else "diagnosis_basesystem_ynh_inconsistent_versions",
details=ynh_version_details) details=ynh_version_details)
if self.is_vulnerable_to_meltdown(): if self.is_vulnerable_to_meltdown():
yield dict(meta={"test": "meltdown"}, yield dict(meta={"test": "meltdown"},
status="ERROR", status="ERROR",

View file

@ -12,9 +12,7 @@ from yunohost.utils.network import dig
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain
# We put here domains we know has dyndns provider, but that are not yet YNH_DYNDNS_DOMAINS = ['nohost.me', 'noho.st', 'ynh.fr']
# registered in the public suffix list
PENDING_SUFFIX_LIST = ['ynh.fr', 'netlib.re']
class DNSRecordsDiagnoser(Diagnoser): class DNSRecordsDiagnoser(Diagnoser):
@ -38,7 +36,7 @@ class DNSRecordsDiagnoser(Diagnoser):
psl = PublicSuffixList() psl = PublicSuffixList()
domains_from_registrar = [psl.get_public_suffix(domain) for domain in all_domains] domains_from_registrar = [psl.get_public_suffix(domain) for domain in all_domains]
domains_from_registrar = [domain for domain in domains_from_registrar if "." in domain] domains_from_registrar = [domain for domain in domains_from_registrar if "." in domain]
domains_from_registrar = set(domains_from_registrar) - set(PENDING_SUFFIX_LIST) domains_from_registrar = set(domains_from_registrar) - set(YNH_DYNDNS_DOMAINS + ["netlib.re"])
for report in self.check_expiration_date(domains_from_registrar): for report in self.check_expiration_date(domains_from_registrar):
yield report yield report
@ -100,7 +98,13 @@ class DNSRecordsDiagnoser(Diagnoser):
summary=summary) summary=summary)
if discrepancies: if discrepancies:
output["details"] = ["diagnosis_dns_point_to_doc"] + discrepancies # For ynh-managed domains (nohost.me etc...), tell people to try to "yunohost dyndns update --force"
if any(domain.endswith(ynh_dyndns_domain) for ynh_dyndns_domain in YNH_DYNDNS_DOMAINS):
output["details"] = ["diagnosis_dns_try_dyndns_update_force"]
# Otherwise point to the documentation
else:
output["details"] = ["diagnosis_dns_point_to_doc"]
output["details"] += discrepancies
yield output yield output

View file

@ -3,9 +3,9 @@
import os import os
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
from yunohost.utils.error import YunohostError
from yunohost.service import _get_services from yunohost.service import _get_services
class PortsDiagnoser(Diagnoser): class PortsDiagnoser(Diagnoser):
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]

View file

@ -8,7 +8,6 @@ from moulinette.utils.filesystem import read_file
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
from yunohost.domain import domain_list from yunohost.domain import domain_list
from yunohost.utils.error import YunohostError
DIAGNOSIS_SERVER = "diagnosis.yunohost.org" DIAGNOSIS_SERVER = "diagnosis.yunohost.org"

View file

@ -47,7 +47,6 @@ class MailDiagnoser(Diagnoser):
status="SUCCESS", status="SUCCESS",
summary="diagnosis_mail_" + name + "_ok") summary="diagnosis_mail_" + name + "_ok")
def check_outgoing_port_25(self): def check_outgoing_port_25(self):
""" """
Check outgoing port 25 is open and not blocked by router Check outgoing port 25 is open and not blocked by router
@ -64,7 +63,6 @@ class MailDiagnoser(Diagnoser):
details=["diagnosis_mail_outgoing_port_25_blocked_details", details=["diagnosis_mail_outgoing_port_25_blocked_details",
"diagnosis_mail_outgoing_port_25_blocked_relay_vpn"]) "diagnosis_mail_outgoing_port_25_blocked_relay_vpn"])
def check_ehlo(self): def check_ehlo(self):
""" """
Check the server is reachable from outside and it's the good one Check the server is reachable from outside and it's the good one
@ -99,7 +97,6 @@ class MailDiagnoser(Diagnoser):
summary="diagnosis_mail_ehlo_wrong", summary="diagnosis_mail_ehlo_wrong",
details=["diagnosis_mail_ehlo_wrong_details"]) details=["diagnosis_mail_ehlo_wrong_details"])
def check_fcrdns(self): def check_fcrdns(self):
""" """
Check the reverse DNS is well defined by doing a Forward-confirmed Check the reverse DNS is well defined by doing a Forward-confirmed
@ -126,7 +123,7 @@ class MailDiagnoser(Diagnoser):
query += '.ip6.arpa' query += '.ip6.arpa'
# Do the DNS Query # Do the DNS Query
status, value = dig(query, 'PTR') status, value = dig(query, 'PTR', resolvers="force_external")
if status == "nok": if status == "nok":
yield dict(meta={"test": "mail_fcrdns", "ipversion": ipversion}, yield dict(meta={"test": "mail_fcrdns", "ipversion": ipversion},
data={"ip": ip, "ehlo_domain": self.ehlo_domain}, data={"ip": ip, "ehlo_domain": self.ehlo_domain},
@ -148,7 +145,6 @@ class MailDiagnoser(Diagnoser):
summary="diagnosis_mail_fcrdns_different_from_ehlo_domain", summary="diagnosis_mail_fcrdns_different_from_ehlo_domain",
details=details) details=details)
def check_blacklist(self): def check_blacklist(self):
""" """
Check with dig onto blacklist DNS server Check with dig onto blacklist DNS server
@ -225,7 +221,6 @@ class MailDiagnoser(Diagnoser):
status="SUCCESS", status="SUCCESS",
summary="diagnosis_mail_queue_ok") summary="diagnosis_mail_queue_ok")
def get_ips_checked(self): def get_ips_checked(self):
outgoing_ipversions = [] outgoing_ipversions = []
outgoing_ips = [] outgoing_ips = []
@ -245,5 +240,6 @@ class MailDiagnoser(Diagnoser):
outgoing_ips.append(global_ipv6) outgoing_ips.append(global_ipv6)
return (outgoing_ipversions, outgoing_ips) return (outgoing_ipversions, outgoing_ips)
def main(args, env, loggers): def main(args, env, loggers):
return MailDiagnoser(args, env, loggers).diagnose() return MailDiagnoser(args, env, loggers).diagnose()

View file

@ -5,6 +5,7 @@ import os
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
from yunohost.service import service_status from yunohost.service import service_status
class ServicesDiagnoser(Diagnoser): class ServicesDiagnoser(Diagnoser):
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
@ -36,5 +37,6 @@ class ServicesDiagnoser(Diagnoser):
yield item yield item
def main(args, env, loggers): def main(args, env, loggers):
return ServicesDiagnoser(args, env, loggers).diagnose() return ServicesDiagnoser(args, env, loggers).diagnose()

View file

@ -1,9 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import psutil import psutil
import subprocess
import datetime
import re
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
class SystemResourcesDiagnoser(Diagnoser): class SystemResourcesDiagnoser(Diagnoser):
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
@ -93,9 +97,50 @@ class SystemResourcesDiagnoser(Diagnoser):
item["status"] = "SUCCESS" item["status"] = "SUCCESS"
item["summary"] = "diagnosis_diskusage_ok" item["summary"] = "diagnosis_diskusage_ok"
yield item yield item
#
# Recent kills by oom_reaper
#
kills_count = self.recent_kills_by_oom_reaper()
if kills_count:
kills_summary = "\n".join(["%s (x%s)" % (proc, count) for proc, count in kills_count])
yield dict(meta={"test": "oom_reaper"},
status="WARNING",
summary="diagnosis_processes_killed_by_oom_reaper",
data={"kills_summary": kills_summary})
def recent_kills_by_oom_reaper(self):
if not os.path.exists("/var/log/kern.log"):
return []
def analyzed_kern_log():
cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true'
out = subprocess.check_output(cmd, shell=True).strip()
lines = out.split("\n") if out else []
now = datetime.datetime.now()
for line in reversed(lines):
# Lines look like :
# Aug 25 18:48:21 yolo kernel: [ 9623.613667] oom_reaper: reaped process 11509 (uwsgi), now anon-rss:0kB, file-rss:0kB, shmem-rss:328kB
date_str = str(now.year) + " " + " ".join(line.split()[:3])
date = datetime.datetime.strptime(date_str, '%Y %b %d %H:%M:%S')
diff = now - date
if diff.days >= 1:
break
process_killed = re.search(r"\(.*\)", line).group().strip("()")
yield process_killed
processes = list(analyzed_kern_log())
kills_count = [(p, len([p_ for p_ in processes if p_ == p])) for p in set(processes)]
kills_count = sorted(kills_count, key=lambda p: p[1], reverse=True)
return kills_count
def human_size(bytes_): def human_size(bytes_):
# Adapted from https://stackoverflow.com/a/1094933 # Adapted from https://stackoverflow.com/a/1094933
@ -114,5 +159,6 @@ def round_(n):
n = int(round(n)) n = int(round(n))
return n return n
def main(args, env, loggers): def main(args, env, loggers):
return SystemResourcesDiagnoser(args, env, loggers).diagnose() return SystemResourcesDiagnoser(args, env, loggers).diagnose()

View file

@ -2,7 +2,6 @@
import os import os
import subprocess
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
from yunohost.regenconf import _get_regenconf_infos, _calculate_hash from yunohost.regenconf import _get_regenconf_infos, _calculate_hash

View file

@ -39,6 +39,9 @@ else
# Restore the configuration # Restore the configuration
mv /etc/ldap/slapd.d "$TMPDIR" mv /etc/ldap/slapd.d "$TMPDIR"
mkdir -p /etc/ldap/slapd.d mkdir -p /etc/ldap/slapd.d
cp -a "${backup_dir}/ldap.conf" /etc/ldap/ldap.conf
cp -a "${backup_dir}/slapd.ldif" /etc/ldap/slapd.ldif
# Legacy thing but we need it to force the regen-conf in case of it exist
cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf cp -a "${backup_dir}/slapd.conf" /etc/ldap/slapd.conf
slapadd -F /etc/ldap/slapd.d -b cn=config \ slapadd -F /etc/ldap/slapd.d -b cn=config \
-l "${backup_dir}/cn=config.master.ldif" \ -l "${backup_dir}/cn=config.master.ldif" \

View file

@ -1,42 +1,5 @@
backup_dir="$1/conf/ynh/mysql" # We don't backup/restore mysql password anymore
MYSQL_PKG="$(dpkg --list | sed -ne 's/^ii \(mariadb-server-[[:digit:].]\+\) .*$/\1/p')" # c.f. https://github.com/YunoHost/yunohost/pull/912
. /usr/share/yunohost/helpers # This is a dummy empty file as a workaround for
# https://github.com/YunoHost/issues/issues/1553 until it is fixed
# ensure that mysql is running
service mysql status >/dev/null 2>&1 \
|| service mysql start
# retrieve current and new password
[ -f /etc/yunohost/mysql ] \
&& curr_pwd=$(cat /etc/yunohost/mysql)
new_pwd=$(cat "${backup_dir}/root_pwd" || cat "${backup_dir}/mysql")
[ -z "$curr_pwd" ] && curr_pwd="yunohost"
[ -z "$new_pwd" ] && {
new_pwd=$(ynh_string_random 10)
}
# attempt to change it
mysqladmin -s -u root -p"$curr_pwd" password "$new_pwd" || {
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, and is going to reset the MySQL root password." \
"You can find this new password in /etc/yunohost/mysql." >&2
# set new password with debconf
debconf-set-selections << EOF
$MYSQL_PKG mysql-server/root_password password $new_pwd
$MYSQL_PKG mysql-server/root_password_again password $new_pwd
EOF
# reconfigure Debian package
dpkg-reconfigure -freadline -u "$MYSQL_PKG" 2>&1
}
# store new root password
echo "$new_pwd" | tee /etc/yunohost/mysql
chmod 400 /etc/yunohost/mysql
# reload the grant tables
mysqladmin -s -u root -p"$new_pwd" reload

8
data/other/ffdhe2048.pem Normal file
View file

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

View file

@ -1,4 +1,9 @@
address=/{{ domain }}/{{ ip }} address=/{{ domain }}/{{ ipv4 }}
address=/xmpp-upload.{{ domain }}/{{ ipv4 }}
{% if ipv6 %}
address=/{{ domain }}/{{ ipv6 }}
address=/xmpp-upload.{{ domain }}/{{ ipv6 }}
{% endif %}
txt-record={{ domain }},"v=spf1 mx a -all" txt-record={{ domain }},"v=spf1 mx a -all"
mx-host={{ domain }},{{ domain }},5 mx-host={{ domain }},{{ domain }},5
srv-host=_xmpp-client._tcp.{{ domain }},{{ domain }},5222,0,5 srv-host=_xmpp-client._tcp.{{ domain }},{{ domain }},5222,0,5

View file

@ -14,18 +14,19 @@ mail_plugins = $mail_plugins quota
############################################################################### ###############################################################################
# generated 2020-04-03, Mozilla Guideline v5.4, Dovecot 2.2.27, OpenSSL 1.1.0l, intermediate configuration # generated 2020-08-18, Mozilla Guideline v5.6, Dovecot 2.3.4, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=dovecot&version=2.2.27&config=intermediate&openssl=1.1.0l&guideline=5.4 # https://ssl-config.mozilla.org/#server=dovecot&version=2.3.4&config=intermediate&openssl=1.1.1d&guideline=5.6
ssl = required ssl = required
ssl_cert = </etc/yunohost/certs/{{ main_domain }}/crt.pem ssl_cert = </etc/yunohost/certs/{{ main_domain }}/crt.pem
ssl_key = </etc/yunohost/certs/{{ main_domain }}/key.pem ssl_key = </etc/yunohost/certs/{{ main_domain }}/key.pem
ssl_dh_parameters_length = 2048 # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
ssl_dh = </usr/share/yunohost/other/ffdhe2048.pem
# intermediate configuration # intermediate configuration
ssl_protocols = TLSv1.2 ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl_prefer_server_ciphers = no ssl_prefer_server_ciphers = no

View file

@ -47,6 +47,10 @@ Component "muc.{{ domain }}" "muc"
muc_event_rate = 0.5 muc_event_rate = 0.5
muc_burst_factor = 10 muc_burst_factor = 10
room_default_config = {
logging = true,
persistent = true
};
---Set up a PubSub server ---Set up a PubSub server
Component "pubsub.{{ domain }}" "pubsub" Component "pubsub.{{ domain }}" "pubsub"

View file

@ -1,3 +1,3 @@
lua_shared_dict cache 10m; lua_shared_dict cache 10m;
init_by_lua_file /usr/share/ssowat/init.lua; init_by_lua_file /usr/share/ssowat/init.lua;
server_names_hash_bucket_size 64; server_names_hash_bucket_size 128;

View file

@ -0,0 +1,7 @@
# Avoid the nginx path/alias traversal weakness ( #1037 )
rewrite ^/yunohost/sso$ /yunohost/sso/ permanent;
location /yunohost/sso/ {
# This is an empty location, only meant to avoid other locations
# from matching /yunohost/sso, such that it's correctly handled by ssowat
}

View file

@ -0,0 +1,3 @@
location / {
return 302 https://$http_host/yunohost/admin;
}

View file

@ -2,21 +2,25 @@ ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m; # about 200000 sessions ssl_session_cache shared:SSL:50m; # about 200000 sessions
ssl_session_tickets off; ssl_session_tickets off;
# nginx 1.10 in stretch doesn't support TLS1.3 and Mozilla doesn't have any {% if compatibility == "modern" %}
# "modern" config recommendation with it. # generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, modern configuration
# So until buster the modern conf is same as intermediate # https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=modern&openssl=1.1.1d&guideline=5.6
{% if compatibility == "modern" %} {% else %} {% endif %} ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
{% else %}
# Ciphers with intermediate compatibility # Ciphers with intermediate compatibility
# generated 2020-04-03, Mozilla Guideline v5.4, nginx 1.10.3, OpenSSL 1.1.0l, intermediate configuration # generated 2020-08-14, Mozilla Guideline v5.6, nginx 1.14.2, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=nginx&version=1.10.3&config=intermediate&openssl=1.1.0l&guideline=5.4 # https://ssl-config.mozilla.org/#server=nginx&version=1.14.2&config=intermediate&openssl=1.1.1d&guideline=5.6
ssl_protocols TLSv1.2; ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off; ssl_prefer_server_ciphers off;
# Uncomment the following directive after DH generation # Pre-defined FFDHE group (RFC 7919)
# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048 # From https://ssl-config.mozilla.org/ffdhe2048.txt
#ssl_dhparam /etc/ssl/private/dh2048.pem; # https://security.stackexchange.com/a/149818
ssl_dhparam /usr/share/yunohost/other/ffdhe2048.pem;
{% endif %}
# Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners
# https://wiki.mozilla.org/Security/Guidelines/Web_Security # https://wiki.mozilla.org/Security/Guidelines/Web_Security

View file

@ -14,7 +14,7 @@ server {
include /etc/nginx/conf.d/{{ domain }}.d/*.conf; include /etc/nginx/conf.d/{{ domain }}.d/*.conf;
location /yunohost/admin { location /yunohost {
return 301 https://$http_host$request_uri; return 301 https://$http_host$request_uri;
} }
@ -60,6 +60,7 @@ server {
include /etc/nginx/conf.d/{{ domain }}.d/*.conf; include /etc/nginx/conf.d/{{ domain }}.d/*.conf;
include /etc/nginx/conf.d/yunohost_sso.conf.inc;
include /etc/nginx/conf.d/yunohost_admin.conf.inc; include /etc/nginx/conf.d/yunohost_admin.conf.inc;
include /etc/nginx/conf.d/yunohost_api.conf.inc; include /etc/nginx/conf.d/yunohost_api.conf.inc;

View file

@ -2,13 +2,7 @@ server {
listen 80 default_server; listen 80 default_server;
listen [::]:80 default_server; listen [::]:80 default_server;
location / { include /etc/nginx/conf.d/default.d/*.conf;
return 302 https://$http_host/yunohost/admin;
}
location /yunohost/admin {
return 301 https://$http_host$request_uri;
}
} }
server { server {
@ -23,21 +17,12 @@ server {
more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload"; more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload";
more_set_headers "Referrer-Policy : 'same-origin'"; more_set_headers "Referrer-Policy : 'same-origin'";
location / {
return 302 https://$http_host/yunohost/admin;
}
location /yunohost { location /yunohost {
# Block crawlers bot
if ($http_user_agent ~ (crawl|Googlebot|Slurp|spider|bingbot|tracker|click|parser|spider|facebookexternalhit) ) {
return 403;
}
# X-Robots-Tag to precise the rules applied.
add_header X-Robots-Tag "nofollow, noindex, noarchive, nosnippet";
# Redirect most of 404 to maindomain.tld/yunohost/sso # Redirect most of 404 to maindomain.tld/yunohost/sso
access_by_lua_file /usr/share/ssowat/access.lua; access_by_lua_file /usr/share/ssowat/access.lua;
} }
include /etc/nginx/conf.d/yunohost_admin.conf.inc; include /etc/nginx/conf.d/yunohost_admin.conf.inc;
include /etc/nginx/conf.d/yunohost_api.conf.inc; include /etc/nginx/conf.d/yunohost_api.conf.inc;
include /etc/nginx/conf.d/default.d/*.conf;
} }

View file

@ -15,6 +15,18 @@ base dc=yunohost,dc=org
# The LDAP protocol version to use. # The LDAP protocol version to use.
#ldap_version 3 #ldap_version 3
# The DN to bind with for normal lookups.
#binddn cn=annonymous,dc=example,dc=net
#bindpw secret
# The DN used for password modifications by root.
#rootpwmoddn cn=admin,dc=example,dc=com
# SSL options
#ssl off
#tls_reqcert never
tls_cacertfile /etc/ssl/certs/ca-certificates.crt
# The search scope. # The search scope.
#scope sub #scope sub

View file

@ -1,12 +1,8 @@
# /etc/nsswitch.conf # /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.
passwd: compat ldap passwd: files systemd ldap
group: compat ldap group: files systemd ldap
shadow: compat ldap shadow: files ldap
gshadow: files gshadow: files
hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns hosts: files myhostname mdns4_minimal [NOTFOUND=return] dns

View file

@ -19,34 +19,35 @@ readme_directory = no
# -- TLS for incoming connections # -- TLS for incoming connections
############################################################################### ###############################################################################
# generated 2020-04-03, Mozilla Guideline v5.4, Postfix 3.1.14, OpenSSL 1.1.0l, intermediate configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.1.14&config=intermediate&openssl=1.1.0l&guideline=5.4
# (No modern conf support until we're on buster...)
# {% if compatibility == "intermediate" %} {% else %} {% endif %}
smtpd_use_tls = yes smtpd_use_tls = yes
smtpd_tls_security_level = may smtpd_tls_security_level = may
smtpd_tls_auth_only = yes smtpd_tls_auth_only = yes
smtpd_tls_cert_file = /etc/yunohost/certs/{{ main_domain }}/crt.pem smtpd_tls_cert_file = /etc/yunohost/certs/{{ main_domain }}/crt.pem
smtpd_tls_key_file = /etc/yunohost/certs/{{ main_domain }}/key.pem smtpd_tls_key_file = /etc/yunohost/certs/{{ main_domain }}/key.pem
{% if compatibility == "intermediate" %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, intermediate configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=intermediate&openssl=1.1.1d&guideline=5.6
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1 smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# smtpd_tls_mandatory_ciphers = medium # (c.f. below) smtpd_tls_mandatory_ciphers = medium
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem
# not actually 1024 bits, this applies to all DHE >= 1024 bits # not actually 1024 bits, this applies to all DHE >= 1024 bits
# smtpd_tls_dh1024_param_file = /path/to/dhparam.pem smtpd_tls_dh1024_param_file = /usr/share/yunohost/other/ffdhe2048.pem
tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
{% else %}
# generated 2020-08-18, Mozilla Guideline v5.6, Postfix 3.4.14, OpenSSL 1.1.1d, modern configuration
# https://ssl-config.mozilla.org/#server=postfix&version=3.4.14&config=modern&openssl=1.1.1d&guideline=5.6
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, !TLSv1.2
{% endif %}
# This custom medium cipherlist recommendation only works if we have a DH ... which we don't, c.f. https://github.com/YunoHost/issues/issues/93
# On the other hand, the postfix doc strongly discourage tweaking this list ... So whatever, let's keep the mandatory_ciphers to high like we did before applying the Mozilla recommendation ...
#tls_medium_cipherlist = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
tls_preempt_cipherlist = no tls_preempt_cipherlist = no
# Custom Yunohost stuff ... because we can't use the recommendation about medium cipher list ...
smtpd_tls_mandatory_ciphers=high
smtpd_tls_eecdh_grade = ultra
############################################################################### ###############################################################################
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_loglevel=1 smtpd_tls_loglevel=1
@ -170,7 +171,7 @@ smtpd_milters = inet:localhost:11332
milter_default_action = accept milter_default_action = accept
# Avoid to send simultaneously too many emails # Avoid to send simultaneously too many emails
smtp_destination_concurrency_limit = 1 smtp_destination_concurrency_limit = 2
default_destination_rate_delay = 5s default_destination_rate_delay = 5s
# Avoid email adress scanning # Avoid email adress scanning

View file

@ -2,58 +2,62 @@
## Version 0.1 ## Version 0.1
## Adrien Beudin ## Adrien Beudin
dn: cn=mailserver,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: mailserver
#
# Attributes # Attributes
attributetype ( 1.3.6.1.4.1.40328.1.20.2.1 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.1
NAME 'maildrop' NAME 'maildrop'
DESC 'Mail addresses where mails are forwarded -- ie forwards' DESC 'Mail addresses where mails are forwarded -- ie forwards'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.2 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.2
NAME 'mailalias' NAME 'mailalias'
DESC 'Mail addresses accepted by this account -- ie aliases' DESC 'Mail addresses accepted by this account -- ie aliases'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.3 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.3
NAME 'mailenable' NAME 'mailenable'
DESC 'Mail Account validity' DESC 'Mail Account validity'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{8}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{8})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.4 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.4
NAME 'mailbox' NAME 'mailbox'
DESC 'Mailbox path where mails are delivered' DESC 'Mailbox path where mails are delivered'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.5 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.5
NAME 'virtualdomain' NAME 'virtualdomain'
DESC 'A mail domain name' DESC 'A mail domain name'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.6 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.6
NAME 'virtualdomaindescription' NAME 'virtualdomaindescription'
DESC 'Virtual domain description' DESC 'Virtual domain description'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512})
#
attributetype ( 1.3.6.1.4.1.40328.1.20.2.7 olcAttributeTypes: ( 1.3.6.1.4.1.40328.1.20.2.7
NAME 'mailuserquota' NAME 'mailuserquota'
DESC 'Mailbox quota for a user' DESC 'Mailbox quota for a user'
EQUALITY caseIgnoreMatch EQUALITY caseIgnoreMatch
SUBSTR caseIgnoreSubstringsMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{16} SINGLE-VALUE ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{16} SINGLE-VALUE )
#
# Mail Account Objectclass # Mail Account Objectclass
objectclass ( 1.3.6.1.4.1.40328.1.1.2.1 olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.1
NAME 'mailAccount' NAME 'mailAccount'
DESC 'Mail Account' DESC 'Mail Account'
SUP top SUP top
@ -65,9 +69,9 @@ objectclass ( 1.3.6.1.4.1.40328.1.1.2.1
mailalias $ maildrop $ mailenable $ mailbox $ mailuserquota mailalias $ maildrop $ mailenable $ mailbox $ mailuserquota
) )
) )
#
# Mail Domain Objectclass # Mail Domain Objectclass
objectclass ( 1.3.6.1.4.1.40328.1.1.2.2 olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.2
NAME 'mailDomain' NAME 'mailDomain'
DESC 'Domain mail entry' DESC 'Domain mail entry'
SUP top SUP top
@ -79,9 +83,9 @@ objectclass ( 1.3.6.1.4.1.40328.1.1.2.2
virtualdomaindescription $ mailuserquota virtualdomaindescription $ mailuserquota
) )
) )
#
# Mail Group Objectclass # Mail Group Objectclass
objectclass ( 1.3.6.1.4.1.40328.1.1.2.3 olcObjectClasses: ( 1.3.6.1.4.1.40328.1.1.2.3
NAME 'mailGroup' SUP top AUXILIARY NAME 'mailGroup' SUP top AUXILIARY
DESC 'Mail Group' DESC 'Mail Group'
MUST ( mail ) MUST ( mail )

View file

@ -1,33 +1,35 @@
#dn: cn=yunohost,cn=schema,cn=config # Yunohost schema for group and permission support
#objectClass: olcSchemaConfig
#cn: yunohost dn: cn=yunohost,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: yunohost
# ATTRIBUTES # ATTRIBUTES
# For Permission # For Permission
attributetype ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission' olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.1 NAME 'permission'
DESC 'Yunohost permission on user and group side' DESC 'Yunohost permission on user and group side'
SUP distinguishedName ) SUP distinguishedName )
attributetype ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission' olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.2 NAME 'groupPermission'
DESC 'Yunohost permission for a group on permission side' DESC 'Yunohost permission for a group on permission side'
SUP distinguishedName ) SUP distinguishedName )
attributetype ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission' olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.3 NAME 'inheritPermission'
DESC 'Yunohost permission for user on permission side' DESC 'Yunohost permission for user on permission side'
SUP distinguishedName ) SUP distinguishedName )
attributetype ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL' olcAttributeTypes: ( 1.3.6.1.4.1.17953.9.1.4 NAME 'URL'
DESC 'Yunohost application URL' DESC 'Yunohost application URL'
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
# OBJECTCLASS # OBJECTCLASS
# For Applications # For Applications
objectclass ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh' olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.1 NAME 'groupOfNamesYnh'
DESC 'Yunohost user group' DESC 'Yunohost user group'
SUP top AUXILIARY SUP top AUXILIARY
MAY ( member $ businessCategory $ seeAlso $ owner $ ou $ o $ permission ) ) MAY ( member $ businessCategory $ seeAlso $ owner $ ou $ o $ permission ) )
objectclass ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh' olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.2 NAME 'permissionYnh'
DESC 'a Yunohost application' DESC 'a Yunohost application'
SUP top AUXILIARY SUP top AUXILIARY
MUST cn MUST cn
MAY ( groupPermission $ inheritPermission $ URL ) ) MAY ( groupPermission $ inheritPermission $ URL ) )
# For User # For User
objectclass ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh' olcObjectClasses: ( 1.3.6.1.4.1.17953.9.2.3 NAME 'userPermissionYnh'
DESC 'a Yunohost application' DESC 'a Yunohost application'
SUP top AUXILIARY SUP top AUXILIARY
MAY ( permission ) ) MAY ( permission ) )

View file

@ -1,154 +0,0 @@
# This is the main slapd configuration file. See slapd.conf(5) for more
# info on the configuration options.
#######################################################################
# Global Directives:
# Features to permit
#allow bind_v2
# Schema and objectClass definitions
include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/mailserver.schema
include /etc/ldap/schema/sudo.schema
include /etc/ldap/schema/yunohost.schema
# Where the pid file is put. The init.d script
# will not stop the server if you change this.
pidfile /var/run/slapd/slapd.pid
# List of arguments that were passed to the server
argsfile /var/run/slapd/slapd.args
# Read slapd.conf(5) for possible values
loglevel none
# Hashes to be used in generation of user passwords
password-hash {SSHA}
# Where the dynamically loaded modules are stored
modulepath /usr/lib/ldap
moduleload back_mdb
moduleload memberof
# The maximum number of entries that is returned for a search operation
sizelimit 500
# The tool-threads parameter sets the actual amount of cpu's that is used
# for indexing.
tool-threads 1
# TLS Support
TLSCertificateFile /etc/yunohost/certs/yunohost.org/crt.pem
TLSCertificateKeyFile /etc/yunohost/certs/yunohost.org/key.pem
#######################################################################
# Specific Backend Directives for mdb:
# Backend specific directives apply to this backend until another
# 'backend' directive occurs
backend mdb
#######################################################################
# Specific Directives for database #1, of type mdb:
# Database specific directives apply to this databasse until another
# 'database' directive occurs
database mdb
# The base of your directory in database #1
suffix "dc=yunohost,dc=org"
# rootdn directive for specifying a superuser on the database. This is needed
# for syncrepl.
# rootdn "cn=admin,dc=yunohost,dc=org"
# Where the database file are physically stored for database #1
directory "/var/lib/ldap"
# Indexing options for database #1
index objectClass eq
index uid,sudoUser eq,sub
index entryCSN,entryUUID eq
index cn,mail eq
index gidNumber,uidNumber eq
index member,memberUid,uniqueMember eq
index virtualdomain eq
index permission eq
# Save the time that the entry gets modified, for database #1
lastmod on
# Checkpoint the BerkeleyDB database periodically in case of system
# failure and to speed slapd shutdown.
checkpoint 512 30
# The userPassword by default can be changed
# by the entry owning it if they are authenticated.
# Others should not be able to see it, except the
# admin entry below
# These access lines apply to database #1 only
access to attrs=userPassword,shadowLastChange
by dn="cn=admin,dc=yunohost,dc=org" write
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by anonymous auth
by self write
by * none
# Personnal information can be changed by the entry
# owning it if they are authenticated.
# Others should be able to see it.
access to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn
by dn="cn=admin,dc=yunohost,dc=org" write
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by self write
by * read
# Ensure read access to the base for things like
# supportedSASLMechanisms. Without this you may
# have problems with SASL not knowing what
# mechanisms are available and the like.
# Note that this is covered by the 'access to *'
# ACL below too but if you change that as people
# are wont to do you'll still need this if you
# want SASL (and possible other things) to work
# happily.
access to dn.base="" by * read
# The admin dn has full write access, everyone else
# can read everything.
access to *
by dn="cn=admin,dc=yunohost,dc=org" write
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by group/groupOfNames/Member="cn=admin,ou=groups,dc=yunohost,dc=org" write
by * read
# Configure Memberof Overlay (used for Yunohost permission)
# Link user <-> group
#dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
overlay memberof
memberof-group-oc groupOfNamesYnh
memberof-member-ad member
memberof-memberof-ad memberOf
memberof-dangling error
memberof-refint TRUE
# Link permission <-> groupes
#dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config
overlay memberof
memberof-group-oc permissionYnh
memberof-member-ad groupPermission
memberof-memberof-ad permission
memberof-dangling error
memberof-refint TRUE
# Link permission <-> user
#dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config
overlay memberof
memberof-group-oc permissionYnh
memberof-member-ad inheritPermission
memberof-memberof-ad permission
memberof-dangling error
memberof-refint TRUE

View file

@ -0,0 +1,235 @@
# OpenLDAP server configuration for Yunohost
# ------------------------------------------
#
# Because of the Yunohost's regen-conf mechanism, it is NOT POSSIBLE to
# edit the config database using an LDAP request.
#
# If you wish to edit the config database, you should edit THIS file
# and update the config database based on this file.
#
# Config database customization:
# 1. Edit this file as you want.
# 2. Apply your modifications. For this just run this following command in a shell:
# $ /usr/share/yunohost/hooks/conf_regen/06-slapd apply_config
#
# Note that if you customize this file, YunoHost's regen-conf will NOT
# overwrite this file. But that also means that you should be careful about
# upgrades, because they may ship important/necessary changes to this
# configuration that you will have to propagate yourself.
#
# Main configuration
#
dn: cn=config
objectClass: olcGlobal
cn: config
olcConfigFile: /etc/ldap/slapd.conf
olcConfigDir: /etc/ldap/slapd.d/
# List of arguments that were passed to the server
olcArgsFile: /var/run/slapd/slapd.args
#
olcAttributeOptions: lang-
olcAuthzPolicy: none
olcConcurrency: 0
olcConnMaxPending: 100
olcConnMaxPendingAuth: 1000
olcIdleTimeout: 0
olcIndexSubstrIfMaxLen: 4
olcIndexSubstrIfMinLen: 2
olcIndexSubstrAnyLen: 4
olcIndexSubstrAnyStep: 2
olcIndexIntLen: 4
olcListenerThreads: 1
olcLocalSSF: 71
# Read slapd.conf(5) for possible values
olcLogLevel: None
# Where the pid file is put. The init.d script
# will not stop the server if you change this.
olcPidFile: /var/run/slapd/slapd.pid
olcReverseLookup: FALSE
olcThreads: 16
# TLS Support
olcTLSCertificateFile: /etc/yunohost/certs/yunohost.org/crt.pem
olcTLSCertificateKeyFile: /etc/yunohost/certs/yunohost.org/key.pem
olcTLSVerifyClient: never
olcTLSProtocolMin: 0.0
# The tool-threads parameter sets the actual amount of cpu's that is used
# for indexing.
olcToolThreads: 1
structuralObjectClass: olcGlobal
#
# Schema and objectClass definitions
#
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
include: file:///etc/ldap/schema/core.ldif
include: file:///etc/ldap/schema/cosine.ldif
include: file:///etc/ldap/schema/nis.ldif
include: file:///etc/ldap/schema/inetorgperson.ldif
include: file:///etc/ldap/schema/mailserver.ldif
include: file:///etc/ldap/schema/sudo.ldif
include: file:///etc/ldap/schema/permission.ldif
#
# Module management
#
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
# Where the dynamically loaded modules are stored
olcModulePath: /usr/lib/ldap
olcModuleLoad: {0}back_mdb
olcModuleLoad: {1}memberof
structuralObjectClass: olcModuleList
#
# Frontend database
#
dn: olcDatabase={-1}frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
olcAddContentAcl: FALSE
olcLastMod: TRUE
olcSchemaDN: cn=Subschema
# Hashes to be used in generation of user passwords
olcPasswordHash: {SSHA}
structuralObjectClass: olcDatabaseConfig
#
# Config database Configuration (#0)
#
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
# Give access to root user.
# This give the possiblity to the admin to customize the LDAP configuration
olcAccess: {0}to * by * none
olcAddContentAcl: TRUE
olcLastMod: TRUE
olcRootDN: cn=config
structuralObjectClass: olcDatabaseConfig
#
# Main database Configuration (#1)
#
dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
# The base of your directory in database #1
olcSuffix: dc=yunohost,dc=org
#
# The userPassword by default can be changed
# by the entry owning it if they are authenticated.
# Others should not be able to see it, except the
# admin entry below
# These access lines apply to database #1 only
olcAccess: {0}to attrs=userPassword,shadowLastChange
by dn.base="cn=admin,dc=yunohost,dc=org" write
by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by anonymous auth
by self write
by * none
#
# Personnal information can be changed by the entry
# owning it if they are authenticated.
# Others should be able to see it.
olcAccess: {1}to attrs=cn,gecos,givenName,mail,maildrop,displayName,sn
by dn.base="cn=admin,dc=yunohost,dc=org" write
by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by self write
by * read
#
# Ensure read access to the base for things like
# supportedSASLMechanisms. Without this you may
# have problems with SASL not knowing what
# mechanisms are available and the like.
# Note that this is covered by the 'access to *'
# ACL below too but if you change that as people
# are wont to do you'll still need this if you
# want SASL (and possible other things) to work
# happily.
olcAccess: {2}to dn.base=""
by * read
#
# The admin dn has full write access, everyone else
# can read everything.
olcAccess: {3}to *
by dn.base="cn=admin,dc=yunohost,dc=org" write
by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
by group/groupOfNames/member.exact="cn=admin,ou=groups,dc=yunohost,dc=org" write
by * read
#
olcAddContentAcl: FALSE
# Save the time that the entry gets modified, for database #1
olcLastMod: TRUE
# Where the database file are physically stored for database #1
olcDbDirectory: /var/lib/ldap
# Checkpoint the BerkeleyDB database periodically in case of system
# failure and to speed slapd shutdown.
olcDbCheckpoint: 512 30
olcDbNoSync: FALSE
# Indexing options for database #1
olcDbIndex: objectClass eq
olcDbIndex: entryUUID eq
olcDbIndex: entryCSN eq
olcDbIndex: cn eq
olcDbIndex: uid eq,sub
olcDbIndex: uidNumber eq
olcDbIndex: gidNumber eq
olcDbIndex: sudoUser eq,sub
olcDbIndex: member eq
olcDbIndex: mail eq
olcDbIndex: memberUid eq
olcDbIndex: uniqueMember eq
olcDbIndex: virtualdomain eq
olcDbIndex: permission eq
olcDbMaxSize: 10485760
structuralObjectClass: olcMdbConfig
#
# Configure Memberof Overlay (used for Yunohost permission)
#
# Link user <-> group
dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: {0}memberof
olcMemberOfDangling: error
olcMemberOfDanglingError: constraintViolation
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: groupOfNamesYnh
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
structuralObjectClass: olcMemberOf
# Link permission <-> groupes
dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: {1}memberof
olcMemberOfDangling: error
olcMemberOfDanglingError: constraintViolation
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: permissionYnh
olcMemberOfMemberAD: groupPermission
olcMemberOfMemberOfAD: permission
structuralObjectClass: olcMemberOf
# Link permission <-> user
dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: {2}memberof
olcMemberOfDangling: error
olcMemberOfDanglingError: constraintViolation
olcMemberOfRefInt: TRUE
olcMemberOfGroupOC: permissionYnh
olcMemberOfMemberAD: inheritPermission
olcMemberOfMemberOfAD: permission
structuralObjectClass: olcMemberOf

View file

@ -1,76 +1,78 @@
# #
# OpenLDAP schema file for Sudo # OpenLDAP schema file for Sudo
# Save as /etc/openldap/schema/sudo.schema # Save as /etc/openldap/schema/sudo.ldif
# #
attributetype ( 1.3.6.1.4.1.15953.9.1.1 dn: cn=sudo,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: sudo
olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.1
NAME 'sudoUser' NAME 'sudoUser'
DESC 'User(s) who may run sudo' DESC 'User(s) who may run sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.2 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.2
NAME 'sudoHost' NAME 'sudoHost'
DESC 'Host(s) who may run sudo' DESC 'Host(s) who may run sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.3 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.3
NAME 'sudoCommand' NAME 'sudoCommand'
DESC 'Command(s) to be executed by sudo' DESC 'Command(s) to be executed by sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.4 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.4
NAME 'sudoRunAs' NAME 'sudoRunAs'
DESC 'User(s) impersonated by sudo (deprecated)' DESC 'User(s) impersonated by sudo (deprecated)'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.5 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.5
NAME 'sudoOption' NAME 'sudoOption'
DESC 'Options(s) followed by sudo' DESC 'Options(s) followed by sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.6 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.6
NAME 'sudoRunAsUser' NAME 'sudoRunAsUser'
DESC 'User(s) impersonated by sudo' DESC 'User(s) impersonated by sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.7 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.7
NAME 'sudoRunAsGroup' NAME 'sudoRunAsGroup'
DESC 'Group(s) impersonated by sudo' DESC 'Group(s) impersonated by sudo'
EQUALITY caseExactIA5Match EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.8 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.8
NAME 'sudoNotBefore' NAME 'sudoNotBefore'
DESC 'Start of time interval for which the entry is valid' DESC 'Start of time interval for which the entry is valid'
EQUALITY generalizedTimeMatch EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
#
attributetype ( 1.3.6.1.4.1.15953.9.1.9 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.9
NAME 'sudoNotAfter' NAME 'sudoNotAfter'
DESC 'End of time interval for which the entry is valid' DESC 'End of time interval for which the entry is valid'
EQUALITY generalizedTimeMatch EQUALITY generalizedTimeMatch
ORDERING generalizedTimeOrderingMatch ORDERING generalizedTimeOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )
#
attributeTypes ( 1.3.6.1.4.1.15953.9.1.10 olcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.10
NAME 'sudoOrder' NAME 'sudoOrder'
DESC 'an integer to order the sudoRole entries' DESC 'an integer to order the sudoRole entries'
EQUALITY integerMatch EQUALITY integerMatch
ORDERING integerOrderingMatch ORDERING integerOrderingMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
#
objectclass ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL olcObjectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL
DESC 'Sudoer Entries' DESC 'Sudoer Entries'
MUST ( cn ) MUST ( cn )
MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ description )
description )
) )

View file

@ -27,9 +27,6 @@ HostKey {{ key }}{% endfor %}
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 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 %} {% endif %}
# Use kernel sandbox mechanisms where possible in unprivileged processes
UsePrivilegeSeparation sandbox
# LogLevel VERBOSE logs user's key fingerprint on login. # LogLevel VERBOSE logs user's key fingerprint on login.
# Needed to have a clear audit track of which key was using to log in. # Needed to have a clear audit track of which key was using to log in.
SyslogFacility AUTH SyslogFacility AUTH

View file

@ -1,5 +0,0 @@
# https://wiki.debian.org/UnattendedUpgrades#automatic_call_via_.2Fetc.2Fapt.2Fapt.conf.d.2F02periodic
APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::Verbose "1";

View file

@ -1,36 +0,0 @@
// Automatically upgrade packages from these (origin, archive) pairs
Unattended-Upgrade::Allowed-Origins {
"${distro_id} stable";
"${distro_id} testing";
"Depot-Debian testing";
"${distro_id} ${distro_codename}-security";
"${distro_id} ${distro_codename}-updates";
// "${distro_id} ${distro_codename}-proposed-updates";
};
// List of packages to not update
Unattended-Upgrade::Package-Blacklist {
// "vim";
// "libc6";
// "libc6-dev";
// "libc6-i686";
};
// Send email to this address for problems or packages upgrades
// If empty or unset then no email is sent, make sure that you
// have a working mail setup on your system. The package 'mailx'
// must be installed or anything that provides /usr/bin/mail.
//Unattended-Upgrade::Mail "root@localhost";
// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Automatically reboot *WITHOUT CONFIRMATION* if a
// the file /var/run/reboot-required is found after the upgrade
Unattended-Upgrade::Automatic-Reboot "false";
// Use apt bandwidth limit feature, this example limits the download
// speed to 70kb/sec
//Acquire::http::Dl-Limit "70";

View file

@ -20,10 +20,9 @@ nginx:
test_conf: nginx -t test_conf: nginx -t
needs_exposed_ports: [80, 443] needs_exposed_ports: [80, 443]
category: web category: web
nslcd: {} php7.3-fpm:
php7.0-fpm: log: /var/log/php7.3-fpm.log
log: /var/log/php7.0-fpm.log test_conf: php-fpm7.3 --test
test_conf: php-fpm7.0 --test
category: web category: web
postfix: postfix:
log: [/var/log/mail.log,/var/log/mail.err] log: [/var/log/mail.log,/var/log/mail.err]
@ -64,3 +63,5 @@ postgrey: null
spamassassin: null spamassassin: null
rmilter: null rmilter: null
php5-fpm: null php5-fpm: null
php7.0-fpm: null
nslcd: null

219
debian/changelog vendored
View file

@ -1,3 +1,216 @@
yunohost (4.1.0) testing; urgency=low
- Tmp bump of the version number to fix CI (c.f. Breaks: yunohost(<<4.1) in moulinette)
yunohost (4.0.7) stable; urgency=low
- [fix] Require explicitly php7.3-foo packages because in some cases Sury's php7.4- packages are installed and php7.3-fpm doesn't get installed ... (1288159a)
- [fix] Make sure app nginx confs do not prevent the loading of /yunohost/sso (#1044)
Thanks to all contributors <3 ! (Kayou, ljf)
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 04 Sep 2020 14:32:07 +0200
yunohost (4.0.6.1) stable; urgency=low
- [fix] Stupid syntax issue in dovecot conf
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 01 Sep 2020 02:00:19 +0200
yunohost (4.0.6) stable; urgency=low
- [mod] Add apt conf regen hook to manage sury pinning policy (#1041)
- [fix] Use proper templating + handle xmpp-upload.domain.tld in dnsmasq conf (bc7344b6, 503e08b5)
- [fix] Explicitly require php-fpm >= 7.3 ... (41813744)
- [i18n] Translations updated for Catalan, French, German
Thanks to all contributors <3 ! (Christian W., Titus PiJean, xaloc33)
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 31 Aug 2020 19:57:24 +0200
yunohost (4.0.5) testing; urgency=low
- [enh] Update postfix, dovecot, nginx configuration according to Mozilla guidelines (Buster + DH params) (f3a4334a, 89bcf1ba, 2d661737)
- [enh] Update acme_tiny to 4.1.0 (#1037)
- [fix] ref to variable in i18n string (c.f. issue 1647) (7b1f02e0)
- [fix] Recursively enforce ownership for rspamd (8454f2ec)
- [fix] Stupid encoding issue when fetching service description (6ec0e7b6)
- [fix] Misc fixes for CI (ca0a42f2, 485c65a9, #1038, a891d20a)
Thanks to all contributors <3 ! (Eric G., Kay0u)
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 25 Aug 2020 19:32:27 +0200
yunohost (4.0.4) stable; urgency=low
- Debugging and robustness improvements for postgresql 9.6 -> 11 and xtables->nftables migrations (accc2da4, 59bd7d66, 4cb6f7fd, 4b14402c)
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 12 Aug 2020 18:14:00 +0200
yunohost (4.0.3) stable; urgency=low
- Bump version number for stable release
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 29 Jul 2020 17:00:00 +0200
yunohost (4.0.2~beta) testing; urgency=low
- [mod] Rebase on stretch-unstable to include recent changes
- [fix] Create admin's home during postinstall (#1021)
Thanks to all contributors <3 ! (Kay0u)
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 19 Jun 2020 15:16:26 +0200
yunohost (4.0.1~alpha) testing; urgency=low
- [fix] It just make no sense to backup/restore the mysql password... (#911)
- [fix] Fix getopts and helpers (#885, #886)
- [fix] Explicitly create home using mkhomedir_helper instead of obscure pam rule that doesn't work anymore (b67ff314)
- [fix] Ldap interface seems to expect lists everywhere now? (fb8c2b7b)
- [deb] Clean control file, remove some legacy Conflicts and Replaces (ca0d4933)
- [deb] Add conflicts with versions from backports for critical dependencies (#967)
- [cleanup] Stale / legacy code (217aaa36, d77da6a0, af047468, 82d468a3)
- [conf] Automatically disable/stop systemd-resolved that conflicts with dnsmasq on fresh setups ... (e7214b37)
- [conf] Remove deprecated option in sshd conf, c.f. https://patchwork.openembedded.org/patch/139981/ (2723d245)
- [conf] Small tweak in dovecot conf (deprecated settings) (dc0481e2)
- [conf] Update nslcd and nsswitch stuff using new Buster's default configs + get rid of nslcd service, only keep the regen-conf part (6ef3520f)
- [php] Migrate from php7.0 to php7.3 (3374e653, 9be10506, dd9564d3, 9679c291, 212a15e4, 25fcaa19, c4ad66f5)
- [psql] Migrate from psql 11 to 9.6 (e88aed72, 4920d4f9, c70b0ae4)
- [firewall] Migrate from xtable to nftable (05fb58f2, 2c4a8b73, 625d5372)
- [slapd] Rework slapd regenconf to use new backend (#984)
Thanks to all contributors <3 ! (Étienne M., Josué, Kay0u)
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 05 Jun 2020 03:10:09 +0200
yunohost (3.8.5.5) stable; urgency=low
- [enh] Allow to extend the nginx default_server configuration (f1bfc521)
- [mod] Move redirect to /yunohost/admin to a separate nginx conf file to allow customizing it more easily (ac9182d6)
- [enh] Make sure to validate/upgrade that we don't have any active weak certificate used by nginx at the beginning of the buster migration, otherwise nginx will later miserably fail to start (d4358897)
- [fix] get_files_diff crashing if {orig,new}_file is None (7bfe564a)
- [enh] Remove some useless message about file that "wasn't deleted because it doesn't exist." (#1024)
- [mod] Remove useless robot protection code (#1026)
- [fix] Let's not redefine the value for the 'service' var ... (1a2f26dc)
- [fix] More general stretch->buster patching for sources.list (#1028)
- [mod] Tweak custom disclaimer about the migration still being a bit touchy in preparation for stable release (852dea07)
- [mod] Typo/wording in en.json (#1030)
- [i18n] Translations updated for Catalan, French, Italian, Occitan
Thanks to all contributors <3 ! (É. Gaspar, Kay0u, L. Noferini, ppr, Quentí, xaloc33)
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 27 Jul 2020 19:03:33 +0200
yunohost (3.8.5.4) testing; urgency=low
- [fix] Fix unscd version parsing *again*
- [fix] Enforce permissions on rspamd log directory
- [enh] Ignore stupid warnings about sudo-ldap that is already provided
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 21 Jun 2020 23:37:09 +0200
yunohost (3.8.5.3) testing; urgency=low
- [fix] Fix the fix about unscd downgrade :/
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 19 Jun 2020 18:50:58 +0200
yunohost (3.8.5.2) testing; urgency=low
- [fix] Small issue with unscd upgrade/downgrade ... new version ain't always 0.53.1, so find it using dirty scrapping
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 18 Jun 2020 16:19:35 +0200
yunohost (3.8.5.1) testing; urgency=low
- [fix] Update Stretch->Buster migration disclaimer to make it clear that this is alpha-stage
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 06 Jun 2020 03:30:00 +0200
yunohost (3.8.5) testing; urgency=low
- [enh] Add migration procedure for Stretch->Buster (a2b83c0f, a26411db, 9f1211e9, e544bf3e, a0511cca)
- [fix] Disable/skip ntp when inside a container (9d0c0924)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 06 Jun 2020 02:11:51 +0200
yunohost (3.8.4.9) stable; urgency=low
- [fix] Force lowercase on domain names (804f4b3e)
- [fix] Add dirmngr to Depends:, needed for apt-key / gpg (cd115ed8)
- [fix] Improve debugging when diagnosis ain't happy when renewing certs (0f0194be)
- [enh] Add yunohost version to logs metadata (d615546b)
- [enh] Alway filter irrelevant log lines when sharing it (38704cba, 51d53be5)
- [fix] Regen-conf outputing many 'forget-about-it' because of files flagged as to be removed (f4525488)
- [fix] postfix per-domain destination concurrency (#988)
- [fix] Call regenconf for ssh before the general regenconf during the postinstall to avoid an irrelevant warning (7805837b)
- [i18n] Translations updated for Catalan, French, German
Thanks to all contributors <3 ! (taziden, ljf, ppr, xaloc33, Yasss Gurl)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 18 Jun 2020 15:13:01 +0200
yunohost (3.8.4.8) stable; urgency=low
- [fix] Don't add unprotected_urls if it's already in skipped_urls (#1005)
- [enh] Add pre-defined DHE group and set up Nginx to use it (#1007)
- [fix] Make sure to propagate change in slapd systemd conf during initial install (2d42480f)
- [fix] More accurate grep to avoid mistakenly grepping commented lines... (2408a620)
- [enh] Update n to 6.5.1 (#1012)
- [fix] Set sury default pinning to 600 (653c5fde)
- [enh] Clean stale file/hashes in regen-conf (#1009)
- [fix] Weirdness in regen-conf mechanism for SSH conf (#1014)
Thanks to all contributors <3 ! (É. Gaspar, Josué, SohKa)
-- Alexandre Aubin <alex.aubin@mailoo.org> Sat, 06 Jun 2020 01:59:08 +0200
yunohost (3.8.4.7) stable; urgency=low
- [fix] Remove some remains of glances (17eec25e)
- [fix] Force external resolution for reverse DNS dig (852cd14c)
- [fix] Make sure mysql is an alias to mariadb (e24191ce, ca89607d)
- [fix] Path for ynh_add_fpm_config template in restore (#1001)
- [fix] Add -o Acquire::Retries=3 to fix some stupid network issues happening sometimes with apt (03432349)
- [fix] ynh_setup_source: Retry wget on non-critical failures to try to avoid tmp dns issues (3d66eaec)
- [fix] ynh_setup_source: Calling ynh_print_err in case of error didn't work, and we probably want a ynh_die here (55036fad)
- [i18n] Translations updated for Catalan, French, Italian, Occitan
Thanks to all contributors <3 ! (JimboJoe, Leandro N., ppr, Quentí, xaloc33, yalh76)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 04 Jun 2020 02:28:33 +0200
yunohost (3.8.4.6) stable; urgency=low
- [fix] Bump server_names_hash_bucket_size to 128 to avoid nginx exploding for stupid reasons (b3db4d92)
- [fix] More sensible strategy for sury pinning (#1006)
- [fix] Stop trying to fetch log categories that are not implemented yet T.T (77bd9ae3)
- [enh] Add logging and persistent as default config for new muc room (#1008)
- [tests] Moar tests for app args parsing (#1004)
Thanks to all contributors <3 ! (Gabriel, Kay0u, Bram)
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 28 May 2020 00:22:10 +0200
yunohost (3.8.4.5) stable; urgency=low
- [enh] Tell systemctl to stfu about creating symlinks when enabling/disabling services (6637c8a8)
- [enh] Add maindomain in diagnosis email subject (e30e25fa)
- [fix] Webpath should also be normalized for args_list, so that we can get rid of the 'malformed path' check of the CI... (58ce6e5e)
- [fix] Increase time window for auto diagnosis cron to avoid remote diagnosis server overload (dc221495)
- [fix] encoding bullshit (4c600125, 64596bc1)
- [fix] Typo in diagnosis message + fix FR translation report format of bad DNS conf (#1002, b8f8ea14)
- [fix] Flag old etckeeper.conf as 'should not exist' in regenconf (5a3b382f)
- [enh] Detect dyndns-domains managed by yunohost and advice to use yunohost dyndns update --force (8b169f13)
- [enh] Complain if apps savagely edit system configurations during install and upgrade (a23f02db)
- [i18n] Translations updated for Arabic, Catalan, French, German, Italian
- [tests] CI V2 : Rework CI workflow (#991)
Thanks to all contributors <3 ! (ButterflyOfFire, Kay0u, L. Noferini, rynas, V. Rubiolo, xaloc33, Yasss Gurl)
-- Alexandre Aubin <alex.aubin@mailoo.org> Tue, 26 May 2020 03:20:39 +0200
yunohost (3.8.4.4) stable; urgency=low yunohost (3.8.4.4) stable; urgency=low
- [fix] Crash when the services file is empty (85f1802) - [fix] Crash when the services file is empty (85f1802)
@ -33,8 +246,8 @@ yunohost (3.8.4.2) testing; urgency=low
- [fix] Diagnosis: Try to not have weird warnings if no diagnosis ran yet... (65c87d55) - [fix] Diagnosis: Try to not have weird warnings if no diagnosis ran yet... (65c87d55)
- [fix] Diagnosis: Change logic of --email to avoid sending empty mail if some issues are found but ignored (4cd4938e) - [fix] Diagnosis: Change logic of --email to avoid sending empty mail if some issues are found but ignored (4cd4938e)
- [enh] Diagnosis/services: Report the service status as warning/unknown if service type is oneshot and status exited (dd09758f, 1cd7ffea) - [enh] Diagnosis/services: Report the service status as warning/unknown if service type is oneshot and status exited (dd09758f, 1cd7ffea)
- [fix] Rework ynh_psql_test_if_first_run ([#993](https://github.com/yunohost/yunohost/pull/993)) - [fix] Rework ynh_psql_test_if_first_run (#993)
- [tests] Tests for args parsing ([#989](https://github.com/yunohost/yunohost/pull/989), 108a3ca4) - [tests] Tests for args parsing (#989, 108a3ca4)
Thanks to all contributors <3 ! (Bram, Kayou) Thanks to all contributors <3 ! (Bram, Kayou)
@ -1713,7 +1926,7 @@ yunohost (2.5.2) testing; urgency=low
Other fixes and improvements: Other fixes and improvements:
* [enh] remove timeout from cli interface * [enh] remove timeout from cli interface
* [fix] [#662](https://dev.yunohost.org/issues/662): missing 'python-openssl' dependency for Let's Encrypt integration. * [fix] #662: missing 'python-openssl' dependency for Let's Encrypt integration.
* [fix] --no-remove-on-failure for app install should behave as a flag. * [fix] --no-remove-on-failure for app install should behave as a flag.
* [fix] don't remove trailing char if it's not a slash * [fix] don't remove trailing char if it's not a slash

View file

@ -1,6 +0,0 @@
Name: Create home directory during login
Default: yes
Priority: 900
Session-Type: Additional
Session:
required pam_mkhomedir.so umask=0022 skel=/etc/skel

39
debian/control vendored
View file

@ -11,45 +11,42 @@ Package: yunohost
Essential: yes Essential: yes
Architecture: all Architecture: all
Depends: ${python:Depends}, ${misc:Depends} Depends: ${python:Depends}, ${misc:Depends}
, moulinette (>= 3.7), ssowat (>= 3.7) , moulinette (>= 4.1), ssowat (>= 4.0)
, python-psutil, python-requests, python-dnspython, python-openssl , python-psutil, python-requests, python-dnspython, python-openssl
, python-miniupnpc, python-dbus, python-jinja2 , python-miniupnpc, python-dbus, python-jinja2
, python-toml, python-packaging , python-toml, python-packaging, python-publicsuffix
, apt, apt-transport-https , apt, apt-transport-https, dirmngr
, nginx, nginx-extras (>=1.6.2) , php7.3-common, php7.3-fpm, php7.3-ldap, php7.3-intl
, php-fpm, php-ldap, php-intl , mariadb-server, php7.3-mysql
, mariadb-server, php-mysql | php-mysqlnd
, openssh-server, iptables, fail2ban, dnsutils, bind9utils , openssh-server, iptables, fail2ban, dnsutils, bind9utils
, openssl, ca-certificates, netcat-openbsd, iproute2 , openssl, ca-certificates, netcat-openbsd, iproute2
, slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd , slapd, ldap-utils, sudo-ldap, libnss-ldapd, unscd, libpam-ldapd
, dnsmasq, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname , dnsmasq, avahi-daemon, libnss-mdns, resolvconf, libnss-myhostname
, postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre , postfix, postfix-ldap, postfix-policyd-spf-perl, postfix-pcre
, dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam , dovecot-core, dovecot-ldap, dovecot-lmtpd, dovecot-managesieved, dovecot-antispam
, rspamd (>= 1.6.0), opendkim-tools, postsrsd, procmail, mailutils , rspamd, opendkim-tools, postsrsd, procmail, mailutils
, redis-server , redis-server
, metronome (>=3.14.0) , metronome (>=3.14.0)
, git, curl, wget, cron, unzip, jq , git, curl, wget, cron, unzip, jq, bc
, lsb-release, haveged, fake-hwclock, equivs, lsof, whois, python-publicsuffix , lsb-release, haveged, fake-hwclock, equivs, lsof, whois
Recommends: yunohost-admin Recommends: yunohost-admin
, ntp, inetutils-ping | iputils-ping , ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog , bash-completion, rsyslog
, php-gd, php-curl, php-gettext, php-mcrypt , php7.3-gd, php7.3-curl, php-gettext
, python-pip , python-pip
, unattended-upgrades , unattended-upgrades
, libdbd-ldap-perl, libnet-dns-perl , libdbd-ldap-perl, libnet-dns-perl
Suggests: htop, vim, rsync, acpi-support-base, udisks2 Suggests: htop, vim, rsync, acpi-support-base, udisks2
Conflicts: iptables-persistent Conflicts: iptables-persistent
, moulinette-yunohost, yunohost-config , apache2
, yunohost-config-others, yunohost-config-postfix , bind9
, yunohost-config-dovecot, yunohost-config-slapd , nginx-extras (>= 1.16)
, yunohost-config-nginx, yunohost-config-amavis , openssl (>= 1.1.1g)
, yunohost-config-mysql, yunohost-predepends , slapd (>= 2.4.49)
, apache2, bind9 , dovecot-core (>= 1:2.3.7)
Replaces: moulinette-yunohost, yunohost-config , redis-server (>= 5:5.0.7)
, yunohost-config-others, yunohost-config-postfix , fail2ban (>= 0.11)
, yunohost-config-dovecot, yunohost-config-slapd , iptables (>= 1.8.3)
, yunohost-config-nginx, yunohost-config-amavis
, yunohost-config-mysql, yunohost-predepends
Description: manageable and configured self-hosting server Description: manageable and configured self-hosting server
YunoHost aims to make self-hosting accessible to everyone. It configures YunoHost aims to make self-hosting accessible to everyone. It configures
an email, Web and IM server alongside a LDAP base. It also provides an email, Web and IM server alongside a LDAP base. It also provides

2
debian/install vendored
View file

@ -8,11 +8,11 @@ data/other/yunoprompt.service /etc/systemd/system/
data/other/password/* /usr/share/yunohost/other/password/ data/other/password/* /usr/share/yunohost/other/password/
data/other/dpkg-origins/yunohost /etc/dpkg/origins data/other/dpkg-origins/yunohost /etc/dpkg/origins
data/other/dnsbl_list.yml /usr/share/yunohost/other/ data/other/dnsbl_list.yml /usr/share/yunohost/other/
data/other/ffdhe2048.pem /usr/share/yunohost/other/
data/other/* /usr/share/yunohost/yunohost-config/moulinette/ data/other/* /usr/share/yunohost/yunohost-config/moulinette/
data/templates/* /usr/share/yunohost/templates/ data/templates/* /usr/share/yunohost/templates/
data/helpers /usr/share/yunohost/ data/helpers /usr/share/yunohost/
data/helpers.d/* /usr/share/yunohost/helpers.d/ data/helpers.d/* /usr/share/yunohost/helpers.d/
debian/conf/pam/* /usr/share/pam-configs/
lib/metronome/modules/* /usr/lib/metronome/modules/ lib/metronome/modules/* /usr/lib/metronome/modules/
locales/* /usr/lib/moulinette/yunohost/locales/ locales/* /usr/lib/moulinette/yunohost/locales/
src/yunohost /usr/lib/moulinette src/yunohost /usr/lib/moulinette

5
debian/postinst vendored
View file

@ -29,11 +29,6 @@ do_configure() {
# Yunoprompt # Yunoprompt
systemctl enable yunoprompt.service systemctl enable yunoprompt.service
# remove old PAM config and update it
[[ ! -f /usr/share/pam-configs/my_mkhomedir ]] \
|| rm /usr/share/pam-configs/my_mkhomedir
pam-auth-update --package
} }
# summary of how this script can be called: # summary of how this script can be called:

View file

@ -4,6 +4,7 @@ import os
import glob import glob
import datetime import datetime
def render(helpers): def render(helpers):
data = {"helpers": helpers, data = {"helpers": helpers,
@ -29,6 +30,7 @@ def render(helpers):
############################################################################## ##############################################################################
class Parser(): class Parser():
def __init__(self, filename): def __init__(self, filename):
@ -180,13 +182,14 @@ class Parser():
b["usage"] = b["usage"].strip() b["usage"] = b["usage"].strip()
def is_global_comment(line): def is_global_comment(line):
return line.startswith('#') return line.startswith('#')
def malformed_error(line_number): def malformed_error(line_number):
return "Malformed file line {} ?".format(line_number) return "Malformed file line {} ?".format(line_number)
def main(): def main():
helper_files = sorted(glob.glob("../data/helpers.d/*")) helper_files = sorted(glob.glob("../data/helpers.d/*"))
@ -204,5 +207,5 @@ def main():
render(helpers) render(helpers)
main()
main()

View file

@ -164,7 +164,7 @@
"diagnosis_basesystem_kernel": "هذا الخادم يُشغّل نواة لينكس {kernel_version}", "diagnosis_basesystem_kernel": "هذا الخادم يُشغّل نواة لينكس {kernel_version}",
"diagnosis_basesystem_ynh_single_version": "{package} الإصدار: {version} ({repo})", "diagnosis_basesystem_ynh_single_version": "{package} الإصدار: {version} ({repo})",
"diagnosis_basesystem_ynh_main_version": "هذا الخادم يُشغّل YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_main_version": "هذا الخادم يُشغّل YunoHost {main_version} ({repo})",
"diagnosis_everything_ok": "كل شيء على ما يرام في {category}!", "diagnosis_everything_ok": "كل شيء يبدو على ما يرام في {category}!",
"diagnosis_ip_connected_ipv4": "الخادم مُتّصل بالإنترنت عبر IPv4!", "diagnosis_ip_connected_ipv4": "الخادم مُتّصل بالإنترنت عبر IPv4!",
"diagnosis_ip_connected_ipv6": "الخادم مُتّصل بالإنترنت عبر IPv6!", "diagnosis_ip_connected_ipv6": "الخادم مُتّصل بالإنترنت عبر IPv6!",
"diagnosis_ip_not_connected_at_all": "يبدو أنّ الخادم غير مُتّصل بتاتا بالإنترنت!؟", "diagnosis_ip_not_connected_at_all": "يبدو أنّ الخادم غير مُتّصل بتاتا بالإنترنت!؟",
@ -172,5 +172,16 @@
"apps_already_up_to_date": "كافة التطبيقات مُحدّثة", "apps_already_up_to_date": "كافة التطبيقات مُحدّثة",
"app_remove_after_failed_install": "جارٍ حذف التطبيق بعدما فشل تنصيبها…", "app_remove_after_failed_install": "جارٍ حذف التطبيق بعدما فشل تنصيبها…",
"apps_catalog_updating": "جارٍ تحديث فهرس التطبيقات…", "apps_catalog_updating": "جارٍ تحديث فهرس التطبيقات…",
"apps_catalog_update_success": "تم تحديث فهرس التطبيقات!" "apps_catalog_update_success": "تم تحديث فهرس التطبيقات!",
"diagnosis_domain_expiration_error": "ستنتهي مدة صلاحية بعض النطاقات في القريب العاجل!",
"diagnosis_domain_expiration_warning": "ستنتهي مدة صلاحية بعض النطاقات قريبًا!",
"diagnosis_ports_could_not_diagnose_details": "خطأ: {error}",
"diagnosis_description_regenconf": "إعدادات النظام",
"diagnosis_description_mail": "البريد الإلكتروني",
"diagnosis_description_web": "الويب",
"diagnosis_description_systemresources": "موارد النظام",
"diagnosis_description_services": "حالة الخدمات",
"diagnosis_description_dnsrecords": "تسجيلات خدمة DNS",
"diagnosis_description_ip": "الإتصال بالإنترنت",
"diagnosis_description_basesystem": "النظام الأساسي"
} }

View file

@ -20,15 +20,15 @@
"app_location_unavailable": "Aquesta URL no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}", "app_location_unavailable": "Aquesta URL no està disponible o entra en conflicte amb aplicacions ja instal·lades:\n{apps:s}",
"app_manifest_invalid": "Hi ha algun error amb el manifest de l'aplicació: {error}", "app_manifest_invalid": "Hi ha algun error amb el manifest de l'aplicació: {error}",
"app_not_correctly_installed": "{app:s} sembla estar mal instal·lada", "app_not_correctly_installed": "{app:s} sembla estar mal instal·lada",
"app_not_installed": "No s'ha trobat l'aplicació «{app:s}» en la llista d'aplicacions instal·lades: {all_apps}", "app_not_installed": "No s'ha trobat {app:s} en la llista d'aplicacions instal·lades: {all_apps}",
"app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament", "app_not_properly_removed": "{app:s} no s'ha pogut suprimir correctament",
"app_removed": "{app:s} ha estat suprimida", "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_unmeet": "No es compleixen els requeriments per {app}, el paquet {pkgname} ({version}) ha de ser {spec}", "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, l'URL és correcta?", "app_sources_fetch_failed": "No s'han pogut carregar els fitxers font, l'URL és correcta?",
"app_unknown": "Aplicació desconeguda", "app_unknown": "Aplicació desconeguda",
"app_unsupported_remote_type": "El tipus remot utilitzat per l'aplicació no està suportat", "app_unsupported_remote_type": "El tipus remot utilitzat per l'aplicació no està suportat",
"app_upgrade_app_name": "Actualitzant {app}", "app_upgrade_app_name": "Actualitzant {app}...",
"app_upgrade_failed": "No s'ha pogut actualitzar {app:s}: {error}", "app_upgrade_failed": "No s'ha pogut actualitzar {app:s}: {error}",
"app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions", "app_upgrade_some_app_failed": "No s'han pogut actualitzar algunes aplicacions",
"app_upgraded": "S'ha actualitzat {app:s}", "app_upgraded": "S'ha actualitzat {app:s}",
@ -39,12 +39,12 @@
"ask_new_admin_password": "Nova contrasenya d'administrador", "ask_new_admin_password": "Nova contrasenya d'administrador",
"ask_password": "Contrasenya", "ask_password": "Contrasenya",
"backup_abstract_method": "Encara està per implementar aquest mètode de còpia de seguretat", "backup_abstract_method": "Encara està per implementar aquest mètode de còpia de seguretat",
"backup_app_failed": "No s'ha pogut fer la còpia de seguretat de l'aplicació \"{app:s}\"", "backup_app_failed": "No s'ha pogut fer la còpia de seguretat de {app:s}",
"backup_applying_method_borg": "Enviant tots els fitxers de la còpia de seguretat al repositori borg-backup", "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_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_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_tar": "Creació de l'arxiu TAR de la còpia de seguretat...",
"backup_archive_app_not_found": "No s'ha pogut trobar l'aplicació «{app:s}» dins l'arxiu de la còpia de seguretat", "backup_archive_app_not_found": "No s'ha pogut trobar {app:s} en 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_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_name_exists": "Ja hi ha una còpia de seguretat amb aquest nom.", "backup_archive_name_exists": "Ja hi ha una còpia de seguretat amb aquest nom.",
"backup_archive_name_unknown": "Còpia de seguretat local \"{name:s}\" desconeguda", "backup_archive_name_unknown": "Còpia de seguretat local \"{name:s}\" desconeguda",
@ -60,14 +60,14 @@
"backup_created": "S'ha creat la còpia de seguretat", "backup_created": "S'ha creat la còpia de seguretat",
"aborting": "Avortant.", "aborting": "Avortant.",
"app_not_upgraded": "L'aplicació «{failed_app}» no s'ha pogut actualitzar, i com a conseqüència s'ha cancel·lat l'actualització de les següents aplicacions: {apps}", "app_not_upgraded": "L'aplicació «{failed_app}» no s'ha pogut actualitzar, i com a conseqüència s'ha cancel·lat l'actualització de les següents aplicacions: {apps}",
"app_start_install": "instal·lant l'aplicació «{app}»…", "app_start_install": "instal·lant {app}...",
"app_start_remove": "Eliminant l'aplicació «{app}»…", "app_start_remove": "Eliminant {app}...",
"app_start_backup": "Recuperant els fitxers pels que s'ha de fer una còpia de seguretat per «{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_start_restore": "Recuperant {app}...",
"app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}", "app_upgrade_several_apps": "S'actualitzaran les següents aplicacions: {apps}",
"ask_new_domain": "Nou domini", "ask_new_domain": "Nou domini",
"ask_new_path": "Nou camí", "ask_new_path": "Nou camí",
"backup_actually_backuping": "Creant un arxiu de còpia de seguretat a partir dels fitxers recuperats", "backup_actually_backuping": "Creant un arxiu de còpia de seguretat a partir dels fitxers recuperats...",
"backup_creation_failed": "No s'ha pogut crear l'arxiu de la còpia de seguretat", "backup_creation_failed": "No s'ha pogut crear l'arxiu 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_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 la restauració", "backup_csv_creation_failed": "No s'ha pogut crear el fitxer CSV necessari per a la restauració",
@ -81,7 +81,7 @@
"backup_method_copy_finished": "La còpia de la còpia de seguretat 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_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_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ó", "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).", "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_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_1": "La contrasenya ha de tenir un mínim de 8 caràcters",
@ -95,11 +95,11 @@
"backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat", "backup_output_directory_required": "Heu d'especificar un directori de sortida per la còpia de seguretat",
"backup_output_symlink_dir_broken": "El directori del arxiu «{path:s}» es un enllaç simbòlic trencat. Pot ser heu oblidat muntar, tornar a muntar o connectar el mitja d'emmagatzematge al que apunta.", "backup_output_symlink_dir_broken": "El directori del arxiu «{path:s}» es un enllaç simbòlic trencat. Pot ser heu oblidat muntar, tornar a muntar o connectar el mitja d'emmagatzematge al que apunta.",
"backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar PHP 7, pot ser que no es puguin restaurar les vostres aplicacions PHP (raó: {error:s})", "backup_php5_to_php7_migration_may_fail": "No s'ha pogut convertir l'arxiu per suportar PHP 7, pot ser que no es puguin restaurar les vostres aplicacions PHP (raó: {error:s})",
"backup_running_hooks": "Executant els scripts de la còpia de seguretat", "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_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'ha pogut utilitzar el mètode ràpid per organitzar els fitxers dins de l'arxiu", "backup_unable_to_organize_files": "No s'ha pogut utilitzar el mètode ràpid per organitzar els fitxers dins de l'arxiu",
"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_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ó.", "backup_with_no_restore_script_for_app": "{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": "No s'ha pogut executar el ACME challenge pel domini {domain} en aquests moments ja que a la seva configuració de nginx li manca el codi corresponent… Assegureu-vos que la configuració nginx està actualitzada utilitzant «yunohost tools regen-conf nginx --dry-run --with-diff».", "certmanager_acme_not_configured_for_domain": "No s'ha pogut executar el ACME challenge pel domini {domain} en aquests moments ja que a la seva configuració de nginx li manca el codi corresponent… Assegureu-vos que la configuració nginx està actualitzada utilitzant «yunohost tools regen-conf nginx --dry-run --with-diff».",
"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_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 si sabeu el que esteu fent)", "certmanager_attempt_to_renew_valid_cert": "El certificat pel domini «{domain:s}» està a punt de caducar! (Utilitzeu --force si sabeu el que esteu fent)",
@ -109,7 +109,7 @@
"certmanager_cert_install_success_selfsigned": "S'ha instal·lat correctament un certificat auto-signat 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_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_cert_signing_failed": "No s'ha pogut firmar el nou certificat",
"certmanager_certificate_fetching_or_enabling_failed": "Sembla que utilitzar el nou certificat per {domain:s} ha fallat", "certmanager_certificate_fetching_or_enabling_failed": "Sembla que utilitzar el 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_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_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_cert_not_selfsigned": "El certificat pel domini {domain:s} no és auto-signat Esteu segur de voler canviar-lo? (Utilitzeu «--force» per fer-ho)",
@ -127,7 +127,7 @@
"confirm_app_install_thirdparty": "PERILL! Aquesta aplicació no es part del catàleg d'aplicacions de YunoHost. La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema… Si accepteu el risc, escriviu «{answers:s}»", "confirm_app_install_thirdparty": "PERILL! Aquesta aplicació no es part del catàleg d'aplicacions de YunoHost. La instal·lació d'aplicacions de terceres parts pot comprometre la integritat i seguretat del seu sistema. No hauríeu d'instal·lar-ne a no ser que sapigueu el que feu. No obtindreu CAP AJUDA si l'aplicació no funciona o trenca el sistema… Si accepteu el risc, escriviu «{answers:s}»",
"custom_app_url_required": "Heu de especificar una URL per actualitzar la vostra aplicació personalitzada {app:s}", "custom_app_url_required": "Heu de especificar una URL per actualitzar la vostra aplicació personalitzada {app:s}",
"admin_password_too_long": "Trieu una contrasenya de menys de 127 caràcters", "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».", "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 apt install --fix-broken» i/o «sudo dpkg --configure -a».",
"domain_cannot_remove_main": "No es pot eliminar «{domain:s}» ja que és el domini principal, primer s'ha d'establir un nou domini principal utilitzant «yunohost domain main-domain -n <un-altre-domini>»; aquí hi ha una llista dels possibles dominis: {other_domains:s}", "domain_cannot_remove_main": "No es pot eliminar «{domain:s}» ja que és el domini principal, primer s'ha d'establir un nou domini principal utilitzant «yunohost domain main-domain -n <un-altre-domini>»; aquí hi ha una llista dels possibles dominis: {other_domains:s}",
"domain_cert_gen_failed": "No s'ha pogut generar el certificat", "domain_cert_gen_failed": "No s'ha pogut generar el certificat",
"domain_created": "S'ha creat el domini", "domain_created": "S'ha creat el domini",
@ -149,16 +149,16 @@
"dyndns_could_not_check_available": "No s'ha pogut verificar la disponibilitat de {domain:s} a {provider: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_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_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_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_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_no_domain_registered": "No hi ha cap domini registrat amb DynDNS",
"dyndns_registered": "S'ha registrat el domini DynDNS", "dyndns_registered": "S'ha registrat el domini DynDNS",
"dyndns_registration_failed": "No s'ha pogut registrar el domini DynDNS: {error:s}", "dyndns_registration_failed": "No s'ha pogut registrar el domini DynDNS: {error:s}",
"dyndns_domain_not_provided": "El proveïdor de DynDNS {provider:s} no pot oferir el domini {domain:s}.", "dyndns_domain_not_provided": "El proveïdor de DynDNS {provider:s} no pot oferir el domini {domain:s}.",
"dyndns_unavailable": "El domini {domain:s} no està disponible.", "dyndns_unavailable": "El domini {domain:s} no està disponible.",
"executing_command": "Execució de l'ordre « {command:s} »", "executing_command": "Execució de l'ordre « {command:s} »...",
"executing_script": "Execució de l'script « {script:s} »", "executing_script": "Execució de l'script « {script:s} »...",
"extracting": "Extracció en curs", "extracting": "Extracció en curs...",
"dyndns_cron_installed": "S'ha creat la tasca cron pel DynDNS", "dyndns_cron_installed": "S'ha creat la tasca cron pel DynDNS",
"dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron per a DynDNS: {error}", "dyndns_cron_remove_failed": "No s'ha pogut eliminar la tasca cron per a DynDNS: {error}",
"dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS", "dyndns_cron_removed": "S'ha eliminat la tasca cron pel DynDNS",
@ -288,10 +288,10 @@
"migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració… (?) Ometent.", "migration_0009_not_needed": "Sembla que ja s'ha fet aquesta migració… (?) Ometent.",
"migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí «%s»", "migrations_cant_reach_migration_file": "No s'ha pogut accedir als fitxers de migració al camí «%s»",
"migrations_list_conflict_pending_done": "No es pot utilitzar «--previous» i «--done» al mateix temps.", "migrations_list_conflict_pending_done": "No es pot utilitzar «--previous» i «--done» al mateix temps.",
"migrations_loading_migration": "Carregant la migració {id}", "migrations_loading_migration": "Carregant la migració {id}...",
"migrations_migration_has_failed": "La migració {id} ha fallat, cancel·lant. Error: {exception}", "migrations_migration_has_failed": "La migració {id} ha fallat, cancel·lant. Error: {exception}",
"migrations_no_migrations_to_run": "No hi ha cap migració a fer", "migrations_no_migrations_to_run": "No hi ha cap migració a fer",
"migrations_skip_migration": "Saltant migració {id}", "migrations_skip_migration": "Saltant migració {id}...",
"migrations_to_be_ran_manually": "La migració {id} s'ha de fer manualment. Aneu a Eines → Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».", "migrations_to_be_ran_manually": "La migració {id} s'ha de fer manualment. Aneu a Eines → Migracions a la interfície admin, o executeu «yunohost tools migrations migrate».",
"migrations_need_to_accept_disclaimer": "Per fer la migració {id}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció «--accept-disclaimer».", "migrations_need_to_accept_disclaimer": "Per fer la migració {id}, heu d'acceptar aquesta clàusula de no responsabilitat:\n---\n{disclaimer}\n---\nSi accepteu fer la migració, torneu a executar l'ordre amb l'opció «--accept-disclaimer».",
"no_internet_connection": "El servidor no està connectat a Internet", "no_internet_connection": "El servidor no està connectat a Internet",
@ -325,9 +325,9 @@
"regenconf_would_be_updated": "La configuració hagués estat actualitzada per la categoria «{category}»", "regenconf_would_be_updated": "La configuració hagués estat actualitzada per la categoria «{category}»",
"regenconf_dry_pending_applying": "Verificació de la configuració pendent que s'hauria d'haver aplicat per la categoria «{category}»…", "regenconf_dry_pending_applying": "Verificació de la configuració pendent que s'hauria d'haver aplicat per la categoria «{category}»…",
"regenconf_failed": "No s'ha pogut regenerar la configuració per la/les categoria/es : {categories}", "regenconf_failed": "No s'ha pogut regenerar la configuració per la/les categoria/es : {categories}",
"regenconf_pending_applying": "Aplicació de la configuració pendent per la categoria «{category}»", "regenconf_pending_applying": "Aplicació de la configuració pendent per la categoria «{category}»...",
"restore_already_installed_app": "Una aplicació amb la ID «{app:s}» ja està instal·lada", "restore_already_installed_app": "Una aplicació amb la ID «{app:s}» ja està instal·lada",
"restore_app_failed": "No s'ha pogut restaurar l'aplicació «{app:s}»", "restore_app_failed": "No s'ha pogut restaurar {app:s}",
"restore_cleaning_failed": "No s'ha pogut netejar el directori temporal de restauració", "restore_cleaning_failed": "No s'ha pogut netejar el directori temporal de restauració",
"restore_complete": "Restauració completada", "restore_complete": "Restauració completada",
"restore_confirm_yunohost_installed": "Esteu segur de voler restaurar un sistema ja instal·lat? [{answers:s}]", "restore_confirm_yunohost_installed": "Esteu segur de voler restaurar un sistema ja instal·lat? [{answers:s}]",
@ -390,7 +390,7 @@
"ssowat_conf_updated": "S'ha actualitzat la configuració SSOwat", "ssowat_conf_updated": "S'ha actualitzat la configuració SSOwat",
"system_upgraded": "S'ha actualitzat el sistema", "system_upgraded": "S'ha actualitzat el sistema",
"system_username_exists": "El nom d'usuari ja existeix en la llista d'usuaris de sistema", "system_username_exists": "El nom d'usuari ja existeix en la llista d'usuaris de sistema",
"this_action_broke_dpkg": "Aquesta acció a trencat dpkg/APT (els gestors de paquets del sistema) Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo dpkg --configure -a».", "this_action_broke_dpkg": "Aquesta acció a trencat dpkg/APT (els gestors de paquets del sistema)... Podeu intentar resoldre el problema connectant-vos amb SSH i executant «sudo apt install --fix-broken» i/o «sudo dpkg --configure -a».",
"tools_upgrade_at_least_one": "Especifiqueu «--apps», o «--system»", "tools_upgrade_at_least_one": "Especifiqueu «--apps», o «--system»",
"tools_upgrade_cant_both": "No es poden actualitzar tant el sistema com les aplicacions al mateix temps", "tools_upgrade_cant_both": "No es poden actualitzar tant el sistema com les aplicacions al mateix temps",
"tools_upgrade_cant_hold_critical_packages": "No es poden mantenir els paquets crítics…", "tools_upgrade_cant_hold_critical_packages": "No es poden mantenir els paquets crítics…",
@ -400,15 +400,15 @@
"tools_upgrade_special_packages": "Actualitzant els paquets «especials» (relacionats amb YunoHost)…", "tools_upgrade_special_packages": "Actualitzant els paquets «especials» (relacionats amb YunoHost)…",
"tools_upgrade_special_packages_explanation": "Aquesta actualització especial continuarà en segon pla. No comenceu cap altra acció al servidor en els pròxims ~10 minuts (depèn de la velocitat del maquinari). Després d'això, pot ser que us hagueu de tornar a connectar a la interfície d'administració. Els registres de l'actualització estaran disponibles a Eines → Registres (a la interfície d'administració) o utilitzant «yunohost log list» (des de la línia d'ordres).", "tools_upgrade_special_packages_explanation": "Aquesta actualització especial continuarà en segon pla. No comenceu cap altra acció al servidor en els pròxims ~10 minuts (depèn de la velocitat del maquinari). Després d'això, pot ser que us hagueu de tornar a connectar a la interfície d'administració. Els registres de l'actualització estaran disponibles a Eines → Registres (a la interfície d'administració) o utilitzant «yunohost log list» (des de la línia d'ordres).",
"tools_upgrade_special_packages_completed": "Actualització dels paquets YunoHost acabada.\nPremeu [Enter] per tornar a la línia d'ordres", "tools_upgrade_special_packages_completed": "Actualització dels paquets YunoHost acabada.\nPremeu [Enter] per tornar a la línia d'ordres",
"unbackup_app": "L'aplicació «{app:s}» no serà guardada", "unbackup_app": "{app:s} no es guardarà",
"unexpected_error": "Hi ha hagut un error inesperat: {error}", "unexpected_error": "Hi ha hagut un error inesperat: {error}",
"unlimit": "Sense quota", "unlimit": "Sense quota",
"unrestore_app": "L'aplicació «{app:s} no serà restaurada", "unrestore_app": "{app:s} no es restaurarà",
"update_apt_cache_failed": "No s'ha pogut actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list, que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", "update_apt_cache_failed": "No s'ha pogut actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list, que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}",
"update_apt_cache_warning": "Hi ha hagut errors al actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}", "update_apt_cache_warning": "Hi ha hagut errors al actualitzar la memòria cau d'APT (el gestor de paquets de Debian). Aquí teniu les línies de sources.list que poden ajudar-vos a identificar les línies problemàtiques:\n{sourceslist}",
"updating_apt_cache": "Obtenció de les actualitzacions disponibles per als paquets del sistema", "updating_apt_cache": "Obtenció de les actualitzacions disponibles per als paquets del sistema...",
"upgrade_complete": "Actualització acabada", "upgrade_complete": "Actualització acabada",
"upgrading_packages": "Actualitzant els paquets", "upgrading_packages": "Actualitzant els paquets...",
"upnp_dev_not_found": "No s'ha trobat cap dispositiu UPnP", "upnp_dev_not_found": "No s'ha trobat cap dispositiu UPnP",
"upnp_disabled": "S'ha desactivat UPnP", "upnp_disabled": "S'ha desactivat UPnP",
"upnp_enabled": "S'ha activat UPnP", "upnp_enabled": "S'ha activat UPnP",
@ -426,9 +426,9 @@
"yunohost_ca_creation_failed": "No s'ha pogut crear l'autoritat de certificació", "yunohost_ca_creation_failed": "No s'ha pogut crear l'autoritat de certificació",
"yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.", "yunohost_ca_creation_success": "S'ha creat l'autoritat de certificació local.",
"yunohost_configured": "YunoHost està configurat", "yunohost_configured": "YunoHost està configurat",
"yunohost_installing": "Instal·lació de YunoHost", "yunohost_installing": "Instal·lació de YunoHost...",
"yunohost_not_installed": "YunoHost no està instal·lat correctament. Executeu «yunohost tools postinstall»", "yunohost_not_installed": "YunoHost no està instal·lat correctament. Executeu «yunohost tools postinstall»",
"backup_permission": "Permís de còpia de seguretat per l'aplicació {app:s}", "backup_permission": "Permís de còpia de seguretat per {app:s}",
"group_created": "S'ha creat el grup «{group}»", "group_created": "S'ha creat el grup «{group}»",
"group_creation_failed": "No s'ha pogut crear el grup «{group}»: {error}", "group_creation_failed": "No s'ha pogut crear el grup «{group}»: {error}",
"group_deleted": "S'ha eliminat el grup «{group}»", "group_deleted": "S'ha eliminat el grup «{group}»",
@ -445,11 +445,11 @@
"migration_0011_create_group": "Creant un grup per a cada usuari…", "migration_0011_create_group": "Creant un grup per a cada usuari…",
"migration_0011_done": "Migració completada. Ja podeu gestionar grups d'usuaris.", "migration_0011_done": "Migració completada. Ja podeu gestionar grups d'usuaris.",
"migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}", "migration_0011_LDAP_update_failed": "Ha fallat l'actualització de LDAP. Error: {error:s}",
"migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP", "migration_0011_migrate_permission": "Fent la migració dels permisos de la configuració de les aplicacions a LDAP...",
"migration_0011_migration_failed_trying_to_rollback": "No s'ha pogut fer la migració… s'intenta tornar el sistema a l'estat anterior.", "migration_0011_migration_failed_trying_to_rollback": "No s'ha pogut fer la migració… s'intenta tornar el sistema a l'estat anterior.",
"migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.", "migration_0011_rollback_success": "S'ha tornat el sistema a l'estat anterior.",
"migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP", "migration_0011_update_LDAP_database": "Actualitzant la base de dades LDAP...",
"migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP", "migration_0011_update_LDAP_schema": "Actualitzant l'esquema LDAP...",
"permission_already_exist": "El permís «{permission:s}» ja existeix", "permission_already_exist": "El permís «{permission:s}» ja existeix",
"permission_created": "S'ha creat el permís «{permission:s}»", "permission_created": "S'ha creat el permís «{permission:s}»",
"permission_creation_failed": "No s'ha pogut crear el permís «{permission}»: {error}", "permission_creation_failed": "No s'ha pogut crear el permís «{permission}»: {error}",
@ -474,7 +474,7 @@
"migrations_must_provide_explicit_targets": "Heu de proporcionar objectius explícits al utilitzar «--skip» o «--force-rerun»", "migrations_must_provide_explicit_targets": "Heu de proporcionar objectius explícits al utilitzar «--skip» o «--force-rerun»",
"migrations_no_such_migration": "No hi ha cap migració anomenada «{id}»", "migrations_no_such_migration": "No hi ha cap migració anomenada «{id}»",
"migrations_pending_cant_rerun": "Aquestes migracions encara estan pendents, així que no es poden tornar a executar: {ids}", "migrations_pending_cant_rerun": "Aquestes migracions encara estan pendents, així que no es poden tornar a executar: {ids}",
"migrations_running_forward": "Executant la migració {id}", "migrations_running_forward": "Executant la migració {id}...",
"migrations_success_forward": "Migració {id} completada", "migrations_success_forward": "Migració {id} completada",
"apps_already_up_to_date": "Ja estan actualitzades totes les aplicacions", "apps_already_up_to_date": "Ja estan actualitzades totes les aplicacions",
"dyndns_provider_unreachable": "No s'ha pogut connectar amb el proveïdor DynDNS {provider}: o el vostre YunoHost no està ben connectat a Internet o el servidor dynette està caigut.", "dyndns_provider_unreachable": "No s'ha pogut connectar amb el proveïdor DynDNS {provider}: o el vostre YunoHost no està ben connectat a Internet o el servidor dynette està caigut.",
@ -500,7 +500,7 @@
"permission_already_up_to_date": "No s'ha actualitzat el permís perquè la petició d'afegir/eliminar ja corresponent a l'estat actual.", "permission_already_up_to_date": "No s'ha actualitzat el permís perquè la petició d'afegir/eliminar ja corresponent a l'estat actual.",
"permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït.", "permission_currently_allowed_for_all_users": "El permís ha el té el grup de tots els usuaris (all_users) a més d'altres grups. Segurament s'hauria de revocar el permís a «all_users» o eliminar els altres grups als que s'ha atribuït.",
"permission_require_account": "El permís {permission} només té sentit per als usuaris que tenen un compte, i per tant no es pot activar per als visitants.", "permission_require_account": "El permís {permission} només té sentit per als usuaris que tenen un compte, i per tant no es pot activar per als visitants.",
"app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació", "app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació...",
"diagnosis_basesystem_ynh_main_version": "El servidor funciona amb YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_main_version": "El servidor funciona amb YunoHost {main_version} ({repo})",
"diagnosis_ram_low": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}. Aneu amb compte.", "diagnosis_ram_low": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}. Aneu amb compte.",
"diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de {recommended} de swap per evitar situacions en les que el sistema es queda sense memòria.", "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de {recommended} de swap per evitar situacions en les que el sistema es queda sense memòria.",
@ -537,7 +537,7 @@
"diagnosis_dns_good_conf": "Els registres DNS han estat correctament configurats pel domini {domain} (categoria {category})", "diagnosis_dns_good_conf": "Els registres DNS han estat correctament configurats pel domini {domain} (categoria {category})",
"diagnosis_dns_bad_conf": "Alguns registres DNS són incorrectes o no existeixen pel domini {domain} (categoria {category})", "diagnosis_dns_bad_conf": "Alguns registres DNS són incorrectes o no existeixen pel domini {domain} (categoria {category})",
"diagnosis_dns_missing_record": "Segons la configuració DNS recomanada, hauríeu d'afegir un registre DNS amb la següent informació.<br>Tipus: <code>{type}</code><br>Nom: <code>{name}</code><br>Valor: <code>{value}</code>", "diagnosis_dns_missing_record": "Segons la configuració DNS recomanada, hauríeu d'afegir un registre DNS amb la següent informació.<br>Tipus: <code>{type}</code><br>Nom: <code>{name}</code><br>Valor: <code>{value}</code>",
"diagnosis_dns_discrepancy": "El registre DNS de tipus {type} i nom {name} no concorda amb la configuració recomanada.\nValor actual: {current}\nValor esperat: {value}", "diagnosis_dns_discrepancy": "La configuració DNS següent sembla que no segueix la configuració recomanada: <br>Tipus: <code>{type}</code><br>Nom: <code>{name}</code><br>Valor actual: <code>{current}</code><br>Valor esperat: <code>{value}</code>",
"diagnosis_services_bad_status": "El servei {service} està {status} :(", "diagnosis_services_bad_status": "El servei {service} està {status} :(",
"diagnosis_diskusage_verylow": "El lloc d'emmagatzematge <code>{mountpoint}</code> (en l'aparell <code>{device}</code>) només té disponibles {free} ({free_percent}%). Hauríeu de considerar alliberar una mica d'espai!", "diagnosis_diskusage_verylow": "El lloc d'emmagatzematge <code>{mountpoint}</code> (en l'aparell <code>{device}</code>) només té disponibles {free} ({free_percent}%). Hauríeu de considerar alliberar una mica d'espai!",
"diagnosis_diskusage_low": "El lloc d'emmagatzematge <code>{mountpoint}</code> (en l'aparell <code>{device}</code>) només té disponibles {free} ({free_percent}%). Aneu amb compte.", "diagnosis_diskusage_low": "El lloc d'emmagatzematge <code>{mountpoint}</code> (en l'aparell <code>{device}</code>) només té disponibles {free} ({free_percent}%). Aneu amb compte.",
@ -596,7 +596,7 @@
"diagnosis_description_web": "Web", "diagnosis_description_web": "Web",
"diagnosis_basesystem_hardware_board": "El model de la targeta del servidor és {model}", "diagnosis_basesystem_hardware_board": "El model de la targeta del servidor és {model}",
"diagnosis_basesystem_hardware": "L'arquitectura del maquinari del servidor és {virt} {arch}", "diagnosis_basesystem_hardware": "L'arquitectura del maquinari del servidor és {virt} {arch}",
"group_already_exist_on_system_but_removing_it": "El grup {group} ja existeix en els grups del sistema, però YunoHost l'eliminarà", "group_already_exist_on_system_but_removing_it": "El grup {group} ja existeix en els grups del sistema, però YunoHost l'eliminarà...",
"certmanager_warning_subdomain_dns_record": "El subdomini «{subdomain:s}» no resol a la mateixa adreça IP que «{domain:s}». Algunes funcions no estaran disponibles fins que no s'hagi arreglat i s'hagi regenerat el certificat.", "certmanager_warning_subdomain_dns_record": "El subdomini «{subdomain:s}» no resol a la mateixa adreça IP que «{domain:s}». Algunes funcions no estaran disponibles fins que no s'hagi arreglat i s'hagi regenerat el certificat.",
"domain_cannot_add_xmpp_upload": "No podeu afegir dominis començant per «xmpp-upload.». Aquest tipus de nom està reservat per a la funció de pujada de XMPP integrada a YunoHost.", "domain_cannot_add_xmpp_upload": "No podeu afegir dominis començant per «xmpp-upload.». Aquest tipus de nom està reservat per a la funció de pujada de XMPP integrada a YunoHost.",
"diagnosis_display_tip": "Per veure els problemes que s'han trobat, podeu anar a la secció de Diagnòstic a la pàgina web d'administració, o utilitzar « yunohost diagnostic show --issues » a la línia de comandes.", "diagnosis_display_tip": "Per veure els problemes que s'han trobat, podeu anar a la secció de Diagnòstic a la pàgina web d'administració, o utilitzar « yunohost diagnostic show --issues » a la línia de comandes.",
@ -637,9 +637,9 @@
"diagnosis_mail_fcrdns_nok_alternatives_4": "Alguns proveïdors no permeten configurar el DNS invers (o aquesta funció pot no funcionar…). Si teniu problemes a causa d'això, considereu les solucions següents:<br> - Alguns proveïdors d'accés a internet (ISP) donen l'alternativa de <a href='https://yunohost.org/#/smtp_relay'> utilitzar un relay de servidor de correu electrònic</a> tot i que implica que el relay podrà espiar el trànsit de correus electrònics.<br>- Una alternativa respectuosa amb la privacitat és utilitzar una VPN *amb una IP pública dedicada* per sobrepassar aquest tipus de limitacions. Mireu <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- O es pot <a href='https://yunohost.org/#/isp'>canviar a un proveïdor diferent</a>", "diagnosis_mail_fcrdns_nok_alternatives_4": "Alguns proveïdors no permeten configurar el DNS invers (o aquesta funció pot no funcionar…). Si teniu problemes a causa d'això, considereu les solucions següents:<br> - Alguns proveïdors d'accés a internet (ISP) donen l'alternativa de <a href='https://yunohost.org/#/smtp_relay'> utilitzar un relay de servidor de correu electrònic</a> tot i que implica que el relay podrà espiar el trànsit de correus electrònics.<br>- Una alternativa respectuosa amb la privacitat és utilitzar una VPN *amb una IP pública dedicada* per sobrepassar aquest tipus de limitacions. Mireu <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a><br>- O es pot <a href='https://yunohost.org/#/isp'>canviar a un proveïdor diferent</a>",
"diagnosis_mail_fcrdns_nok_alternatives_6": "Alguns proveïdors no permeten configurar el vostre DNS invers (o la funció no els hi funciona…). Si el vostre DNS invers està correctament configurat per IPv4, podeu intentar deshabilitar l'ús de IPv6 per a enviar correus electrònics utilitzant <cmd>yunohost settings set smtp.allow_ipv6 -v off</cmd>. Nota: aquesta última solució implica que no podreu enviar o rebre correus electrònics cap a els pocs servidors que hi ha que només tenen IPv-6.", "diagnosis_mail_fcrdns_nok_alternatives_6": "Alguns proveïdors no permeten configurar el vostre DNS invers (o la funció no els hi funciona…). Si el vostre DNS invers està correctament configurat per IPv4, podeu intentar deshabilitar l'ús de IPv6 per a enviar correus electrònics utilitzant <cmd>yunohost settings set smtp.allow_ipv6 -v off</cmd>. Nota: aquesta última solució implica que no podreu enviar o rebre correus electrònics cap a els pocs servidors que hi ha que només tenen IPv-6.",
"diagnosis_http_hairpinning_issue_details": "Això és probablement a causa del router del vostre proveïdor d'accés a internet. El que fa, que gent de fora de la xarxa local pugui accedir al servidor sense problemes, però no la gent de dins la xarxa local (com vostè probablement) quan s'utilitza el nom de domini o la IP global. Podreu segurament millorar la situació fent una ullada a <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>", "diagnosis_http_hairpinning_issue_details": "Això és probablement a causa del router del vostre proveïdor d'accés a internet. El que fa, que gent de fora de la xarxa local pugui accedir al servidor sense problemes, però no la gent de dins la xarxa local (com vostè probablement) quan s'utilitza el nom de domini o la IP global. Podreu segurament millorar la situació fent una ullada a <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>",
"backup_archive_cant_retrieve_info_json": "No s'ha pogut carregar la informació de l'arxiu «{archive}» No s'ha pogut obtenir el fitxer info.json (o no és un fitxer json vàlid).", "backup_archive_cant_retrieve_info_json": "No s'ha pogut carregar la informació de l'arxiu «{archive}»... No s'ha pogut obtenir el fitxer info.json (o no és un fitxer json vàlid).",
"backup_archive_corrupted": "Sembla que l'arxiu de la còpia de seguretat «{archive}» està corromput : {error}", "backup_archive_corrupted": "Sembla que l'arxiu de la còpia de seguretat «{archive}» està corromput : {error}",
"certmanager_domain_not_diagnosed_yet": "Encara no hi ha cap resultat de diagnòstic per al domini %s. Torneu a executar el diagnòstic per a les categories «Registres DNS» i «Web» en la secció de diagnòstic per comprovar que el domini està preparat per a Let's Encrypt. (O si sabeu el que esteu fent, utilitzant «--no-checks» per deshabilitar les comprovacions.)", "certmanager_domain_not_diagnosed_yet": "Encara no hi ha cap resultat de diagnòstic per al domini {domain}. Torneu a executar el diagnòstic per a les categories «Registres DNS» i «Web» en la secció de diagnòstic per comprovar que el domini està preparat per a Let's Encrypt. (O si sabeu el que esteu fent, utilitzant «--no-checks» per deshabilitar les comprovacions.)",
"diagnosis_ip_no_ipv6_tip": "Utilitzar una IPv6 no és obligatori per a que funcioni el servidor, però és millor per la salut d'Internet en conjunt. La IPv6 hauria d'estar configurada automàticament pel sistema o pel proveïdor si està disponible. Si no és el cas, pot ser necessari configurar alguns paràmetres més de forma manual tal i com s'explica en la documentació disponible aquí: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. Si no podeu habilitar IPv6 o us sembla massa tècnic, podeu ignorar aquest avís sense problemes.", "diagnosis_ip_no_ipv6_tip": "Utilitzar una IPv6 no és obligatori per a que funcioni el servidor, però és millor per la salut d'Internet en conjunt. La IPv6 hauria d'estar configurada automàticament pel sistema o pel proveïdor si està disponible. Si no és el cas, pot ser necessari configurar alguns paràmetres més de forma manual tal i com s'explica en la documentació disponible aquí: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. Si no podeu habilitar IPv6 o us sembla massa tècnic, podeu ignorar aquest avís sense problemes.",
"diagnosis_domain_expiration_not_found": "No s'ha pogut comprovar la data d'expiració d'alguns dominis", "diagnosis_domain_expiration_not_found": "No s'ha pogut comprovar la data d'expiració d'alguns dominis",
"diagnosis_domain_not_found_details": "El domini {domain} no existeix en la base de dades WHOIS o ha expirat!", "diagnosis_domain_not_found_details": "El domini {domain} no existeix en la base de dades WHOIS o ha expirat!",
@ -648,5 +648,33 @@
"diagnosis_domain_expiration_warning": "Alguns dominis expiraran properament!", "diagnosis_domain_expiration_warning": "Alguns dominis expiraran properament!",
"diagnosis_domain_expiration_error": "Alguns dominis expiraran EN BREUS!", "diagnosis_domain_expiration_error": "Alguns dominis expiraran EN BREUS!",
"diagnosis_domain_expires_in": "{domain} expirarà en {days} dies.", "diagnosis_domain_expires_in": "{domain} expirarà en {days} dies.",
"diagnosis_swap_tip": "Vigileu i tingueu en compte que els servidor està allotjant memòria d'intercanvi en una targeta SD o en l'emmagatzematge SSD, això pot reduir dràsticament l'esperança de vida del dispositiu." "diagnosis_swap_tip": "Vigileu i tingueu en compte que els servidor està allotjant memòria d'intercanvi en una targeta SD o en l'emmagatzematge SSD, això pot reduir dràsticament l'esperança de vida del dispositiu.",
"restore_already_installed_apps": "No s'han pogut restaurar les següents aplicacions perquè ja estan instal·lades: {apps}",
"app_packaging_format_not_supported": "No es pot instal·lar aquesta aplicació ja que el format del paquet no és compatible amb la versió de YunoHost del sistema. Hauríeu de considerar actualitzar el sistema.",
"diagnosis_dns_try_dyndns_update_force": "La configuració DNS d'aquest domini hauria de ser gestionada automàticament per YunoHost. Si aquest no és el cas, podeu intentar forçar-ne l'actualització utilitzant <cmd>yunohost dyndns update --force</cmd>.",
"migration_0015_cleaning_up": "Netejant la memòria cau i els paquets que ja no són necessaris...",
"migration_0015_specific_upgrade": "Començant l'actualització dels paquets del sistema que s'han d'actualitzar de forma independent...",
"migration_0015_modified_files": "Tingueu en compte que s'han trobat els següents fitxers que es van modificar manualment i podria ser que es sobreescriguin durant l'actualització: {manually_modified_files}",
"migration_0015_problematic_apps_warning": "Tingueu en compte que s'han trobat les següents aplicacions que podrien ser problemàtiques. Sembla que aquestes aplicacions no s'han instal·lat des del catàleg d'aplicacions de YunoHost, o no estan marcades com «funcionant». En conseqüència, no es pot garantir que segueixin funcionant després de l'actualització: {problematic_apps}",
"migration_0015_general_warning": "Tingueu en compte que aquesta migració és una operació delicada. L'equip de YunoHost ha fet tots els possibles per revisar i testejar, però tot i això podria ser que la migració trenqui alguna part del sistema o algunes aplicacions.\n\nPer tant, està recomana:\n - Fer una còpia de seguretat de totes les dades o aplicacions crítiques. Més informació a https://yunohost.org/backup;\n - Ser pacient un cop comenci la migració: en funció de la connexió Internet i del maquinari, podria estar unes hores per actualitzar-ho tot.",
"migration_0015_system_not_fully_up_to_date": "El sistema no està completament al dia. Heu de fer una actualització normal abans de fer la migració a Buster.",
"migration_0015_not_enough_free_space": "Hi ha poc espai lliure a /var/! HI hauria d'haver un mínim de 1GB lliure per poder fer aquesta migració.",
"migration_0015_not_stretch": "La distribució actual de Debian no és Stretch!",
"migration_0015_yunohost_upgrade": "Començant l'actualització del nucli de YunoHost...",
"migration_0015_still_on_stretch_after_main_upgrade": "Alguna cosa ha anat malament durant la actualització principal, sembla que el sistema encara està en Debian Stretch",
"migration_0015_main_upgrade": "Començant l'actualització principal...",
"migration_0015_patching_sources_list": "Apedaçament de source.lists...",
"migration_0015_start": "Començant la migració a Buster",
"migration_description_0015_migrate_to_buster": "Actualitza els sistema a Debian Buster i YunoHost 4.x",
"regenconf_need_to_explicitly_specify_ssh": "La configuració ssh ha estat modificada manualment, però heu d'especificar explícitament la categoria «ssh» amb --force per fer realment els canvis.",
"migration_0015_weak_certs": "S'han trobat els següents certificats que encara utilitzen algoritmes de signatura febles i s'han d'actualitzar per a ser compatibles amb la propera versió de nginx: {certs}",
"service_description_php7.3-fpm": "Executa aplicacions escrites en PHP amb NGINX",
"migration_0018_failed_to_reset_legacy_rules": "No s'ha pogut restaurar les regles legacy iptables: {error}",
"migration_0018_failed_to_migrate_iptables_rules": "No s'ha pogut migrar les regles legacy iptables a nftables: {error}",
"migration_0017_not_enough_space": "Feu suficient espai disponible en {path} per a realitzar la migració.",
"migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 està instal·lat, però postgreSQL 11 no? Potser que hagi passat alguna cosa rara en aquest sistema :(...",
"migration_0017_postgresql_96_not_installed": "PostgreSQL no està instal·lat en aquest sistema. No s'ha de realitzar cap operació.",
"migration_description_0018_xtable_to_nftable": "Migrar les regles del trànsit de xarxa al nou sistema nftable",
"migration_description_0017_postgresql_9p6_to_11": "Migrar les bases de dades de PosrgreSQL 9.6 a 11",
"migration_description_0016_php70_to_php73_pools": "Migrar els fitxers de configuració «pool» php7.0-fpm a php7.3"
} }

View file

@ -11,7 +11,7 @@
"app_id_invalid": "Falsche App-ID", "app_id_invalid": "Falsche App-ID",
"app_install_files_invalid": "Diese Dateien können nicht installiert werden", "app_install_files_invalid": "Diese Dateien können nicht installiert werden",
"app_manifest_invalid": "Mit dem App-Manifest stimmt etwas nicht: {error}", "app_manifest_invalid": "Mit dem App-Manifest stimmt etwas nicht: {error}",
"app_not_installed": "Die App {app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}", "app_not_installed": "{app:s} konnte nicht in der Liste installierter Apps gefunden werden: {all_apps}",
"app_removed": "{app:s} wurde entfernt", "app_removed": "{app:s} wurde entfernt",
"app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?", "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden, ist die URL korrekt?",
"app_unknown": "Unbekannte App", "app_unknown": "Unbekannte App",
@ -23,8 +23,8 @@
"ask_main_domain": "Hauptdomain", "ask_main_domain": "Hauptdomain",
"ask_new_admin_password": "Neues Verwaltungskennwort", "ask_new_admin_password": "Neues Verwaltungskennwort",
"ask_password": "Passwort", "ask_password": "Passwort",
"backup_app_failed": "Konnte keine Sicherung für die App '{app:s}' erstellen", "backup_app_failed": "Konnte keine Sicherung für {app:s} erstellen",
"backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden", "backup_archive_app_not_found": "{app:s} konnte in keiner Datensicherung gefunden werden",
"backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits.", "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits.",
"backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden", "backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden",
"backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen",
@ -38,7 +38,7 @@
"backup_output_directory_forbidden": "Wähle ein anderes Ausgabeverzeichnis. Datensicherungen können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden", "backup_output_directory_forbidden": "Wähle ein anderes Ausgabeverzeichnis. Datensicherungen können nicht in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var oder in Unterordnern von /home/yunohost.backup/archives erstellt werden",
"backup_output_directory_not_empty": "Der gewählte Ausgabeordner sollte leer sein", "backup_output_directory_not_empty": "Der gewählte Ausgabeordner sollte leer sein",
"backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden", "backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden",
"backup_running_hooks": "Datensicherunghook wird ausgeführt", "backup_running_hooks": "Datensicherunghook wird ausgeführt...",
"custom_app_url_required": "Es muss eine URL angegeben werden, um deine benutzerdefinierte App {app:s} zu aktualisieren", "custom_app_url_required": "Es muss eine URL angegeben werden, um deine benutzerdefinierte App {app:s} zu aktualisieren",
"domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden", "domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden",
"domain_created": "Die Domain wurde angelegt", "domain_created": "Die Domain wurde angelegt",
@ -78,7 +78,7 @@
"iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt", "iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container oder es wird nicht vom Kernel unterstützt",
"ldap_initialized": "LDAP wurde initialisiert", "ldap_initialized": "LDAP wurde initialisiert",
"mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden", "mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden",
"mail_domain_unknown": "Unbekannte Mail Domain '{domain:s}'", "mail_domain_unknown": "Die Domäne '{domain:s}' dieser E-Mail-Adresse ist ungültig. Wähle bitte eine Domäne, welche durch diesen Server verwaltet wird.",
"mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden", "mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden",
"main_domain_change_failed": "Die Hauptdomain konnte nicht geändert werden", "main_domain_change_failed": "Die Hauptdomain konnte nicht geändert werden",
"main_domain_changed": "Die Hauptdomain wurde geändert", "main_domain_changed": "Die Hauptdomain wurde geändert",
@ -105,26 +105,26 @@
"restore_nothings_done": "Es wurde nicht wiederhergestellt", "restore_nothings_done": "Es wurde nicht wiederhergestellt",
"restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...",
"restore_running_hooks": "Wiederherstellung wird gestartet…", "restore_running_hooks": "Wiederherstellung wird gestartet…",
"service_add_failed": "Der Dienst '{service:s}' kann nicht hinzugefügt werden", "service_add_failed": "Der Dienst '{service:s}' konnte nicht hinzugefügt werden",
"service_added": "Der Service '{service:s}' wurde erfolgreich hinzugefügt", "service_added": "Der Dienst '{service:s}' wurde erfolgreich hinzugefügt",
"service_already_started": "Der Dienst '{service:s}' läuft bereits", "service_already_started": "Der Dienst '{service:s}' läuft bereits",
"service_already_stopped": "Dienst '{service:s}' wurde bereits gestoppt", "service_already_stopped": "Der Dienst '{service:s}' wurde bereits gestoppt",
"service_cmd_exec_failed": "Der Befehl '{command:s}' konnte nicht ausgeführt werden", "service_cmd_exec_failed": "Der Befehl '{command:s}' konnte nicht ausgeführt werden",
"service_disable_failed": "Der Dienst '{service:s}' konnte nicht deaktiviert werden", "service_disable_failed": "Der Dienst '{service:s}' konnte nicht deaktiviert werden",
"service_disabled": "Der Dienst '{service:s}' wurde erfolgreich deaktiviert", "service_disabled": "Der Dienst '{service:s}' wurde erfolgreich deaktiviert",
"service_enable_failed": "Der Dienst '{service:s}' konnte nicht aktiviert werden", "service_enable_failed": "Der Dienst '{service:s}' konnte beim Hochfahren nicht gestartet werden.\n\nKürzlich erstelle Logs des Dienstes: {logs:s}",
"service_enabled": "Der Dienst '{service:s}' wurde erfolgreich aktiviert", "service_enabled": "Der Dienst '{service:s}' wird nun beim Hochfahren des Systems automatisch gestartet.",
"service_remove_failed": "Der Dienst '{service:s}' konnte nicht entfernt werden", "service_remove_failed": "Der Dienst '{service:s}' konnte nicht entfernt werden",
"service_removed": "Der Dienst '{service:s}' wurde erfolgreich entfernt", "service_removed": "Der Dienst '{service:s}' wurde erfolgreich entfernt",
"service_start_failed": "Der Dienst '{service:s}' konnte nicht gestartet werden", "service_start_failed": "Der Dienst '{service:s}' konnte nicht gestartet werden\n\nKürzlich erstellte Logs des Dienstes: {logs:s}",
"service_started": "Der Dienst '{service:s}' wurde erfolgreich gestartet", "service_started": "Der Dienst '{service:s}' wurde erfolgreich gestartet",
"service_stop_failed": "Der Dienst '{service:s}' kann nicht gestoppt werden", "service_stop_failed": "Der Dienst '{service:s}' kann nicht gestoppt werden",
"service_stopped": "Der Dienst '{service:s}' wurde erfolgreich beendet", "service_stopped": "Der Dienst '{service:s}' wurde erfolgreich beendet",
"service_unknown": "Unbekannter Dienst '{service:s}'", "service_unknown": "Unbekannter Dienst '{service:s}'",
"ssowat_conf_generated": "Die Konfiguration von SSOwat war erfolgreich", "ssowat_conf_generated": "Die Konfiguration von SSOwat erstellt",
"ssowat_conf_updated": "Die persistente SSOwat Einstellung wurde aktualisiert", "ssowat_conf_updated": "Die Konfiguration von SSOwat aktualisiert",
"system_upgraded": "Das System wurde aktualisiert", "system_upgraded": "System aktualisiert",
"system_username_exists": "Der Benutzername existiert bereits", "system_username_exists": "Der Benutzername existiert bereits in der Liste der System-Benutzer",
"unbackup_app": "App '{app:s}' konnte nicht gespeichert werden", "unbackup_app": "App '{app:s}' konnte nicht gespeichert werden",
"unexpected_error": "Ein unerwarteter Fehler ist aufgetreten", "unexpected_error": "Ein unerwarteter Fehler ist aufgetreten",
"unlimit": "Kein Kontingent", "unlimit": "Kein Kontingent",
@ -154,7 +154,7 @@
"backup_creation_failed": "Konnte Backup-Archiv nicht erstellen", "backup_creation_failed": "Konnte Backup-Archiv nicht erstellen",
"pattern_positive_number": "Muss eine positive Zahl sein", "pattern_positive_number": "Muss eine positive Zahl sein",
"app_not_correctly_installed": "{app:s} scheint nicht korrekt installiert zu sein", "app_not_correctly_installed": "{app:s} scheint nicht korrekt installiert zu sein",
"app_requirements_checking": "Überprüfe notwendige Pakete für {app}", "app_requirements_checking": "Überprüfe notwendige Pakete für {app}...",
"app_requirements_unmeet": "Anforderungen für {app} werden nicht erfüllt, das Paket {pkgname} ({version}) muss {spec} sein", "app_requirements_unmeet": "Anforderungen für {app} werden nicht erfüllt, das Paket {pkgname} ({version}) muss {spec} sein",
"app_unsupported_remote_type": "Für die App wurde ein nicht unterstützer Steuerungstyp verwendet", "app_unsupported_remote_type": "Für die App wurde ein nicht unterstützer Steuerungstyp verwendet",
"backup_archive_broken_link": "Auf das Backup-Archiv konnte nicht zugegriffen werden (ungültiger Link zu {path:s})", "backup_archive_broken_link": "Auf das Backup-Archiv konnte nicht zugegriffen werden (ungültiger Link zu {path:s})",
@ -166,24 +166,24 @@
"package_unknown": "Unbekanntes Paket '{pkgname}'", "package_unknown": "Unbekanntes Paket '{pkgname}'",
"certmanager_attempt_to_replace_valid_cert": "Du versuchst gerade eine richtiges und gültiges Zertifikat der Domain {domain:s} zu überschreiben! (Benutze --force , um diese Nachricht zu umgehen)", "certmanager_attempt_to_replace_valid_cert": "Du versuchst gerade eine richtiges und gültiges Zertifikat der Domain {domain:s} zu überschreiben! (Benutze --force , um diese Nachricht zu umgehen)",
"certmanager_domain_unknown": "Unbekannte Domain '{domain:s}'", "certmanager_domain_unknown": "Unbekannte Domain '{domain:s}'",
"certmanager_domain_cert_not_selfsigned": "Das Zertifikat der Domain {domain:s} ist kein selbstsigniertes Zertifikat. Bist du dir sicher, dass du es ersetzen willst? (Benutze dafür '--force')", "certmanager_domain_cert_not_selfsigned": "Das Zertifikat der Domain {domain:s} ist kein selbstsigniertes Zertifikat. Sind Sie sich sicher, dass Sie es ersetzen wollen? (Benutzen Sie dafür '--force')",
"certmanager_certificate_fetching_or_enabling_failed": "Es scheint so als wäre die Aktivierung des Zertifikats für die Domain {domain:s} fehlgeschlagen...", "certmanager_certificate_fetching_or_enabling_failed": "Die Aktivierung des neuen Zertifikats für die {domain:s} ist fehlgeschlagen...",
"certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!", "certmanager_attempt_to_renew_nonLE_cert": "Das Zertifikat der Domain '{domain:s}' wurde nicht von Let's Encrypt ausgestellt. Es kann nicht automatisch erneuert werden!",
"certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)", "certmanager_attempt_to_renew_valid_cert": "Das Zertifikat der Domain {domain:s} läuft nicht in Kürze ab! (Benutze --force um diese Nachricht zu umgehen)",
"certmanager_domain_http_not_working": "Es scheint so, dass die Domain {domain:s} nicht über HTTP erreicht werden kann. Bitte überprüfe, ob deine DNS und nginx Konfiguration in Ordnung ist", "certmanager_domain_http_not_working": "Die Domäne {domain:s} scheint über HTTP nicht erreichbar zu sein. Für weitere Informationen überprüfen Sie bitte die Kategorie 'Web' im Diagnose-Bereich. (Wenn Sie wißen was Sie tun, nutzen Sie '--no-checks' um die Überprüfung zu überspringen.)",
"certmanager_error_no_A_record": "Kein DNS 'A' Eintrag für die Domain {domain:s} gefunden. Dein Domainname muss auf diese Maschine weitergeleitet werden, um ein Let's Encrypt Zertifikat installieren zu können! (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )", "certmanager_error_no_A_record": "Kein DNS 'A' Eintrag für die Domain {domain:s} gefunden. Dein Domainname muss auf diese Maschine weitergeleitet werden, um ein Let's Encrypt Zertifikat installieren zu können! (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )",
"certmanager_domain_dns_ip_differs_from_public_ip": "Der DNS 'A' Eintrag der Domain {domain:s} unterscheidet sich von dieser Server-IP. Wenn du gerade deinen A Eintrag verändert hast, warte bitte etwas, damit die Änderungen wirksam werden (du kannst die DNS Propagation mittels Website überprüfen) (Wenn du weißt was du tust, kannst du --no-checks benutzen, um diese Überprüfung zu überspringen. )", "certmanager_domain_dns_ip_differs_from_public_ip": "Die DNS-Einträge der Domäne {domain:s} unterscheiden sich von der IP dieses Servers. Wenn Sie gerade Ihren A-Eintrag verändert haben, warten Sie bitte etwas, damit die Änderungen wirksam werden (Sie können die DNS Propagation mittels Website überprüfen) (Wenn Sie wißen was Sie tun, können Sie --no-checks benutzen, um diese Überprüfung zu überspringen. )",
"certmanager_cannot_read_cert": "Es ist ein Fehler aufgetreten, als es versucht wurde das aktuelle Zertifikat für die Domain {domain:s} zu öffnen (Datei: {file:s}), Grund: {reason:s}", "certmanager_cannot_read_cert": "Es ist ein Fehler aufgetreten, als es versucht wurde das aktuelle Zertifikat für die Domain {domain:s} zu öffnen (Datei: {file:s}), Grund: {reason:s}",
"certmanager_cert_install_success_selfsigned": "Ein selbstsigniertes Zertifikat für die Domain {domain:s} wurde erfolgreich installiert!", "certmanager_cert_install_success_selfsigned": "Ein selbstsigniertes Zertifikat für die Domain {domain:s} wurde erfolgreich installiert",
"certmanager_cert_install_success": "Für die Domain {domain:s} wurde erfolgreich ein Let's Encrypt installiert!", "certmanager_cert_install_success": "Für die Domain {domain:s} wurde erfolgreich ein Let's Encrypt Zertifikat installiert.",
"certmanager_cert_renew_success": "Das Let's Encrypt Zertifikat für die Domain {domain:s} wurde erfolgreich erneuert!", "certmanager_cert_renew_success": "Das Let's Encrypt Zertifikat für die Domain {domain:s} wurde erfolgreich erneuert.",
"certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit schon zu viele Zertifikate für die exakt gleiche Domain {domain:s} ausgestellt. Bitte versuche es später nochmal. Besuche https://letsencrypt.org/docs/rate-limits/ für mehr Informationen", "certmanager_hit_rate_limit": "Es wurden innerhalb kurzer Zeit zu viele Zertifikate für dieselbe Domain {domain:s} ausgestellt. Bitte versuchen Sie es später nochmal. Besuchen Sie https://letsencrypt.org/docs/rate-limits/ für mehr Informationen",
"certmanager_cert_signing_failed": "Das neue Zertifikat konnte nicht signiert werden", "certmanager_cert_signing_failed": "Das neue Zertifikat konnte nicht signiert werden",
"certmanager_no_cert_file": "Die Zertifikatsdatei für die Domain {domain:s} (Datei: {file:s}) konnte nicht gelesen werden", "certmanager_no_cert_file": "Die Zertifikatsdatei für die Domain {domain:s} (Datei: {file:s}) konnte nicht gelesen werden",
"certmanager_conflicting_nginx_file": "Die Domain konnte nicht für die ACME challenge vorbereitet werden: Die nginx Konfigurationsdatei {filepath:s} verursacht Probleme und sollte vorher entfernt werden", "certmanager_conflicting_nginx_file": "Die Domain konnte nicht für die ACME challenge vorbereitet werden: Die nginx Konfigurationsdatei {filepath:s} verursacht Probleme und sollte vorher entfernt werden",
"domain_cannot_remove_main": "Die primäre Domain konnten nicht entfernt werden. Lege zuerst einen neue primäre Domain fest", "domain_cannot_remove_main": "Die primäre Domain konnten nicht entfernt werden. Lege zuerst einen neue primäre Domain fest",
"certmanager_self_ca_conf_file_not_found": "Die Konfigurationsdatei der Zertifizierungsstelle für selbstsignierte Zertifikate wurde nicht gefunden (Datei {file:s})", "certmanager_self_ca_conf_file_not_found": "Die Konfigurationsdatei der Zertifizierungsstelle für selbstsignierte Zertifikate wurde nicht gefunden (Datei {file:s})",
"certmanager_acme_not_configured_for_domain": "Das Zertifikat für die Domain '{domain:s}' scheint nicht richtig installiert zu sein. Bitte führe den Befehl cert-install für diese Domain nochmals aus.", "certmanager_acme_not_configured_for_domain": "Die ACME Challenge kann im Moment nicht für {domain} ausgeführt werden, weil in ihrer nginx conf das entsprechende Code-Snippet fehlt... Bitte stellen Sie sicher, dass Ihre nginx-Konfiguration mit 'yunohost tools regen-conf nginx --dry-run --with-diff' auf dem neuesten Stand ist.",
"certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht analysiert werden (Datei: {file:s})", "certmanager_unable_to_parse_self_CA_name": "Der Name der Zertifizierungsstelle für selbstsignierte Zertifikate konnte nicht analysiert werden (Datei: {file:s})",
"certmanager_http_check_timeout": "Eine Zeitüberschreitung ist aufgetreten, als der Server versuchte sich selbst über HTTP mit der öffentlichen IP (Domain '{domain:s}' mit der IP '{ip:s}') zu erreichen. Möglicherweise ist dafür hairpinning oder eine falsch konfigurierte Firewall/Router deines Servers dafür verantwortlich.", "certmanager_http_check_timeout": "Eine Zeitüberschreitung ist aufgetreten, als der Server versuchte sich selbst über HTTP mit der öffentlichen IP (Domain '{domain:s}' mit der IP '{ip:s}') zu erreichen. Möglicherweise ist dafür hairpinning oder eine falsch konfigurierte Firewall/Router deines Servers dafür verantwortlich.",
"certmanager_couldnt_fetch_intermediate_cert": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte die Teilzertifikate von Let's Encrypt zusammenzusetzen. Die Installation/Erneuerung des Zertifikats wurde abgebrochen — bitte versuche es später erneut.", "certmanager_couldnt_fetch_intermediate_cert": "Eine Zeitüberschreitung ist aufgetreten als der Server versuchte die Teilzertifikate von Let's Encrypt zusammenzusetzen. Die Installation/Erneuerung des Zertifikats wurde abgebrochen — bitte versuche es später erneut.",
@ -194,15 +194,15 @@
"app_change_url_identical_domains": "Die alte und neue domain/url_path sind identisch: ('{domain:s} {path:s}'). Es gibt nichts zu tun.", "app_change_url_identical_domains": "Die alte und neue domain/url_path sind identisch: ('{domain:s} {path:s}'). Es gibt nichts zu tun.",
"app_already_up_to_date": "{app:s} ist bereits aktuell", "app_already_up_to_date": "{app:s} ist bereits aktuell",
"backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt", "backup_abstract_method": "Diese Backup-Methode wird noch nicht unterstützt",
"backup_applying_method_tar": "Erstellen des Backup-tar Archives", "backup_applying_method_tar": "Erstellen des Backup-tar Archives...",
"backup_applying_method_copy": "Kopiere alle Dateien ins Backup", "backup_applying_method_copy": "Kopiere alle Dateien ins Backup...",
"app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modifikation. Vielleicht sollte sie aktualisiert werden.", "app_change_url_no_script": "Die Anwendung '{app_name:s}' unterstützt bisher keine URL-Modifikation. Vielleicht sollte sie aktualisiert werden.",
"app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}", "app_location_unavailable": "Diese URL ist nicht verfügbar oder wird von einer installierten Anwendung genutzt:\n{apps:s}",
"backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf", "backup_applying_method_custom": "Rufe die benutzerdefinierte Backup-Methode '{method:s}' auf...",
"backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten", "backup_archive_system_part_not_available": "Der System-Teil '{part:s}' ist in diesem Backup nicht enthalten",
"backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden", "backup_archive_writing_error": "Die Dateien '{source:s} (im Ordner '{dest:s}') konnten nicht in das komprimierte Archiv-Backup '{archive:s}' hinzugefügt werden",
"app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}", "app_change_url_success": "{app:s} URL ist nun {domain:s}{path:s}",
"backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository", "backup_applying_method_borg": "Sende alle Dateien zur Sicherung ins borg-backup repository...",
"global_settings_bad_type_for_setting": "Falscher Typ für Einstellung {setting:s}. Empfangen: {received_type:s}, aber erwartet: {expected_type:s}", "global_settings_bad_type_for_setting": "Falscher Typ für Einstellung {setting:s}. Empfangen: {received_type:s}, aber erwartet: {expected_type:s}",
"global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting:s}. Habe '{choice:s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices:s}", "global_settings_bad_choice_for_enum": "Falsche Wahl für die Einstellung {setting:s}. Habe '{choice:s}' erhalten, aber es stehen nur folgende Auswahlmöglichkeiten zur Verfügung: {available_choices:s}",
"file_does_not_exist": "Die Datei {path:s} existiert nicht.", "file_does_not_exist": "Die Datei {path:s} existiert nicht.",
@ -212,16 +212,16 @@
"dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider:s} die Domain(s) {domain:s} bereitstellen kann.", "dyndns_could_not_check_provide": "Konnte nicht überprüft, ob {provider:s} die Domain(s) {domain:s} bereitstellen kann.",
"domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen, was die * empfohlene * Konfiguration ist. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.", "domain_dns_conf_is_just_a_recommendation": "Dieser Befehl zeigt Ihnen, was die * empfohlene * Konfiguration ist. Die DNS-Konfiguration wird NICHT für Sie eingerichtet. Es liegt in Ihrer Verantwortung, Ihre DNS-Zone in Ihrem Registrar gemäß dieser Empfehlung zu konfigurieren.",
"dpkg_lock_not_available": "Dieser Befehl kann momentan nicht ausgeführt werden, da anscheinend ein anderes Programm die Sperre von dpkg (dem Systempaket-Manager) verwendet", "dpkg_lock_not_available": "Dieser Befehl kann momentan nicht ausgeführt werden, da anscheinend ein anderes Programm die Sperre von dpkg (dem Systempaket-Manager) verwendet",
"confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Deines Systems beeinträchtigen. Du solltest es wahrscheinlich NICHT installieren, es sei denn, Du weisst, was Du tust. Bist du bereit, dieses Risiko einzugehen? [{answers:s}]", "confirm_app_install_thirdparty": "WARNUNG! Das Installieren von Anwendungen von Drittanbietern kann die Integrität und Sicherheit Ihres Systems beeinträchtigen. Sie sollten Sie wahrscheinlich NICHT installieren, es sei denn, Sie wiẞen, was Sie tun. Sind Sie bereit, dieses Risiko einzugehen? [{answers:s}]",
"confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"nicht funktionsfähig\")! Du solltest es wahrscheinlich NICHT installieren, es sei denn, du weißt, was du tust. Es wird keine Unterstützung geleistet, falls diese Anwendung nicht funktioniert oder dein System zerstört... Falls du bereit bist, dieses Risiko einzugehen, tippe '{answers:s}'", "confirm_app_install_danger": "WARNUNG! Diese Anwendung ist noch experimentell (wenn nicht ausdrücklich \"not working\"/\"nicht funktionsfähig\")! Sie sollten sie wahrscheinlich NICHT installieren, es sei denn, Sie wißen, was Sie tun. Es wird keine Unterstützung geleistet, falls diese Anwendung nicht funktioniert oder Ihr System zerstört... Falls Sie bereit bist, dieses Risiko einzugehen, tippe '{answers:s}'",
"confirm_app_install_warning": "Warnung: Diese Anwendung funktioniert möglicherweise, ist jedoch nicht gut in YunoHost integriert. Einige Funktionen wie Single Sign-On und Backup / Restore sind möglicherweise nicht verfügbar. Trotzdem installieren? [{answers:s}] ", "confirm_app_install_warning": "Warnung: Diese Anwendung funktioniert möglicherweise, ist jedoch nicht gut in YunoHost integriert. Einige Funktionen wie Single Sign-On und Backup / Restore sind möglicherweise nicht verfügbar. Trotzdem installieren? [{answers:s}] ",
"backup_with_no_restore_script_for_app": "Die App {app:s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.", "backup_with_no_restore_script_for_app": "{app:s} hat kein Wiederherstellungsskript. Das Backup dieser App kann nicht automatisch wiederhergestellt werden.",
"backup_with_no_backup_script_for_app": "Die App {app:s} hat kein Sicherungsskript. Ignoriere es.", "backup_with_no_backup_script_for_app": "Die App {app:s} hat kein Sicherungsskript. Ignoriere es.",
"backup_unable_to_organize_files": "Dateien im Archiv konnten nicht mit der schnellen Methode organisiert werden", "backup_unable_to_organize_files": "Dateien im Archiv konnten nicht mit der schnellen Methode organisiert werden",
"backup_system_part_failed": "Der Systemteil '{part:s}' konnte nicht gesichert werden", "backup_system_part_failed": "Der Systemteil '{part:s}' konnte nicht gesichert werden",
"backup_permission": "Sicherungsberechtigung für App {app:s}", "backup_permission": "Sicherungsberechtigung für {app:s}",
"backup_output_symlink_dir_broken": "Ihr Archivverzeichnis '{path:s}' ist ein fehlerhafter Symlink. Vielleicht haben Sie vergessen, das Speichermedium, auf das er verweist, neu zu mounten oder einzustecken.", "backup_output_symlink_dir_broken": "Ihr Archivverzeichnis '{path:s}' ist ein fehlerhafter Symlink. Vielleicht haben Sie vergessen, das Speichermedium, auf das er verweist, neu zu mounten oder einzustecken.",
"backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten", "backup_mount_archive_for_restore": "Archiv für Wiederherstellung vorbereiten...",
"backup_method_tar_finished": "Tar-Backup-Archiv erstellt", "backup_method_tar_finished": "Tar-Backup-Archiv erstellt",
"backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method:s}' beendet", "backup_method_custom_finished": "Benutzerdefinierte Sicherungsmethode '{method:s}' beendet",
"backup_method_copy_finished": "Sicherungskopie beendet", "backup_method_copy_finished": "Sicherungskopie beendet",
@ -232,16 +232,16 @@
"backup_couldnt_bind": "{src:s} konnte nicht an {dest:s} angebunden werden.", "backup_couldnt_bind": "{src:s} konnte nicht an {dest:s} angebunden werden.",
"backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert", "backup_borg_not_implemented": "Die Borg-Sicherungsmethode ist noch nicht implementiert",
"backup_ask_for_copying_if_needed": "Möchten Sie die Sicherung mit {size:s}MB temporär durchführen? (Dieser Weg wird verwendet, da einige Dateien nicht mit einer effizienteren Methode vorbereitet werden konnten.)", "backup_ask_for_copying_if_needed": "Möchten Sie die Sicherung mit {size:s}MB temporär durchführen? (Dieser Weg wird verwendet, da einige Dateien nicht mit einer effizienteren Methode vorbereitet werden konnten.)",
"backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien", "backup_actually_backuping": "Erstellt ein Backup-Archiv aus den gesammelten Dateien...",
"ask_new_path": "Neuer Pfad", "ask_new_path": "Neuer Pfad",
"ask_new_domain": "Neue Domain", "ask_new_domain": "Neue Domain",
"app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden", "app_upgrade_some_app_failed": "Einige Anwendungen können nicht aktualisiert werden",
"app_upgrade_app_name": "{app} wird jetzt aktualisiert", "app_upgrade_app_name": "{app} wird jetzt aktualisiert...",
"app_upgrade_several_apps": "Die folgenden Apps werden aktualisiert: {apps}", "app_upgrade_several_apps": "Die folgenden Apps werden aktualisiert: {apps}",
"app_start_restore": "Anwendung {app} wird wiederhergestellt…", "app_start_restore": "{app} wird wiederhergestellt...",
"app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen", "app_start_backup": "Sammeln von Dateien, die für {app} gesichert werden sollen...",
"app_start_remove": "Anwendung {app} wird entfernt…", "app_start_remove": "{app} wird entfernt...",
"app_start_install": "Anwendung {app} wird installiert…", "app_start_install": "{app} wird installiert...",
"app_not_upgraded": "Die App '{failed_app}' konnte nicht aktualisiert werden. Infolgedessen wurden die folgenden App-Upgrades abgebrochen: {apps}", "app_not_upgraded": "Die App '{failed_app}' konnte nicht aktualisiert werden. Infolgedessen wurden die folgenden App-Upgrades abgebrochen: {apps}",
"app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der App \"{other_app}\" verwendet", "app_make_default_location_already_used": "Die App \"{app}\" kann nicht als Standard für die Domain \"{domain}\" festgelegt werden. Sie wird bereits von der App \"{other_app}\" verwendet",
"aborting": "Breche ab.", "aborting": "Breche ab.",
@ -300,7 +300,7 @@
"app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation auf einer eigenen Domain, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist.", "app_full_domain_unavailable": "Es tut uns leid, aber diese Anwendung erfordert die Installation auf einer eigenen Domain, aber einige andere Anwendungen sind bereits auf der Domäne'{domain}' installiert. Eine mögliche Lösung ist das Hinzufügen und Verwenden einer Subdomain, die dieser Anwendung zugeordnet ist.",
"app_install_failed": "Installation von {app} fehlgeschlagen: {error}", "app_install_failed": "Installation von {app} fehlgeschlagen: {error}",
"app_install_script_failed": "Im Installationsscript ist ein Fehler aufgetreten", "app_install_script_failed": "Im Installationsscript ist ein Fehler aufgetreten",
"app_remove_after_failed_install": "Entfernen der App nach fehlgeschlagener Installation", "app_remove_after_failed_install": "Entfernen der App nach fehlgeschlagener Installation...",
"app_upgrade_script_failed": "Es ist ein Fehler im App-Upgrade-Skript aufgetreten", "app_upgrade_script_failed": "Es ist ein Fehler im App-Upgrade-Skript aufgetreten",
"diagnosis_basesystem_host": "Server läuft unter Debian {debian_version}", "diagnosis_basesystem_host": "Server läuft unter Debian {debian_version}",
"diagnosis_basesystem_kernel": "Server läuft unter Linux-Kernel {kernel_version}", "diagnosis_basesystem_kernel": "Server läuft unter Linux-Kernel {kernel_version}",
@ -329,14 +329,78 @@
"diagnosis_found_errors_and_warnings": "Habe {errors} erhebliche(s) Problem(e) (und {warnings} Warnung(en)) in Verbindung mit {category} gefunden!", "diagnosis_found_errors_and_warnings": "Habe {errors} erhebliche(s) Problem(e) (und {warnings} Warnung(en)) in Verbindung mit {category} gefunden!",
"diagnosis_ip_broken_dnsresolution": "Domänen-Namens-Auflösung scheint aus einem bestimmten Grund nicht zu funktionieren... Blockiert eine Firewall die DNS Anfragen?", "diagnosis_ip_broken_dnsresolution": "Domänen-Namens-Auflösung scheint aus einem bestimmten Grund nicht zu funktionieren... Blockiert eine Firewall die DNS Anfragen?",
"diagnosis_ip_broken_resolvconf": "Domänen-Namens-Auflösung scheint nicht zu funktionieren, was daran liegen könnte, dass in <code>/etc/resolv.conf</> kein Eintrag auf <code>127.0.0.1</code> zeigt.", "diagnosis_ip_broken_resolvconf": "Domänen-Namens-Auflösung scheint nicht zu funktionieren, was daran liegen könnte, dass in <code>/etc/resolv.conf</> kein Eintrag auf <code>127.0.0.1</code> zeigt.",
"diagnosis_ip_weird_resolvconf_details": "Stattdessen sollte diese Datei ein Softlink auf /etc/resolvconf/run/resolv.conf sein, die auf sich selbst zu 127.0.0.1 zeigt (dnsmasq). Der eigentlich Auflösende sollte in /etc/resolv.dnsmasq.conf konfiguriert werden.", "diagnosis_ip_weird_resolvconf_details": "Die Datei <code>/etc/resolv.conf</code> muss ein Symlink auf <code>/etc/resolvconf/run/resolv.conf</code> sein, welcher auf <code>127.0.0.1</code> (dnsmasq) zeigt. Falls Sie die DNS-Resolver manuell konfigurieren möchten, bearbeiten Sie bitte <code>/etc/resolv.dnsmasq.conf</code>.",
"diagnosis_dns_good_conf": "Gute DNS Konfiguration für Domäne {domain} (Kategorie {category})", "diagnosis_dns_good_conf": "Die DNS-Einträge für die Domäne {domain} (Kategorie {category}) sind korrekt konfiguriert",
"diagnosis_ignored_issues": "(+ {nb_ignored} ignorierte(s) Problem(e))", "diagnosis_ignored_issues": "(+ {nb_ignored} ignorierte(s) Problem(e))",
"diagnosis_basesystem_hardware": "Server Hardware Architektur ist {virt} {arch}", "diagnosis_basesystem_hardware": "Server Hardware Architektur ist {virt} {arch}",
"diagnosis_basesystem_hardware_board": "Server Platinen Modell ist {model}", "diagnosis_basesystem_hardware_board": "Server Platinen Modell ist {model}",
"diagnosis_found_errors": "Habe {errors} erhebliche(s) Problem(e) in Verbindung mit {category} gefunden!", "diagnosis_found_errors": "Habe {errors} erhebliche(s) Problem(e) in Verbindung mit {category} gefunden!",
"diagnosis_found_warnings": "Habe {warnings} Ding(e) gefunden, die verbessert werden könnten für {category}.", "diagnosis_found_warnings": "Habe {warnings} Ding(e) gefunden, die verbessert werden könnten für {category}.",
"diagnosis_ip_dnsresolution_working": "Domänen-Namens-Auflösung funktioniert!", "diagnosis_ip_dnsresolution_working": "Domänen-Namens-Auflösung funktioniert!",
"diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber sei vorsichtig wenn du eine eigene <code>/etc/resolv.conf</code> verwendest.", "diagnosis_ip_weird_resolvconf": "DNS Auflösung scheint zu funktionieren, aber seien Sie vorsichtig wenn Sie eine eigene <code>/etc/resolv.conf</code> verwendest.",
"diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, kannst Du zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen." "diagnosis_display_tip": "Um die gefundenen Probleme zu sehen, können Sie zum Diagnose-Bereich des webadmin gehen, oder 'yunohost diagnosis show --issues' in der Kommandozeile ausführen.",
"backup_archive_corrupted": "Das Backup-Archiv '{archive}' scheint beschädigt: {error}",
"backup_archive_cant_retrieve_info_json": "Die Informationen für das Archiv '{archive}' konnten nicht geladen werden... Die Datei info.json wurde nicht gefunden (oder ist kein gültiges json).",
"app_packaging_format_not_supported": "Diese App kann nicht installiert werden da das Paketformat nicht von der YunoHost-Version unterstützt wird. Denken Sie darüber nach das System zu aktualisieren.",
"certmanager_domain_not_diagnosed_yet": "Für {domain} gibt es noch keine Diagnose-Resultate. Bitte wiederholen Sie die Diagnose für die Kategorien 'DNS records' und 'Web' im Diagnose-Bereich um zu überprüfen ob die Domain für Let's Encrypt bereit ist. (Oder wenn Sie wissen was Sie tun, verwenden Sie '--no-checks' um diese Überprüfungen abzuschalten.",
"migration_0015_patching_sources_list": "sources.lists wird repariert...",
"migration_0015_start": "Start der Migration auf Buster",
"migration_0011_failed_to_remove_stale_object": "Abgelaufenes Objekt konne nicht entfernt werden. {dn}: {error}",
"migration_0011_update_LDAP_schema": "Das LDAP-Schema aktualisieren...",
"migration_0011_update_LDAP_database": "Die LDAP-Datenbank aktualisieren...",
"migration_0011_migrate_permission": "Berechtigungen der Applikationen von den Einstellungen zu LDAP migrieren...",
"migration_0011_LDAP_update_failed": "LDAP konnte nicht aktualisiert werden. Fehler:n{error:s}",
"migration_0011_create_group": "Eine Gruppe für jeden Benutzer erstellen…",
"migration_description_0015_migrate_to_buster": "Auf Debian Buster und YunoHost 4.x upgraden",
"mail_unavailable": "Diese E-Mail Adresse ist reserviert und wird dem ersten Benutzer automatisch zugewiesen",
"diagnosis_services_conf_broken": "Die Konfiguration für den Dienst {service} ist fehlerhaft!",
"diagnosis_services_running": "Dienst {service} läuft!",
"diagnosis_domain_expires_in": "{domain} läuft in {days} Tagen ab.",
"diagnosis_domain_expiration_error": "Einige Domänen werden SEHR BALD ablaufen!",
"diagnosis_domain_expiration_success": "Deine Domänen sind registriert und werden in nächster Zeit nicht ablaufen.",
"diagnosis_domain_not_found_details": "Die Domäne {domain} existiert nicht in der WHOIS-Datenbank oder sie ist abgelaufen!",
"diagnosis_domain_expiration_not_found": "Konnte die Ablaufdaten für einige Domänen nicht überprüfen.",
"diagnosis_dns_try_dyndns_update_force": "Die DNS-Konfiguration dieser Domäne sollte automatisch von Yunohost verwaltet werden. Andernfalls können Sie mittels <cmd>yunohost dyndns update --force</cmd> ein Update erzwingen.",
"diagnosis_dns_point_to_doc": "Bitte schauen Sie in die Dokumentation unter <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> wenn Sie Hilfe bei der Konfiguration der DNS-Einträge brauchen.",
"diagnosis_dns_discrepancy": "Der folgende DNS-Eintrag scheint nicht den empfohlenen Einstellungen zu entsprechen: <br>Typ: <code>{type}</code><br>Name: <code>{name}</code><br> Aktueller Wert: <code>{current}</code><br> Erwarteter Wert: <code>{value}</code>",
"diagnosis_dns_missing_record": "Gemäß der empfohlenen DNS-Konfiguration sollten Sie einen DNS-Eintrag mit den folgenden Informationen hinzufügen.<br>Typ: <code>{type}</code><br>Name: <code>{name}</code><br>Wert: <code>{value}</code>",
"diagnosis_dns_bad_conf": "Einige DNS-Einträge für die Domäne {domain} fehlen oder sind nicht korrekt (Kategorie {category})",
"diagnosis_ip_local": "Lokale IP: <code>{local}</code>",
"diagnosis_ip_global": "Globale IP: <code>{global}</code>",
"diagnosis_ip_no_ipv6_tip": "Die Verwendung von IPv6 ist nicht Voraussetzung für das Funktionieren Ihres Servers, trägt aber zur Gesundheit des Internet als Ganzes bei. IPv6 sollte normalerweise automatisch von Ihrem Server oder Ihrem Provider konfiguriert werden, sofern verfügbar. Andernfalls müßen Sie einige Dinge manuell konfigurieren. Weitere Informationen finden Sie hier: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. Wenn Sie IPv6 nicht aktivieren können oder Ihnen das zu technisch ist, können Sie diese Warnung gefahrlos ignorieren.",
"diagnosis_services_bad_status_tip": "Sie können versuchen, <a href='#/services/{service}'>den Dienst neu zu starten</a>, und wenn das nicht funktioniert, schauen Sie sich <a href='#/services/{service}'>die (Dienst-)Logs in der Verwaltung</a> an (In der Kommandozeile können Sie dies mit <cmd>yunohost service restart {service}</cmd> und <cmd>yunohost service log {service}</cmd> tun).",
"diagnosis_services_bad_status": "Der Dienst {service} ist {status} :(",
"diagnosis_diskusage_verylow": "Der Speicher <code>{mountpoint}</code> (auf Gerät <code>{device}</code>) hat nur noch {free} ({free_percent}%) freien Speicherplatz (von ingesamt {total}). Sie sollten sich ernsthaft überlegen, einigen Seicherplatz frei zu machen!",
"diagnosis_http_ok": "Die Domäne {domain} ist über HTTP von außerhalb des lokalen Netzwerks erreichbar.",
"diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Einige Hosting-Anbieter werden es Ihnen nicht gestatten, den ausgehenden Port 25 zu öffnen, da diese sich nicht um die Netzneutralität kümmern.<br>- Einige davon bieten als Alternative an, <a href='https://yunohost.org/#/smtp_relay'>ein Mailserver-Relay zu verwenden</a>, was jedoch bedeutet, dass das Relay Ihren E-Mail-Verkehr ausspionieren kann.<br>- Eine die Privatsphäre berücksichtigende Alternative ist die Verwendung eines VPN *mit einer dedizierten öffentlichen IP* um solche Einschränkungen zu umgehen. Schauen Sie unter <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a> nach.<br>- Sie können auch in Betracht ziehen, zu einem <a href='https://yunohost.org/#/isp'>netzneutralitätfreundlicheren Anbieter</a> zu wechseln.",
"diagnosis_http_timeout": "Wartezeit wurde beim Versuch, von außen eine Verbindung zum Server aufzubauen, überschritten. Er scheint nicht erreichbar zu sein.<br>1. Die häufigste Ursache für dieses Problem ist daß der Port 80 (und 433) <a href='https://yunohost.org/isp_box_config'>nicht richtig zu Ihrem Server weitergeleitet werden</a>.<br>2. Sie sollten auch sicherstellen, daß der Dienst nginx läuft.<br>3. In komplexeren Umgebungen: Stellen Sie sicher, daß keine Firewall oder Reverse-Proxy stört.",
"service_reloaded_or_restarted": "Der Dienst '{service:s}' wurde erfolgreich neu geladen oder gestartet",
"service_restarted": "Der Dienst '{service:s}' wurde neu gestartet",
"service_regen_conf_is_deprecated": "'yunohost service regen-conf' ist veraltet! Bitte verwenden Sie stattdessen 'yunohost tools regen-conf'.",
"certmanager_warning_subdomain_dns_record": "Die Subdomain '{subdomain:s}' löst nicht dieselbe IP wie '{domain:s} auf. Einige Funktionen werden nicht verfügbar sein, solange Sie dies nicht beheben und das Zertifikat erneuern.",
"diagnosis_ports_ok": "Port {port} ist von außen erreichbar.",
"diagnosis_ram_verylow": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total})",
"diagnosis_mail_outgoing_port_25_blocked_details": "Sie sollten zuerst versuchen den ausgehenden Port 25 auf Ihrer Router-Konfigurationsoberfläche oder Ihrer Hosting-Anbieter-Konfigurationsoberfläche zu öffnen. (Bei einigen Hosting-Anbieter kann es sein, daß Sie verlangen, daß man dafür ein Support-Ticket sendet).",
"diagnosis_mail_ehlo_ok": "Der SMTP-Server ist von von außen erreichbar und darum auch in der Lage E-Mails zu empfangen!",
"diagnosis_mail_ehlo_bad_answer": "Ein nicht-SMTP-Dienst antwortete auf Port 25 per IPv{ipversion}",
"diagnosis_swap_notsomuch": "Das System hat nur {total} Swap. Sie sollten sich überlegen mindestens {recommended} an Swap einzurichten, um Situationen zu verhindern, in welchen der RAM des Systems knapp wird.",
"diagnosis_swap_ok": "Das System hat {total} Swap!",
"diagnosis_swap_tip": "Wir sind Ihnen sehr dankbar dafür, daß Sie behutsam und sich bewußt sind, dass das Betreiben einer Swap-Partition auf einer SD-Karte oder einem SSD-Speicher das Risiko einer drastischen Verkürzung der Lebenserwartung dieser Platte nach sich zieht.",
"diagnosis_mail_outgoing_port_25_ok": "Der SMTP-Server ist in der Lage E-Mails zu versenden (der ausgehende Port 25 ist nicht blockiert).",
"diagnosis_mail_outgoing_port_25_blocked": "Der SMTP-Server kann keine E-Mails an andere Server senden, weil der ausgehende Port 25 per IPv{ipversion} blockiert ist. Sie können versuchen diesen in der Konfigurations-Oberfläche Ihres Internet-Anbieters (oder Hosters) zu öffnen.",
"diagnosis_mail_ehlo_unreachable": "Der SMTP-Server ist von außen nicht erreichbar per IPv{ipversion}. Er wird nicht in der Lage sein E-Mails zu empfangen.",
"diagnosis_diskusage_low": "Der Speicher <code>{mountpoint}</code> (auf Gerät <code>{device}</code>) hat nur noch {free} ({free_percent}%) freien Speicherplatz (von insgesamt {total}). Seien Sie vorsichtig.",
"diagnosis_ram_low": "Das System hat nur {available} ({available_percent}%) RAM zur Verfügung! (von insgesamt {total}). Seien Sie vorsichtig.",
"service_reload_or_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut geladen oder gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}",
"diagnosis_domain_expiration_not_found_details": "Die WHOIS-Informationen für die Domäne {domain} scheint keine Informationen über das Ablaufdatum zu enthalten.",
"diagnosis_domain_expiration_warning": "Einige Domänen werden bald ablaufen!",
"diagnosis_diskusage_ok": "Der Speicher <code>{mountpoint}</code> (auf Gerät <code>{device}</code>) hat immer noch {free} ({free_percent}%) freien Speicherplatz übrig(von insgesamt {total})!",
"diagnosis_ram_ok": "Das System hat immer noch {available} ({available_percent}%) RAM zu Verfügung von {total}.",
"diagnosis_swap_none": "Das System hat gar keinen Swap. Sie sollten sich überlegen mindestens {recommended} an Swap einzurichten, um Situationen zu verhindern, in welchen der RAM des Systems knapp wird.",
"diagnosis_mail_ehlo_unreachable_details": "Konnte keine Verbindung zu Ihrem Server auf dem Port 25 herzustellen per IPv{ipversion}. Er scheint nicht erreichbar zu sein.<br>1. Das häufigste Problem ist, dass der Port 25 <a href='https://yunohost.org/isp_box_config'>nicht richtig zu Ihrem Server weitergeleitet ist</a>.<br>2. Sie sollten auch sicherstellen, dass der Postfix-Dienst läuft.<br>3. In komplexeren Umgebungen: Stellen Sie sicher, daß keine Firewall oder Reverse-Proxy stört.",
"diagnosis_mail_ehlo_wrong": "Ein anderer SMTP-Server antwortet auf IPv{ipversion}. Ihr Server wird wahrscheinlich nicht in der Lage sein, E-Mails zu empfangen.",
"migration_description_0018_xtable_to_nftable": "Alte Netzwerkverkehrsregeln zum neuen nftable-System migrieren",
"service_reload_failed": "Der Dienst '{service:s}' konnte nicht erneut geladen werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}",
"service_reloaded": "Der Dienst '{service:s}' wurde erneut geladen",
"service_restart_failed": "Der Dienst '{service:s}' konnte nicht erneut gestartet werden.\n\nKürzlich erstellte Logs des Dienstes: {logs:s}"
} }

View file

@ -28,35 +28,40 @@
"app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, '{domain}' is already in use by the other app '{other_app}'", "app_make_default_location_already_used": "Can't make the app '{app}' the default on the domain, '{domain}' is already in use by the other app '{other_app}'",
"app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}", "app_location_unavailable": "This URL is either unavailable, or conflicts with the already installed app(s):\n{apps:s}",
"app_manifest_invalid": "Something is wrong with the app manifest: {error}", "app_manifest_invalid": "Something is wrong with the app manifest: {error}",
"app_manifest_install_ask_domain": "Choose the domain where this app should be installed",
"app_manifest_install_ask_path": "Choose the path where this app should be installed",
"app_manifest_install_ask_password": "Choose an administration password for this app",
"app_manifest_install_ask_admin": "Choose an administrator user for this app",
"app_manifest_install_ask_is_public": "Should this app be exposed to anonymous visitors?",
"app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps' upgrades have been cancelled: {apps}", "app_not_upgraded": "The app '{failed_app}' failed to upgrade, and as a consequence the following apps' upgrades have been cancelled: {apps}",
"app_not_correctly_installed": "{app:s} seems to be incorrectly installed", "app_not_correctly_installed": "{app:s} seems to be incorrectly installed",
"app_not_installed": "Could not find the app '{app:s}' in the list of installed apps: {all_apps}", "app_not_installed": "Could not find {app:s} in the list of installed apps: {all_apps}",
"app_not_properly_removed": "{app:s} has not been properly removed", "app_not_properly_removed": "{app:s} has not been properly removed",
"app_removed": "{app:s} removed", "app_removed": "{app:s} removed",
"app_requirements_checking": "Checking required packages for {app}", "app_requirements_checking": "Checking required packages for {app}...",
"app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}", "app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}",
"app_remove_after_failed_install": "Removing the app following the installation failure", "app_remove_after_failed_install": "Removing the app following the installation failure...",
"app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?", "app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?",
"app_start_install": "Installing the app '{app}'…", "app_start_install": "Installing {app}...",
"app_start_remove": "Removing the app '{app}'…", "app_start_remove": "Removing {app}...",
"app_start_backup": "Collecting files to be backed up for the app '{app}'…", "app_start_backup": "Collecting files to be backed up for {app}...",
"app_start_restore": "Restoring the app '{app}'…", "app_start_restore": "Restoring {app}...",
"app_unknown": "Unknown app", "app_unknown": "Unknown app",
"app_unsupported_remote_type": "Unsupported remote type used for the app", "app_unsupported_remote_type": "Unsupported remote type used for the app",
"app_upgrade_several_apps": "The following apps will be upgraded: {apps}", "app_upgrade_several_apps": "The following apps will be upgraded: {apps}",
"app_upgrade_app_name": "Now upgrading {app}", "app_upgrade_app_name": "Now upgrading {app}...",
"app_upgrade_failed": "Could not upgrade {app:s}: {error}", "app_upgrade_failed": "Could not upgrade {app:s}: {error}",
"app_upgrade_script_failed": "An error occurred inside the app upgrade script", "app_upgrade_script_failed": "An error occurred inside the app upgrade script",
"app_upgrade_some_app_failed": "Some apps could not be upgraded", "app_upgrade_some_app_failed": "Some apps could not be upgraded",
"app_upgraded": "{app:s} upgraded", "app_upgraded": "{app:s} upgraded",
"app_packaging_format_not_supported": "This app cannot be installed because its packaging format is not supported by your Yunohost version. You should probably consider upgrading your system.", "app_packaging_format_not_supported": "This app cannot be installed because its packaging format is not supported by your YunoHost version. You should probably consider upgrading your system.",
"apps_already_up_to_date": "All apps are already up-to-date", "apps_already_up_to_date": "All apps are already up-to-date",
"apps_catalog_init_success": "App catalog system initialized!", "apps_catalog_init_success": "App catalog system initialized!",
"apps_catalog_updating": "Updating application catalog…", "apps_catalog_updating": "Updating application catalog…",
"apps_catalog_failed_to_download": "Unable to download the {apps_catalog} app catalog: {error}", "apps_catalog_failed_to_download": "Unable to download the {apps_catalog} app catalog: {error}",
"apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.", "apps_catalog_obsolete_cache": "The app catalog cache is empty or obsolete.",
"apps_catalog_update_success": "The application catalog has been updated!", "apps_catalog_update_success": "The application catalog has been updated!",
"ask_email": "E-mail address", "ask_user_domain": "Domain to use for the user's email address and XMPP account",
"ask_firstname": "First name", "ask_firstname": "First name",
"ask_lastname": "Last name", "ask_lastname": "Last name",
"ask_main_domain": "Main domain", "ask_main_domain": "Main domain",
@ -65,13 +70,12 @@
"ask_new_path": "New path", "ask_new_path": "New path",
"ask_password": "Password", "ask_password": "Password",
"backup_abstract_method": "This backup method has yet to be implemented", "backup_abstract_method": "This backup method has yet to be implemented",
"backup_actually_backuping": "Creating a backup archive from the collected files…", "backup_actually_backuping": "Creating a backup archive from the collected files...",
"backup_app_failed": "Could not back up the app '{app:s}'", "backup_app_failed": "Could not back up {app:s}",
"backup_applying_method_borg": "Sending all files to backup into borg-backup repository…", "backup_applying_method_copy": "Copying all files to backup...",
"backup_applying_method_copy": "Copying all files to backup…", "backup_applying_method_custom": "Calling the custom backup method '{method:s}'...",
"backup_applying_method_custom": "Calling the custom backup method '{method:s}'…", "backup_applying_method_tar": "Creating the backup TAR archive...",
"backup_applying_method_tar": "Creating the backup TAR archive…", "backup_archive_app_not_found": "Could not find {app:s} in the backup archive",
"backup_archive_app_not_found": "Could not find the app '{app:s}' in the backup archive",
"backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})", "backup_archive_broken_link": "Could not access the backup archive (broken link to {path:s})",
"backup_archive_name_exists": "A backup archive with this name already exists.", "backup_archive_name_exists": "A backup archive with this name already exists.",
"backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'", "backup_archive_name_unknown": "Unknown local backup archive named '{name:s}'",
@ -81,7 +85,6 @@
"backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup", "backup_archive_system_part_not_available": "System part '{part:s}' unavailable in this backup",
"backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'", "backup_archive_writing_error": "Could not add the files '{source:s}' (named in the archive '{dest:s}') to be backed up into the compressed archive '{archive:s}'",
"backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s}MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)", "backup_ask_for_copying_if_needed": "Do you want to perform the backup using {size:s}MB temporarily? (This way is used since some files could not be prepared using a more efficient method.)",
"backup_borg_not_implemented": "The Borg backup method is not yet implemented",
"backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected", "backup_cant_mount_uncompress_archive": "Could not mount the uncompressed archive as write protected",
"backup_cleaning_failed": "Could not clean up the temporary backup folder", "backup_cleaning_failed": "Could not clean up the temporary backup folder",
"backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive", "backup_copying_to_organize_the_archive": "Copying {size:s}MB to organize the archive",
@ -95,24 +98,22 @@
"backup_delete_error": "Could not delete '{path:s}'", "backup_delete_error": "Could not delete '{path:s}'",
"backup_deleted": "Backup deleted", "backup_deleted": "Backup deleted",
"backup_hook_unknown": "The backup hook '{hook:s}' is unknown", "backup_hook_unknown": "The backup hook '{hook:s}' is unknown",
"backup_method_borg_finished": "Backup into Borg finished",
"backup_method_copy_finished": "Backup copy finalized", "backup_method_copy_finished": "Backup copy finalized",
"backup_method_custom_finished": "Custom backup method '{method:s}' finished", "backup_method_custom_finished": "Custom backup method '{method:s}' finished",
"backup_method_tar_finished": "TAR backup archive created", "backup_method_tar_finished": "TAR backup archive created",
"backup_mount_archive_for_restore": "Preparing archive for restoration", "backup_mount_archive_for_restore": "Preparing archive for restoration...",
"backup_no_uncompress_archive_dir": "There is no such uncompressed archive directory", "backup_no_uncompress_archive_dir": "There is no such uncompressed archive directory",
"backup_nothings_done": "Nothing to save", "backup_nothings_done": "Nothing to save",
"backup_output_directory_forbidden": "Pick a different output directory. Backups cannot be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders", "backup_output_directory_forbidden": "Pick a different output directory. Backups cannot be created in /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var or /home/yunohost.backup/archives sub-folders",
"backup_output_directory_not_empty": "You should pick an empty output directory", "backup_output_directory_not_empty": "You should pick an empty output directory",
"backup_output_directory_required": "You must provide an output directory for the backup", "backup_output_directory_required": "You must provide an output directory for the backup",
"backup_output_symlink_dir_broken": "Your archive directory '{path:s}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.", "backup_output_symlink_dir_broken": "Your archive directory '{path:s}' is a broken symlink. Maybe you forgot to re/mount or plug in the storage medium it points to.",
"backup_permission": "Backup permission for app {app:s}", "backup_permission": "Backup permission for {app:s}",
"backup_php5_to_php7_migration_may_fail": "Could not convert your archive to support PHP 7, you may be unable to restore your PHP apps (reason: {error:s})", "backup_running_hooks": "Running backup hooks...",
"backup_running_hooks": "Running backup hooks…",
"backup_system_part_failed": "Could not backup the '{part:s}' system part", "backup_system_part_failed": "Could not backup the '{part:s}' system part",
"backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive", "backup_unable_to_organize_files": "Could not use the quick method to organize files in the archive",
"backup_with_no_backup_script_for_app": "The app '{app:s}' has no backup script. Ignoring.", "backup_with_no_backup_script_for_app": "The app '{app:s}' has no backup script. Ignoring.",
"backup_with_no_restore_script_for_app": "The '{app:s}' has no restoration script, you will not be able to automatically restore the backup of this app.", "backup_with_no_restore_script_for_app": "{app:s} has no restoration script, you will not be able to automatically restore the backup of this app.",
"certmanager_acme_not_configured_for_domain": "The ACME challenge cannot be ran for {domain} right now because its nginx conf lacks the corresponding code snippet... Please make sure that your nginx configuration is up to date using `yunohost tools regen-conf nginx --dry-run --with-diff`.", "certmanager_acme_not_configured_for_domain": "The ACME challenge cannot be ran for {domain} right now because its nginx conf lacks the corresponding code snippet... Please make sure that your nginx configuration is up to date using `yunohost tools regen-conf nginx --dry-run --with-diff`.",
"certmanager_attempt_to_renew_nonLE_cert": "The certificate for the domain '{domain:s}' is not issued by Let's Encrypt. Cannot renew it automatically!", "certmanager_attempt_to_renew_nonLE_cert": "The certificate for the domain '{domain:s}' is not issued by Let's Encrypt. Cannot renew it automatically!",
"certmanager_attempt_to_renew_valid_cert": "The certificate for the domain '{domain:s}' is not about to expire! (You may use --force if you know what you're doing)", "certmanager_attempt_to_renew_valid_cert": "The certificate for the domain '{domain:s}' is not about to expire! (You may use --force if you know what you're doing)",
@ -122,9 +123,9 @@
"certmanager_cert_install_success_selfsigned": "Self-signed certificate now installed for the domain '{domain:s}'", "certmanager_cert_install_success_selfsigned": "Self-signed certificate now installed for the domain '{domain:s}'",
"certmanager_cert_renew_success": "Let's Encrypt certificate renewed for the domain '{domain:s}'", "certmanager_cert_renew_success": "Let's Encrypt certificate renewed for the domain '{domain:s}'",
"certmanager_cert_signing_failed": "Could not sign the new certificate", "certmanager_cert_signing_failed": "Could not sign the new certificate",
"certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work", "certmanager_certificate_fetching_or_enabling_failed": "Trying to use the new certificate for {domain:s} did not work...",
"certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted—please try again later.", "certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted—please try again later.",
"certmanager_domain_not_diagnosed_yet": "There is no diagnosis result for domain %s yet. Please re-run a diagnosis for categories 'DNS records' and 'Web' in the diagnosis section to check if the domain is ready for Let's Encrypt. (Or if you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_not_diagnosed_yet": "There is no diagnosis result for domain {domain} yet. Please re-run a diagnosis for categories 'DNS records' and 'Web' in the diagnosis section to check if the domain is ready for Let's Encrypt. (Or if you know what you are doing, use '--no-checks' to turn off those checks.)",
"certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use '--force' to do so.)", "certmanager_domain_cert_not_selfsigned": "The certificate for domain {domain:s} is not self-signed. Are you sure you want to replace it? (Use '--force' to do so.)",
"certmanager_domain_dns_ip_differs_from_public_ip": "The DNS records for domain '{domain:s}' is different from this server's IP. Please check the 'DNS records' (basic) category in the diagnosis for more info. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_dns_ip_differs_from_public_ip": "The DNS records for domain '{domain:s}' is different from this server's IP. Please check the 'DNS records' (basic) category in the diagnosis for more info. If you recently modified your A record, please wait for it to propagate (some DNS propagation checkers are available online). (If you know what you are doing, use '--no-checks' to turn off those checks.)",
"certmanager_domain_http_not_working": "Domain {domain:s} does not seem to be accessible through HTTP. Please check the 'Web' category in the diagnosis for more info. (If you know what you are doing, use '--no-checks' to turn off those checks.)", "certmanager_domain_http_not_working": "Domain {domain:s} does not seem to be accessible through HTTP. Please check the 'Web' category in the diagnosis for more info. (If you know what you are doing, use '--no-checks' to turn off those checks.)",
@ -172,8 +173,9 @@
"diagnosis_dns_good_conf": "DNS records are correctly configured for domain {domain} (category {category})", "diagnosis_dns_good_conf": "DNS records are correctly configured for domain {domain} (category {category})",
"diagnosis_dns_bad_conf": "Some DNS records are missing or incorrect for domain {domain} (category {category})", "diagnosis_dns_bad_conf": "Some DNS records are missing or incorrect for domain {domain} (category {category})",
"diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.<br>Type: <code>{type}</code><br>Name: <code>{name}</code><br>Value: <code>{value}</code>", "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.<br>Type: <code>{type}</code><br>Name: <code>{name}</code><br>Value: <code>{value}</code>",
"diagnosis_dns_discrepancy": "The following DNS record does not seem to follow the recommended configuration:<br>Type: <code>{type}</code><br>Name: <code>{name}</code><br>Current value: <code>{current}</code><br>Excepted value: <code>{value}</code>", "diagnosis_dns_discrepancy": "The following DNS record does not seem to follow the recommended configuration:<br>Type: <code>{type}</code><br>Name: <code>{name}</code><br>Current value: <code>{current}</code><br>Expected value: <code>{value}</code>",
"diagnosis_dns_point_to_doc": "Please check the documentation at <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> if you need help about configuring DNS records.", "diagnosis_dns_point_to_doc": "Please check the documentation at <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> if you need help about configuring DNS records.",
"diagnosis_dns_try_dyndns_update_force": "This domain's DNS configuration should automatically be managed by Yunohost. If that's not the case, you can try to force an update using <cmd>yunohost dyndns update --force</cmd>.",
"diagnosis_domain_expiration_not_found": "Unable to check the expiration date for some domains", "diagnosis_domain_expiration_not_found": "Unable to check the expiration date for some domains",
"diagnosis_domain_not_found_details": "The domain {domain} doesn't exist in WHOIS database or is expired!", "diagnosis_domain_not_found_details": "The domain {domain} doesn't exist in WHOIS database or is expired!",
"diagnosis_domain_expiration_not_found_details": "The WHOIS information for domain {domain} doesn't seem to contain the information about the expiration date?", "diagnosis_domain_expiration_not_found_details": "The WHOIS information for domain {domain} doesn't seem to contain the information about the expiration date?",
@ -258,6 +260,7 @@
"diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> and if you're ok, apply the changes with <cmd>yunohost tools regen-conf nginx --force</cmd>.", "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> and if you're ok, apply the changes with <cmd>yunohost tools regen-conf nginx --force</cmd>.",
"diagnosis_unknown_categories": "The following categories are unknown: {categories}", "diagnosis_unknown_categories": "The following categories are unknown: {categories}",
"diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.", "diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.",
"diagnosis_processes_killed_by_oom_reaper": "Some processes were recently killed by the system because it ran out of memory. This is typically symptomatic of a lack of memory on the system or of a process that ate up to much memory. Summary of the processes killed:\n{kills_summary}",
"domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n <another-domain>'; here is the list of candidate domains: {other_domains:s}", "domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n <another-domain>'; here is the list of candidate domains: {other_domains:s}",
"domain_cannot_add_xmpp_upload": "You cannot add domains starting with 'xmpp-upload.'. This kind of name is reserved for the XMPP upload feature integrated in YunoHost.", "domain_cannot_add_xmpp_upload": "You cannot add domains starting with 'xmpp-upload.'. This kind of name is reserved for the XMPP upload feature integrated in YunoHost.",
"domain_cannot_remove_main_add_new_one": "You cannot remove '{domain:s}' since it's the main domain and your only domain, you need to first add another domain using 'yunohost domain add <another-domain.com>', then set is as the main domain using 'yunohost domain main-domain -n <another-domain.com>' and then you can remove the domain '{domain:s}' using 'yunohost domain remove {domain:s}'.'", "domain_cannot_remove_main_add_new_one": "You cannot remove '{domain:s}' since it's the main domain and your only domain, you need to first add another domain using 'yunohost domain add <another-domain.com>', then set is as the main domain using 'yunohost domain main-domain -n <another-domain.com>' and then you can remove the domain '{domain:s}' using 'yunohost domain remove {domain:s}'.'",
@ -285,7 +288,7 @@
"dyndns_cron_removed": "DynDNS cron job removed", "dyndns_cron_removed": "DynDNS cron job removed",
"dyndns_ip_update_failed": "Could not update IP address to DynDNS", "dyndns_ip_update_failed": "Could not update IP address to DynDNS",
"dyndns_ip_updated": "Updated your IP on DynDNS", "dyndns_ip_updated": "Updated your IP on DynDNS",
"dyndns_key_generating": "Generating DNS key It may take a while.", "dyndns_key_generating": "Generating DNS key... It may take a while.",
"dyndns_key_not_found": "DNS key not found for the domain", "dyndns_key_not_found": "DNS key not found for the domain",
"dyndns_no_domain_registered": "No domain registered with DynDNS", "dyndns_no_domain_registered": "No domain registered with DynDNS",
"dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.", "dyndns_provider_unreachable": "Unable to reach DynDNS provider {provider}: either your YunoHost is not correctly connected to the internet or the dynette server is down.",
@ -293,9 +296,9 @@
"dyndns_registration_failed": "Could not register DynDNS domain: {error:s}", "dyndns_registration_failed": "Could not register DynDNS domain: {error:s}",
"dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.", "dyndns_domain_not_provided": "DynDNS provider {provider:s} cannot provide domain {domain:s}.",
"dyndns_unavailable": "The domain '{domain:s}' is unavailable.", "dyndns_unavailable": "The domain '{domain:s}' is unavailable.",
"executing_command": "Executing command '{command:s}'", "executing_command": "Executing command '{command:s}'...",
"executing_script": "Executing script '{script:s}'", "executing_script": "Executing script '{script:s}'...",
"extracting": "Extracting", "extracting": "Extracting...",
"experimental_feature": "Warning: This feature is experimental and not considered stable, you should not use it unless you know what you are doing.", "experimental_feature": "Warning: This feature is experimental and not considered stable, you should not use it unless you know what you are doing.",
"field_invalid": "Invalid field '{:s}'", "field_invalid": "Invalid field '{:s}'",
"file_does_not_exist": "The file {path:s} does not exist.", "file_does_not_exist": "The file {path:s} does not exist.",
@ -310,10 +313,6 @@
"global_settings_key_doesnt_exists": "The key '{settings_key:s}' does not exist in the global settings, you can see all the available keys by running 'yunohost settings list'", "global_settings_key_doesnt_exists": "The key '{settings_key:s}' does not exist in the global settings, you can see all the available keys by running 'yunohost settings list'",
"global_settings_reset_success": "Previous settings now backed up to {path:s}", "global_settings_reset_success": "Previous settings now backed up to {path:s}",
"global_settings_setting_pop3_enabled": "Enable the POP3 protocol for the mail server", "global_settings_setting_pop3_enabled": "Enable the POP3 protocol for the mail server",
"global_settings_setting_example_bool": "Example boolean option",
"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_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_admin_strength": "Admin password strength",
"global_settings_setting_security_password_user_strength": "User password strength", "global_settings_setting_security_password_user_strength": "User password strength",
@ -322,12 +321,13 @@
"global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard it and save it in /etc/yunohost/settings-unknown.json", "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard 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_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration",
"global_settings_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail", "global_settings_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail",
"global_settings_setting_backup_compress_tar_archives": "When creating new backups, compress the archives (.tar.gz) instead of uncompressed archives (.tar). N.B. : enabling this option means create lighter backup archives, but the initial backup procedure will be significantly longer and heavy on CPU.",
"global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is 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 long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).",
"good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).",
"group_already_exist": "Group {group} already exists", "group_already_exist": "Group {group} already exists",
"group_already_exist_on_system": "Group {group} already exists in the system groups", "group_already_exist_on_system": "Group {group} already exists in the system groups",
"group_already_exist_on_system_but_removing_it": "Group {group} already exists in the system groups, but YunoHost will remove it", "group_already_exist_on_system_but_removing_it": "Group {group} already exists in the system groups, but YunoHost will remove it...",
"group_created": "Group '{group}' created", "group_created": "Group '{group}' created",
"group_creation_failed": "Could not create the group '{group}': {error}", "group_creation_failed": "Could not create the group '{group}': {error}",
"group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in YunoHost", "group_cannot_edit_all_users": "The group 'all_users' cannot be edited manually. It is a special group meant to contain all users registered in YunoHost",
@ -351,7 +351,6 @@
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it", "ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it", "iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
"log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'", "log_corrupted_md_file": "The YAML metadata file associated with logs is damaged: '{md_file}\nError: {error}'",
"log_category_404": "The log category '{category}' does not exist",
"log_link_to_log": "Full log of this operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'", "log_link_to_log": "Full log of this operation: '<a href=\"#/tools/logs/{name}\" style=\"text-decoration:underline\">{desc}</a>'",
"log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'", "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log display {name}'",
"log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by <a href=\"#/tools/logs/{name}\">clicking here</a> to get help", "log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by <a href=\"#/tools/logs/{name}\">clicking here</a> to get help",
@ -387,7 +386,7 @@
"log_user_group_create": "Create '{}' group", "log_user_group_create": "Create '{}' group",
"log_user_group_delete": "Delete '{}' group", "log_user_group_delete": "Delete '{}' group",
"log_user_group_update": "Update '{}' group", "log_user_group_update": "Update '{}' group",
"log_user_update": "Update user info of '{}'", "log_user_update": "Update info for user '{}'",
"log_user_permission_update": "Update accesses for permission '{}'", "log_user_permission_update": "Update accesses for permission '{}'",
"log_user_permission_reset": "Reset permission '{}'", "log_user_permission_reset": "Reset permission '{}'",
"log_domain_main_domain": "Make '{}' the main domain", "log_domain_main_domain": "Make '{}' the main domain",
@ -406,72 +405,42 @@
"mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user", "mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user",
"main_domain_change_failed": "Unable to change the main domain", "main_domain_change_failed": "Unable to change the main domain",
"main_domain_changed": "The main domain has been changed", "main_domain_changed": "The main domain has been changed",
"migrate_tsig_end": "Migration to HMAC-SHA-512 finished", "migration_description_0015_migrate_to_buster": "Upgrade the system to Debian Buster and YunoHost 4.x",
"migrate_tsig_failed": "Could not migrate the DynDNS domain '{domain}' to HMAC-SHA-512, rolling back. Error: {error_code}, {error}", "migration_description_0016_php70_to_php73_pools": "Migrate php7.0-fpm 'pool' conf files to php7.3",
"migrate_tsig_start": "Insufficiently secure key algorithm detected for TSIG signature of the domain '{domain}', initiating migration to the more secure HMAC-SHA-512", "migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11",
"migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account…", "migration_description_0018_xtable_to_nftable": "Migrate old network traffic rules to the new nftable system",
"migrate_tsig_wait_2": "2min…",
"migrate_tsig_wait_3": "1min…",
"migrate_tsig_wait_4": "30 seconds…",
"migrate_tsig_not_needed": "You do not appear to use a DynDNS domain, so no migration is needed.",
"migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'",
"migration_description_0002_migrate_to_tsig_sha256": "Improve security of DynDNS TSIG updates by using SHA-512 instead of MD5",
"migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0",
"migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from PostgreSQL 9.4 to 9.6",
"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 apps catalogs and use the new unified 'apps.json' list instead (outdated, replaced by migration 13)",
"migration_description_0011_setup_group_permission": "Set up user groups and permissions for apps and services",
"migration_description_0012_postgresql_password_to_md5_authentication": "Force PostgreSQL authentication to use MD5 for local connections",
"migration_description_0013_futureproof_apps_catalog_system": "Migrate to the new future-proof apps catalog system",
"migration_description_0014_remove_app_status_json": "Remove legacy status.json app files",
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
"migration_0003_patching_sources_list": "Patching the sources.lists…",
"migration_0003_main_upgrade": "Starting main upgrade…",
"migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade…",
"migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset it to its original state first… The previous file will be available as {backup_dest}.",
"migration_0003_yunohost_upgrade": "Starting the YunoHost package upgrade… The migration will end, but the actual upgrade will happen immediately afterwards. After the operation is complete, you might have to log in to the webadmin page again.",
"migration_0003_not_jessie": "The current Debian distribution is not Jessie!",
"migration_0003_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Stretch.",
"migration_0003_still_on_jessie_after_main_upgrade": "Something went wrong during the main upgrade: Is the system still on Jessie‽ To investigate the issue, please look at {log}:s…",
"migration_0003_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external e-mail clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port (465) will automatically be closed, and the new port (587) will be opened in the firewall. You and your users *will* have to adapt the configuration of your e-mail clients accordingly.",
"migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
"migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
"migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system :(…",
"migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.",
"migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration replaces your root password with the admin password.",
"migration_0007_cancelled": "Could not improve the way your SSH configuration is managed.",
"migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.",
"migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:",
"migration_0008_port": "• You will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;",
"migration_0008_root": "• You will not be able to connect as root through SSH. Instead you should use the admin user;",
"migration_0008_dsa": "• The DSA key will be turned off. 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 want YunoHost to override your current configuration, run the migration. Otherwise, you can also skip the migration, though it is not recommended.",
"migration_0008_no_warning": "Overriding your SSH configuration should be safe, though this cannot be promised! Run the migration to override it. Otherwise, you can also skip the migration, though it is not recommended.",
"migration_0009_not_needed": "This migration already happened somehow… (?) Skipping.",
"migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
"migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}",
"migration_0011_create_group": "Creating a group for each user…", "migration_0011_create_group": "Creating a group for each user…",
"migration_0011_done": "Migration completed. You are now able to manage usergroups.",
"migration_0011_slapd_config_will_be_overwritten": "It looks like you manually edited the slapd configuration. For this critical migration, YunoHost needs to force the update of the slapd configuration. The original files will be backuped in {conf_backup_folder}.",
"migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}", "migration_0011_LDAP_update_failed": "Could not update LDAP. Error: {error:s}",
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP…", "migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...",
"migration_0011_migration_failed_trying_to_rollback": "Could not migrate… trying to roll back the system.", "migration_0011_update_LDAP_database": "Updating LDAP database...",
"migration_0011_rollback_success": "System rolled back.", "migration_0011_update_LDAP_schema": "Updating LDAP schema...",
"migration_0011_update_LDAP_database": "Updating LDAP database…",
"migration_0011_update_LDAP_schema": "Updating LDAP schema…",
"migration_0011_failed_to_remove_stale_object": "Could not remove stale object {dn}: {error}", "migration_0011_failed_to_remove_stale_object": "Could not remove stale object {dn}: {error}",
"migration_0015_start" : "Starting migration to Buster",
"migration_0015_patching_sources_list": "Patching the sources.lists...",
"migration_0015_main_upgrade": "Starting main upgrade...",
"migration_0015_still_on_stretch_after_main_upgrade": "Something went wrong during the main upgrade, the system appears to still be on Debian Stretch",
"migration_0015_yunohost_upgrade" : "Starting YunoHost core upgrade...",
"migration_0015_not_stretch" : "The current Debian distribution is not Stretch!",
"migration_0015_not_enough_free_space" : "Free space is pretty low in /var/! You should have at least 1GB free to run this migration.",
"migration_0015_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Buster.",
"migration_0015_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.",
"migration_0015_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from the YunoHost app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
"migration_0015_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
"migration_0015_specific_upgrade": "Starting upgrade of system packages that needs to be upgrade independently...",
"migration_0015_cleaning_up": "Cleaning up cache and packages not useful anymore...",
"migration_0015_weak_certs": "The following certificates were found to still use weak signature algorithms and have to be upgraded to be compatible with the next version of nginx: {certs}",
"migration_0017_postgresql_96_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
"migration_0017_postgresql_11_not_installed": "PostgreSQL 9.6 is installed, but not postgresql 11‽ Something weird might have happened on your system :(...",
"migration_0017_not_enough_space": "Make sufficient space available in {path} to run the migration.",
"migration_0018_failed_to_migrate_iptables_rules": "Failed to migrate legacy iptables rules to nftables: {error}",
"migration_0018_failed_to_reset_legacy_rules": "Failed to reset legacy iptables rules: {error}",
"migrations_already_ran": "Those migrations are already done: {ids}", "migrations_already_ran": "Those migrations are already done: {ids}",
"migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'", "migrations_cant_reach_migration_file": "Could not access migrations files at the path '%s'",
"migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.", "migrations_dependencies_not_satisfied": "Run these migrations: '{dependencies_id}', before migration {id}.",
"migrations_failed_to_load_migration": "Could not load migration {id}: {error}", "migrations_failed_to_load_migration": "Could not load migration {id}: {error}",
"migrations_exclusive_options": "'--auto', '--skip', and '--force-rerun' are mutually exclusive options.", "migrations_exclusive_options": "'--auto', '--skip', and '--force-rerun' are mutually exclusive options.",
"migrations_list_conflict_pending_done": "You cannot use both '--previous' and '--done' at the same time.", "migrations_list_conflict_pending_done": "You cannot use both '--previous' and '--done' at the same time.",
"migrations_loading_migration": "Loading migration {id}…", "migrations_loading_migration": "Loading migration {id}...",
"migrations_migration_has_failed": "Migration {id} did not complete, aborting. Error: {exception}", "migrations_migration_has_failed": "Migration {id} did not complete, aborting. Error: {exception}",
"migrations_must_provide_explicit_targets": "You must provide explicit targets when using '--skip' or '--force-rerun'", "migrations_must_provide_explicit_targets": "You must provide explicit targets when using '--skip' or '--force-rerun'",
"migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option '--accept-disclaimer'.", "migrations_need_to_accept_disclaimer": "To run the migration {id}, your must accept the following disclaimer:\n---\n{disclaimer}\n---\nIf you accept to run the migration, please re-run the command with the option '--accept-disclaimer'.",
@ -479,11 +448,10 @@
"migrations_no_such_migration": "There is no migration called '{id}'", "migrations_no_such_migration": "There is no migration called '{id}'",
"migrations_not_pending_cant_skip": "Those migrations are not pending, so cannot be skipped: {ids}", "migrations_not_pending_cant_skip": "Those migrations are not pending, so cannot be skipped: {ids}",
"migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}", "migrations_pending_cant_rerun": "Those migrations are still pending, so cannot be run again: {ids}",
"migrations_running_forward": "Running migration {id}", "migrations_running_forward": "Running migration {id}...",
"migrations_skip_migration": "Skipping migration {id}", "migrations_skip_migration": "Skipping migration {id}...",
"migrations_success_forward": "Migration {id} completed", "migrations_success_forward": "Migration {id} completed",
"migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.", "migrations_to_be_ran_manually": "Migration {id} has to be run manually. Please go to Tools → Migrations on the webadmin page, or run `yunohost tools migrations migrate`.",
"no_internet_connection": "The server is not connected to the Internet",
"not_enough_disk_space": "Not enough free space on '{path:s}'", "not_enough_disk_space": "Not enough free space on '{path:s}'",
"operation_interrupted": "The operation was manually interrupted?", "operation_interrupted": "The operation was manually interrupted?",
"packages_upgrade_failed": "Could not upgrade all the packages", "packages_upgrade_failed": "Could not upgrade all the packages",
@ -534,10 +502,11 @@
"regenconf_would_be_updated": "The configuration would have 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_dry_pending_applying": "Checking pending configuration which would have been applied for category '{category}'…",
"regenconf_failed": "Could not regenerate the configuration for category(s): {categories}", "regenconf_failed": "Could not regenerate the configuration for category(s): {categories}",
"regenconf_pending_applying": "Applying pending configuration for category '{category}'…", "regenconf_pending_applying": "Applying pending configuration for category '{category}'...",
"regenconf_need_to_explicitly_specify_ssh": "The ssh configuration has been manually modified, but you need to explicitly specify category 'ssh' with --force to actually apply the changes.",
"restore_already_installed_app": "An app with the ID '{app:s}' is already installed", "restore_already_installed_app": "An app with the ID '{app:s}' is already installed",
"restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}", "restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}",
"restore_app_failed": "Could not restore the app '{app:s}'", "restore_app_failed": "Could not restore {app:s}",
"restore_cleaning_failed": "Could not clean up the temporary restoration directory", "restore_cleaning_failed": "Could not clean up the temporary restoration directory",
"restore_complete": "Restored", "restore_complete": "Restored",
"restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]", "restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]",
@ -569,8 +538,7 @@
"service_description_metronome": "Manage XMPP instant messaging accounts", "service_description_metronome": "Manage XMPP instant messaging accounts",
"service_description_mysql": "Stores app data (SQL database)", "service_description_mysql": "Stores app data (SQL database)",
"service_description_nginx": "Serves or provides access to all the websites hosted on your server", "service_description_nginx": "Serves or provides access to all the websites hosted on your server",
"service_description_nslcd": "Handles YunoHost user shell connection", "service_description_php7.3-fpm": "Runs apps written in PHP with NGINX",
"service_description_php7.0-fpm": "Runs apps written in PHP with NGINX",
"service_description_postfix": "Used to send and receive e-mails", "service_description_postfix": "Used to send and receive e-mails",
"service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs", "service_description_redis-server": "A specialized database used for rapid data access, task queue, and communication between programs",
"service_description_rspamd": "Filters spam, and other e-mail related features", "service_description_rspamd": "Filters spam, and other e-mail related features",
@ -600,7 +568,7 @@
"ssowat_conf_updated": "SSOwat configuration updated", "ssowat_conf_updated": "SSOwat configuration updated",
"system_upgraded": "System upgraded", "system_upgraded": "System upgraded",
"system_username_exists": "Username already exists in the list of system users", "system_username_exists": "Username already exists in the list of system users",
"this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers) You can try to solve this issue by connecting through SSH and running `sudo apt install --fix-broken` and/or `sudo dpkg --configure -a`.", "this_action_broke_dpkg": "This action broke dpkg/APT (the system package managers)... You can try to solve this issue by connecting through SSH and running `sudo apt install --fix-broken` and/or `sudo dpkg --configure -a`.",
"tools_upgrade_at_least_one": "Please specify '--apps', or '--system'", "tools_upgrade_at_least_one": "Please specify '--apps', or '--system'",
"tools_upgrade_cant_both": "Cannot upgrade both system and apps at the same time", "tools_upgrade_cant_both": "Cannot upgrade both system and apps at the same time",
"tools_upgrade_cant_hold_critical_packages": "Could not hold critical packages…", "tools_upgrade_cant_hold_critical_packages": "Could not hold critical packages…",
@ -610,15 +578,15 @@
"tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages…", "tools_upgrade_special_packages": "Now upgrading 'special' (yunohost-related) packages…",
"tools_upgrade_special_packages_explanation": "The special upgrade will continue in the background. Please don't start any other actions on your server for the next ~10 minutes (depending on hardware speed). After this, you may have to re-log in to the webadmin. The upgrade log will be available in Tools → Log (in the webadmin) or using 'yunohost log list' (from the command-line).", "tools_upgrade_special_packages_explanation": "The special upgrade will continue in the background. Please don't start any other actions on your server for the next ~10 minutes (depending on hardware speed). After this, you may have to re-log in to the webadmin. The upgrade log will be available in Tools → Log (in the webadmin) or using 'yunohost log list' (from the command-line).",
"tools_upgrade_special_packages_completed": "YunoHost package upgrade completed.\nPress [Enter] to get the command line back", "tools_upgrade_special_packages_completed": "YunoHost package upgrade completed.\nPress [Enter] to get the command line back",
"unbackup_app": "App '{app:s}' will not be saved", "unbackup_app": "{app:s} will not be saved",
"unexpected_error": "Something unexpected went wrong: {error}", "unexpected_error": "Something unexpected went wrong: {error}",
"unlimit": "No quota", "unlimit": "No quota",
"unrestore_app": "App '{app:s}' will not be restored", "unrestore_app": "{app:s} will not be restored",
"update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "update_apt_cache_failed": "Could not to update the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}",
"update_apt_cache_warning": "Something went wrong while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}", "update_apt_cache_warning": "Something went wrong while updating the cache of APT (Debian's package manager). Here is a dump of the sources.list lines, which might help identify problematic lines: \n{sourceslist}",
"updating_apt_cache": "Fetching available upgrades for system packages", "updating_apt_cache": "Fetching available upgrades for system packages...",
"upgrade_complete": "Upgrade complete", "upgrade_complete": "Upgrade complete",
"upgrading_packages": "Upgrading packages", "upgrading_packages": "Upgrading packages...",
"upnp_dev_not_found": "No UPnP device found", "upnp_dev_not_found": "No UPnP device found",
"upnp_disabled": "UPnP turned off", "upnp_disabled": "UPnP turned off",
"upnp_enabled": "UPnP turned on", "upnp_enabled": "UPnP turned on",
@ -637,7 +605,7 @@
"yunohost_ca_creation_failed": "Could not create certificate authority", "yunohost_ca_creation_failed": "Could not create certificate authority",
"yunohost_ca_creation_success": "Local certification authority created.", "yunohost_ca_creation_success": "Local certification authority created.",
"yunohost_configured": "YunoHost is now configured", "yunohost_configured": "YunoHost is now configured",
"yunohost_installing": "Installing YunoHost", "yunohost_installing": "Installing YunoHost...",
"yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'", "yunohost_not_installed": "YunoHost is not correctly installed. Please run 'yunohost tools postinstall'",
"yunohost_postinstall_end_tip": "The post-install completed! To finalize your setup, please consider:\n - adding a first user through the 'Users' section of the webadmin (or 'yunohost user create <username>' in command-line);\n - diagnose potential issues through the 'Diagnosis' section of the webadmin (or 'yunohost diagnosis run' in command-line);\n - reading the 'Finalizing your setup' and 'Getting to know Yunohost' parts in the admin documentation: https://yunohost.org/admindoc." "yunohost_postinstall_end_tip": "The post-install completed! To finalize your setup, please consider:\n - adding a first user through the 'Users' section of the webadmin (or 'yunohost user create <username>' in command-line);\n - diagnose potential issues through the 'Diagnosis' section of the webadmin (or 'yunohost diagnosis run' in command-line);\n - reading the 'Finalizing your setup' and 'Getting to know Yunohost' parts in the admin documentation: https://yunohost.org/admindoc."
} }

View file

@ -12,10 +12,10 @@
"app_install_files_invalid": "Fichiers dinstallation incorrects", "app_install_files_invalid": "Fichiers dinstallation incorrects",
"app_manifest_invalid": "Manifeste dapplication incorrect : {error}", "app_manifest_invalid": "Manifeste dapplication incorrect : {error}",
"app_not_correctly_installed": "{app:s} semble être mal installé", "app_not_correctly_installed": "{app:s} semble être mal installé",
"app_not_installed": "Nous navons pas trouvé lapplication « {app:s} » dans la liste des applications installées : {all_apps}", "app_not_installed": "Nous navons pas trouvé {app:s} dans la liste des applications installées : {all_apps}",
"app_not_properly_removed": "{app:s} na pas été supprimé correctement", "app_not_properly_removed": "{app:s} na pas été supprimé correctement",
"app_removed": "{app:s} supprimé", "app_removed": "{app:s} supprimé",
"app_requirements_checking": "Vérification des paquets requis pour {app}", "app_requirements_checking": "Vérification des paquets requis pour {app}...",
"app_requirements_unmeet": "Les pré-requis de {app} ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}", "app_requirements_unmeet": "Les pré-requis de {app} ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}",
"app_sources_fetch_failed": "Impossible de récupérer les fichiers sources, lURL est-elle correcte ?", "app_sources_fetch_failed": "Impossible de récupérer les fichiers sources, lURL est-elle correcte ?",
"app_unknown": "Application inconnue", "app_unknown": "Application inconnue",
@ -28,8 +28,8 @@
"ask_main_domain": "Domaine principal", "ask_main_domain": "Domaine principal",
"ask_new_admin_password": "Nouveau mot de passe dadministration", "ask_new_admin_password": "Nouveau mot de passe dadministration",
"ask_password": "Mot de passe", "ask_password": "Mot de passe",
"backup_app_failed": "Impossible de sauvegarder lapplication '{app:s}'", "backup_app_failed": "Impossible de sauvegarder {app:s}",
"backup_archive_app_not_found": "Lapplication '{app:s}' na pas été trouvée dans larchive de la sauvegarde", "backup_archive_app_not_found": "{app:s} na pas été trouvée dans larchive de la sauvegarde",
"backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà.", "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà.",
"backup_archive_name_unknown": "Larchive locale de sauvegarde nommée '{name:s}' est inconnue", "backup_archive_name_unknown": "Larchive locale de sauvegarde nommée '{name:s}' est inconnue",
"backup_archive_open_failed": "Impossible douvrir larchive de la sauvegarde", "backup_archive_open_failed": "Impossible douvrir larchive de la sauvegarde",
@ -44,7 +44,7 @@
"backup_output_directory_forbidden": "Choisissez un répertoire de destination différent. Les sauvegardes ne peuvent pas ê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_forbidden": "Choisissez un répertoire de destination différent. Les sauvegardes ne peuvent pas ê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 répertoire de destination nest pas vide", "backup_output_directory_not_empty": "Le répertoire de destination nest pas vide",
"backup_output_directory_required": "Vous devez spécifier un dossier de destination pour la sauvegarde", "backup_output_directory_required": "Vous devez spécifier un dossier de destination pour la sauvegarde",
"backup_running_hooks": "Exécution des scripts de sauvegarde", "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}", "custom_app_url_required": "Vous devez spécifier une URL pour mettre à jour votre application personnalisée {app:s}",
"domain_cert_gen_failed": "Impossible de générer le certificat", "domain_cert_gen_failed": "Impossible de générer le certificat",
"domain_created": "Le domaine a été créé", "domain_created": "Le domaine a été créé",
@ -63,15 +63,15 @@
"dyndns_cron_removed": "La tâche cron pour le domaine DynDNS enlevée", "dyndns_cron_removed": "La tâche cron pour le domaine DynDNS enlevée",
"dyndns_ip_update_failed": "Impossible de mettre à jour ladresse IP sur le domaine DynDNS", "dyndns_ip_update_failed": "Impossible de mettre à jour ladresse IP sur le domaine DynDNS",
"dyndns_ip_updated": "Mise à jour de votre IP pour le domaine DynDNS", "dyndns_ip_updated": "Mise à jour de votre IP pour le domaine DynDNS",
"dyndns_key_generating": "Génération de la clé DNS, cela peut prendre un certain temps.", "dyndns_key_generating": "Génération de la clé DNS..., cela peut prendre un certain temps.",
"dyndns_key_not_found": "Clé DNS introuvable pour le domaine", "dyndns_key_not_found": "Clé DNS introuvable pour le domaine",
"dyndns_no_domain_registered": "Aucun domaine enregistré avec DynDNS", "dyndns_no_domain_registered": "Aucun domaine enregistré avec DynDNS",
"dyndns_registered": "Domaine DynDNS enregistré", "dyndns_registered": "Domaine DynDNS enregistré",
"dyndns_registration_failed": "Impossible denregistrer le domaine DynDNS : {error:s}", "dyndns_registration_failed": "Impossible denregistrer le domaine DynDNS : {error:s}",
"dyndns_unavailable": "Le domaine {domain:s} est indisponible.", "dyndns_unavailable": "Le domaine {domain:s} est indisponible.",
"executing_command": "Exécution de la commande '{command:s}'", "executing_command": "Exécution de la commande '{command:s}'...",
"executing_script": "Exécution du script '{script:s}'", "executing_script": "Exécution du script '{script:s}'...",
"extracting": "Extraction en cours", "extracting": "Extraction en cours...",
"field_invalid": "Champ incorrect : '{:s}'", "field_invalid": "Champ incorrect : '{:s}'",
"firewall_reload_failed": "Impossible de recharger le pare-feu", "firewall_reload_failed": "Impossible de recharger le pare-feu",
"firewall_reloaded": "Pare-feu rechargé", "firewall_reloaded": "Pare-feu rechargé",
@ -89,7 +89,7 @@
"mail_domain_unknown": "Le domaine '{domain:s}' de cette adresse de courriel nest pas valide. Merci dutiliser un domaine administré par ce serveur.", "mail_domain_unknown": "Le domaine '{domain:s}' de cette adresse de courriel nest pas valide. Merci dutiliser un domaine administré par ce serveur.",
"mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'", "mail_forward_remove_failed": "Impossible de supprimer le courriel de transfert '{mail:s}'",
"main_domain_change_failed": "Impossible de modifier le domaine principal", "main_domain_change_failed": "Impossible de modifier le domaine principal",
"main_domain_changed": "Le domaine principal modifié", "main_domain_changed": "Le domaine principal a été modifié",
"no_internet_connection": "Le serveur nest pas connecté à Internet", "no_internet_connection": "Le serveur nest pas connecté à Internet",
"not_enough_disk_space": "Lespace disque est insuffisant sur '{path:s}'", "not_enough_disk_space": "Lespace disque est insuffisant sur '{path:s}'",
"package_unknown": "Le paquet '{pkgname}' est inconnu", "package_unknown": "Le paquet '{pkgname}' est inconnu",
@ -163,12 +163,12 @@
"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_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_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_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_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_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} nest pas sur le point dexpirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)", "certmanager_attempt_to_renew_valid_cert": "Le certificat pour le domaine {domain:s} nest pas sur le point dexpirer ! (Vous pouvez utiliser --force si vous savez ce que vous faites)",
"certmanager_domain_http_not_working": "Le domaine {domain:s} ne semble pas être accessible via HTTP. Merci de vérifier la catégorie 'Web' dans le diagnostic pour plus d'informations. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", "certmanager_domain_http_not_working": "Le domaine {domain:s} ne semble pas être accessible via HTTP. Merci de vérifier la catégorie 'Web' dans le diagnostic pour plus d'informations. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)",
"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_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 (des vérificateurs de propagation DNS sont disponibles en ligne). (Si vous savez ce que vous faites, utilisez --no-checks pour désactiver ces contrôles)", "certmanager_domain_dns_ip_differs_from_public_ip": "L'enregistrement DNS du domaine {domain:s} est différent de ladresse IP de ce serveur. Pour plus d'informations, veuillez consulter la catégorie \"Enregistrements DNS\" dans la section diagnostic. Si vous avez récemment modifié votre enregistrement 'A', veuillez attendre sa propagation (des vérificateurs 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}", "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}",
"certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »", "certmanager_cert_install_success_selfsigned": "Le certificat auto-signé est maintenant installé pour le domaine « {domain:s} »",
"certmanager_cert_install_success": "Le certificat Lets Encrypt est maintenant installé pour le domaine « {domain:s} »", "certmanager_cert_install_success": "Le certificat Lets Encrypt est maintenant installé pour le domaine « {domain:s} »",
@ -188,7 +188,7 @@
"certmanager_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant ladresse 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_http_check_timeout": "Expiration du délai lorsque le serveur a essayé de se contacter lui-même via HTTP en utilisant ladresse 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.",
"domain_hostname_failed": "Échec de lutilisation dun nouveau nom dhôte. Cela pourrait causer des soucis plus tard (cela nen causera peut-être pas).", "domain_hostname_failed": "Échec de lutilisation dun nouveau nom dhôte. Cela pourrait causer des soucis plus tard (cela nen causera peut-être pas).",
"yunohost_ca_creation_success": "Lautorité de certification locale créée.", "yunohost_ca_creation_success": "L'autorité de certification locale a été créée.",
"app_already_installed_cant_change_url": "Cette application est déjà installée. LURL ne peut pas être changé simplement par cette fonction. Vérifiez si cela est disponible avec `app changeurl`.", "app_already_installed_cant_change_url": "Cette application est déjà installée. LURL ne peut pas être changé simplement par cette fonction. Vérifiez si cela est disponible avec `app changeurl`.",
"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_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_lURL sont identiques pour ('{domain:s}{path:s}'), rien à faire.", "app_change_url_identical_domains": "Lancien et le nouveau couple domaine/chemin_de_lURL sont identiques pour ('{domain:s}{path:s}'), rien à faire.",
@ -209,13 +209,13 @@
"global_settings_unknown_type": "Situation inattendue : la configuration {setting:s} semble avoir le type {unknown_type:s} mais celui-ci nest pas pris en charge par le système.", "global_settings_unknown_type": "Situation inattendue : la configuration {setting:s} semble avoir le type {unknown_type:s} mais celui-ci nest 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", "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",
"backup_abstract_method": "Cette méthode de sauvegarde reste à implémenter", "backup_abstract_method": "Cette méthode de sauvegarde reste à implémenter",
"backup_applying_method_tar": "Création de larchive TAR de la sauvegarde", "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_copy": "Copie de tous les fichiers à sauvegarder...",
"backup_applying_method_borg": "Envoi de tous les fichiers à sauvegarder dans le répertoire 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_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_system_part_not_available": "La partie '{part:s}' du système nest pas disponible dans cette sauvegarde",
"backup_archive_writing_error": "Impossible dajouter des fichiers '{source:s}' (nommés dans larchive : '{dest:s}') à sauvegarder dans larchive compressée '{archive:s}'", "backup_archive_writing_error": "Impossible dajouter des fichiers '{source:s}' (nommés dans larchive : '{dest:s}') à sauvegarder dans larchive compressée '{archive:s}'",
"backup_ask_for_copying_if_needed": "Voulez-vous effectuer la sauvegarde en utilisant {size:s} temporairement ? (Cette méthode est utilisée car certains fichiers nont pas pu être préparés avec une méthode plus efficace.)", "backup_ask_for_copying_if_needed": "Voulez-vous effectuer la sauvegarde en utilisant {size:s}Mo temporairement ? (Cette méthode est utilisée car certains fichiers nont pas pu être préparés avec une méthode plus efficace.)",
"backup_borg_not_implemented": "La méthode de sauvegarde Borg nest pas encore implémentée", "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_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", "backup_copying_to_organize_the_archive": "Copie de {size:s} Mo pour organiser larchive",
@ -231,7 +231,7 @@
"backup_system_part_failed": "Impossible de sauvegarder la partie '{part:s}' du système", "backup_system_part_failed": "Impossible de sauvegarder la partie '{part:s}' du système",
"backup_unable_to_organize_files": "Impossible dutiliser la méthode rapide pour organiser les fichiers dans larchive", "backup_unable_to_organize_files": "Impossible dutiliser la méthode rapide pour organiser les fichiers dans larchive",
"backup_with_no_backup_script_for_app": "Lapplication {app:s} na pas de script de sauvegarde. Ignorer.", "backup_with_no_backup_script_for_app": "Lapplication {app:s} na pas de script de sauvegarde. Ignorer.",
"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.", "backup_with_no_restore_script_for_app": "{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}", "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_removing_tmp_dir_failed": "Impossible de sauvegarder un ancien dossier temporaire",
"restore_extracting": "Extraction des fichiers nécessaires depuis larchive…", "restore_extracting": "Extraction des fichiers nécessaires depuis larchive…",
@ -253,7 +253,7 @@
"dyndns_could_not_check_provide": "Impossible de vérifier si {provider:s} peut fournir {domain:s}.", "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}.", "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 il est déjà utilisé par lapplication '{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 lapplication '{other_app}'",
"app_upgrade_app_name": "Mise à jour de lapplication {app} …", "app_upgrade_app_name": "Mise à jour de {app}...",
"backup_output_symlink_dir_broken": "Votre répertoire darchivage '{path:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.", "backup_output_symlink_dir_broken": "Votre répertoire darchivage '{path:s}' est un lien symbolique brisé. Peut-être avez-vous oublié de re/monter ou de brancher le support de stockage sur lequel il pointe.",
"migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée", "migrate_tsig_end": "La migration à HMAC-SHA-512 est terminée",
"migrate_tsig_failed": "La migration du domaine DynDNS {domain} à HMAC-SHA-512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}", "migrate_tsig_failed": "La migration du domaine DynDNS {domain} à HMAC-SHA-512 a échoué. Annulation des modifications. Erreur : {error_code} - {error}",
@ -352,15 +352,15 @@
"root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !", "root_password_desynchronized": "Le mot de passe administrateur a été changé, mais YunoHost na pas pu le propager au mot de passe root !",
"aborting": "Annulation.", "aborting": "Annulation.",
"app_not_upgraded": "Lapplication {failed_app} na pas été mise à jour et par conséquence les applications suivantes nont pas été mises à jour : {apps}", "app_not_upgraded": "Lapplication {failed_app} na pas été mise à jour et par conséquence les applications suivantes nont pas été mises à jour : {apps}",
"app_start_install": "Installation de lapplication {app} …", "app_start_install": "Installation de {app}...",
"app_start_remove": "Suppression de lapplication {app} …", "app_start_remove": "Suppression de {app}...",
"app_start_backup": "Collecte des fichiers devant être sauvegardés pour lapplication {app} …", "app_start_backup": "Collecte des fichiers devant être sauvegardés pour {app}...",
"app_start_restore": "Restauration de lapplication {app} …", "app_start_restore": "Restauration de {app}...",
"app_upgrade_several_apps": "Les applications suivantes seront mises à jour : {apps}", "app_upgrade_several_apps": "Les applications suivantes seront mises à jour : {apps}",
"ask_new_domain": "Nouveau domaine", "ask_new_domain": "Nouveau domaine",
"ask_new_path": "Nouveau chemin", "ask_new_path": "Nouveau chemin",
"backup_actually_backuping": "Création dune archive de sauvegarde à partir des fichiers collectés", "backup_actually_backuping": "Création dune archive de sauvegarde à partir des fichiers collectés...",
"backup_mount_archive_for_restore": "Préparation de larchive pour restauration", "backup_mount_archive_for_restore": "Préparation de larchive pour restauration...",
"confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais nest pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que lauthentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. Linstaller quand même ? [{answers:s}] ", "confirm_app_install_warning": "Avertissement : cette application peut fonctionner mais nest pas bien intégrée dans YunoHost. Certaines fonctionnalités telles que lauthentification unique et la sauvegarde/restauration peuvent ne pas être disponibles. Linstaller quand même ? [{answers:s}] ",
"confirm_app_install_danger": "DANGER ! Cette application est connue pour être encore expérimentale (si elle ne fonctionne pas explicitement) ! Vous ne devriez probablement PAS linstaller à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système … Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'", "confirm_app_install_danger": "DANGER ! Cette application est connue pour être encore expérimentale (si elle ne fonctionne pas explicitement) ! Vous ne devriez probablement PAS linstaller à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système … Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'",
"confirm_app_install_thirdparty": "DANGER! Cette application ne fait pas partie du catalogue d'applications de Yunohost. 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 à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'", "confirm_app_install_thirdparty": "DANGER! Cette application ne fait pas partie du catalogue d'applications de Yunohost. 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 à moins de savoir ce que vous faites. AUCUN SUPPORT ne sera fourni si cette application ne fonctionne pas ou casse votre système ... Si vous êtes prêt à prendre ce risque de toute façon, tapez '{answers:s}'",
@ -392,7 +392,7 @@
"service_reloaded_or_restarted": "Le service « {service:s} » a été rechargé ou redémarré", "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 apt install --fix-broken` et/ou `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 apt install --fix-broken` et/ou `sudo dpkg --configure -a`.",
"app_action_cannot_be_ran_because_required_services_down": "Ces services requis doivent être en cours dexécution pour exécuter cette action : {services}. Essayez de les redémarrer pour continuer (et éventuellement rechercher pourquoi ils sont en panne).", "app_action_cannot_be_ran_because_required_services_down": "Ces services requis doivent être en cours dexécution pour exécuter cette action : {services}. Essayez de les redémarrer pour continuer (et éventuellement rechercher pourquoi ils sont en panne).",
"admin_password_too_long": "Veuillez choisir un mot de passe de moins de 127 caractères", "admin_password_too_long": "Veuillez choisir un mot de passe comportant moins de 127 caractères",
"log_regen_conf": "Régénérer les configurations du système '{}'", "log_regen_conf": "Régénérer les configurations du système '{}'",
"migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On lignore.", "migration_0009_not_needed": "Cette migration semble avoir déjà été jouée ? On lignore.",
"regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'", "regenconf_file_backed_up": "Le fichier de configuration '{conf}' a été sauvegardé sous '{backup}'",
@ -426,10 +426,10 @@
"tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie !\nPressez [Entrée] pour revenir à la ligne de commande", "tools_upgrade_special_packages_completed": "La mise à jour des paquets de YunoHost est finie !\nPressez [Entrée] pour revenir à la ligne de commande",
"dpkg_lock_not_available": "Cette commande ne peut pas être exécutée pour le moment car un autre programme semble utiliser le verrou de dpkg (le gestionnaire de package système)", "dpkg_lock_not_available": "Cette commande ne peut pas être exécutée pour le moment car un autre programme semble utiliser le verrou de dpkg (le gestionnaire de package système)",
"tools_upgrade_cant_unhold_critical_packages": "Impossible de conserver les paquets critiques…", "tools_upgrade_cant_unhold_critical_packages": "Impossible de conserver les paquets critiques…",
"tools_upgrade_special_packages_explanation": "La mise à niveau spéciale se poursuivra en arrière-plan. Veuillez ne pas lancer d'autres actions sur votre serveur pendant les 10 prochaines minutes (selon la vitesse du matériel). Après cela, vous devrez peut-être vous reconnecter à l'administrateur Web. Le journal de mise à niveau sera disponible dans Outils → Journal (dans le webadmin) ou en utilisant la « liste des journaux yunohost » (à partir de la ligne de commande).", "tools_upgrade_special_packages_explanation": "La mise à niveau spécifique à YunoHost se poursuivra en arrière-plan. Veuillez ne pas lancer d'autres actions sur votre serveur pendant les 10 prochaines minutes (selon la vitesse du matériel). Après cela, vous devrez peut-être vous reconnecter à l'administrateur Web. Le journal de mise à niveau sera disponible dans Outils → Journal (dans le webadmin) ou en utilisant la « liste des journaux yunohost » (à partir de la ligne de commande).",
"update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_failed": "Impossible de mettre à jour le cache APT (gestionnaire de paquets Debian). Voici un extrait du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}",
"update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}", "update_apt_cache_warning": "Des erreurs se sont produites lors de la mise à jour du cache APT (gestionnaire de paquets Debian). Voici un extrait des lignes du fichier sources.list qui pourrait vous aider à identifier les lignes problématiques :\n{sourceslist}",
"backup_permission": "Permission de sauvegarde pour lapplication {app:s}", "backup_permission": "Permission de sauvegarde pour {app:s}",
"group_created": "Le groupe '{group}' a été créé", "group_created": "Le groupe '{group}' a été créé",
"group_deleted": "Suppression du groupe '{group}'", "group_deleted": "Suppression du groupe '{group}'",
"group_unknown": "Le groupe {group:s} est inconnu", "group_unknown": "Le groupe {group:s} est inconnu",
@ -501,7 +501,7 @@
"app_install_failed": "Impossible dinstaller {app} : {error}", "app_install_failed": "Impossible dinstaller {app} : {error}",
"app_install_script_failed": "Une erreur est survenue dans le script dinstallation de lapplication", "app_install_script_failed": "Une erreur est survenue dans le script dinstallation de lapplication",
"permission_require_account": "Permission {permission} na de sens que pour les utilisateurs ayant un compte et ne peut donc pas être activé pour les visiteurs.", "permission_require_account": "Permission {permission} na de sens que pour les utilisateurs ayant un compte et ne peut donc pas être activé pour les visiteurs.",
"app_remove_after_failed_install": "Supprimer lapplication après léchec de linstallation", "app_remove_after_failed_install": "Supprimer lapplication après léchec de linstallation...",
"diagnosis_display_tip_web": "Vous pouvez aller à la section Diagnostic (dans lécran daccueil) pour voir les problèmes rencontrés.", "diagnosis_display_tip_web": "Vous pouvez aller à la section Diagnostic (dans lécran daccueil) pour voir les problèmes rencontrés.",
"diagnosis_cant_run_because_of_dep": "Impossible dexécuter le diagnostic pour {category} alors quil existe des problèmes importants liés à {dep}.", "diagnosis_cant_run_because_of_dep": "Impossible dexécuter le diagnostic pour {category} alors quil existe des problèmes importants liés à {dep}.",
"diagnosis_found_errors": "Trouvé {errors} problème(s) significatif(s) lié(s) à {category} !", "diagnosis_found_errors": "Trouvé {errors} problème(s) significatif(s) lié(s) à {category} !",
@ -509,7 +509,7 @@
"diagnosis_ip_not_connected_at_all": "Le serveur ne semble pas du tout connecté à Internet !?", "diagnosis_ip_not_connected_at_all": "Le serveur ne semble pas du tout connecté à Internet !?",
"diagnosis_ip_weird_resolvconf": "La résolution DNS semble fonctionner, mais il semble que vous utilisez un <code>/etc/resolv.conf</code> personnalisé.", "diagnosis_ip_weird_resolvconf": "La résolution DNS semble fonctionner, mais il semble que vous utilisez un <code>/etc/resolv.conf</code> personnalisé.",
"diagnosis_ip_weird_resolvconf_details": "Le fichier <code>/etc/resolv.conf</code> doit être un lien symbolique vers <code>/etc/resolvconf/run/resolv.conf</code> lui-même pointant vers <code>127.0.0.1</code> (dnsmasq). Si vous souhaitez configurer manuellement les résolveurs DNS, veuillez modifier <code>/etc/resolv.dnsmasq.conf</code>.", "diagnosis_ip_weird_resolvconf_details": "Le fichier <code>/etc/resolv.conf</code> doit être un lien symbolique vers <code>/etc/resolvconf/run/resolv.conf</code> lui-même pointant vers <code>127.0.0.1</code> (dnsmasq). Si vous souhaitez configurer manuellement les résolveurs DNS, veuillez modifier <code>/etc/resolv.dnsmasq.conf</code>.",
"diagnosis_dns_missing_record": "Selon la configuration DNS recommandée, vous devez ajouter un enregistrement DNS\nType : {type}\nNom : {name}\nValeur : {value}", "diagnosis_dns_missing_record": "Selon la configuration DNS recommandée, vous devez ajouter un enregistrement DNS<br>Type : <code>{type}</code><br>Nom : <code>{name}</code><br>Valeur: <code>{value}</code>",
"diagnosis_diskusage_ok": "Lespace de stockage <code>{mountpoint}</code> (sur le périphérique <code>{device}</code>) a encore {free} ({free_percent}%) espace restant (sur {total}) !", "diagnosis_diskusage_ok": "Lespace de stockage <code>{mountpoint}</code> (sur le périphérique <code>{device}</code>) a encore {free} ({free_percent}%) espace restant (sur {total}) !",
"diagnosis_ram_ok": "Le système dispose encore de {available} ({available_percent}%) de RAM sur {total}.", "diagnosis_ram_ok": "Le système dispose encore de {available} ({available_percent}%) de RAM sur {total}.",
"diagnosis_regenconf_allgood": "Tous les fichiers de configuration sont conformes à la configuration recommandée !", "diagnosis_regenconf_allgood": "Tous les fichiers de configuration sont conformes à la configuration recommandée !",
@ -522,7 +522,7 @@
"diagnosis_display_tip_cli": "Vous pouvez exécuter 'yunohost diagnosis show --issues' pour afficher les problèmes détectés.", "diagnosis_display_tip_cli": "Vous pouvez exécuter 'yunohost diagnosis show --issues' pour afficher les problèmes détectés.",
"diagnosis_failed_for_category": "Échec du diagnostic pour la catégorie '{category}': {error}", "diagnosis_failed_for_category": "Échec du diagnostic pour la catégorie '{category}': {error}",
"diagnosis_cache_still_valid": "(Le cache est encore valide pour le diagnostic {category}. Il ne sera pas re-diagnostiqué pour le moment!)", "diagnosis_cache_still_valid": "(Le cache est encore valide pour le diagnostic {category}. Il ne sera pas re-diagnostiqué pour le moment!)",
"diagnosis_ignored_issues": "(+ {nb_ignored} problèmes ignorée(s))", "diagnosis_ignored_issues": "(+ {nb_ignored} problème(s) ignoré(s))",
"diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.", "diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.",
"diagnosis_everything_ok": "Tout semble bien pour {category} !", "diagnosis_everything_ok": "Tout semble bien pour {category} !",
"diagnosis_failed": "Échec de la récupération du résultat du diagnostic pour la catégorie '{category}' : {error}", "diagnosis_failed": "Échec de la récupération du résultat du diagnostic pour la catégorie '{category}' : {error}",
@ -535,7 +535,7 @@
"diagnosis_ip_broken_resolvconf": "La résolution du nom de domaine semble être cassée sur votre serveur, ce qui semble lié au fait que <code>/etc/resolv.conf</code> ne pointe pas vers <code>127.0.0.1</code>.", "diagnosis_ip_broken_resolvconf": "La résolution du nom de domaine semble être cassée sur votre serveur, ce qui semble lié au fait que <code>/etc/resolv.conf</code> ne pointe pas vers <code>127.0.0.1</code>.",
"diagnosis_dns_good_conf": "Les enregistrements DNS sont correctement configurés pour le domaine {domain} (catégorie {category})", "diagnosis_dns_good_conf": "Les enregistrements DNS sont correctement configurés pour le domaine {domain} (catégorie {category})",
"diagnosis_dns_bad_conf": "Certains enregistrements DNS sont manquants ou incorrects pour le domaine {domain} (catégorie {category})", "diagnosis_dns_bad_conf": "Certains enregistrements DNS sont manquants ou incorrects pour le domaine {domain} (catégorie {category})",
"diagnosis_dns_discrepancy": "Lenregistrement DNS de type {type} et nom {name} ne correspond pas à la configuration recommandée.\nValeur actuelle : {current}\nValeur attendue : {value}", "diagnosis_dns_discrepancy": "Cet enregistrement DNS ne semble pas correspondre à la configuration recommandée : <br>Type : <code>{type}</code><br>Nom : <code>{name}</code><br> La valeur actuelle est : <code>{current}</code><br> La valeur attendue est : <code>{value}</code>",
"diagnosis_services_bad_status": "Le service {service} est {status} :-(", "diagnosis_services_bad_status": "Le service {service} est {status} :-(",
"diagnosis_diskusage_verylow": "L'espace de stockage <code>{mountpoint}</code> (sur lappareil <code>{device}</code>) ne dispose que de {free} ({free_percent}%) espace restant (sur {total}). Vous devriez vraiment envisager de nettoyer de lespace !", "diagnosis_diskusage_verylow": "L'espace de stockage <code>{mountpoint}</code> (sur lappareil <code>{device}</code>) ne dispose que de {free} ({free_percent}%) espace restant (sur {total}). Vous devriez vraiment envisager de nettoyer de lespace !",
"diagnosis_diskusage_low": "L'espace de stockage <code>{mountpoint}</code> (sur l'appareil <code>{device}</code>) ne dispose que de {free} ({free_percent}%) espace restant (sur {total}). Faites attention.", "diagnosis_diskusage_low": "L'espace de stockage <code>{mountpoint}</code> (sur l'appareil <code>{device}</code>) ne dispose que de {free} ({free_percent}%) espace restant (sur {total}). Faites attention.",
@ -550,7 +550,7 @@
"diagnosis_regenconf_manually_modified_debian_details": "Cela peut probablement être OK, mais il faut garder un œil dessus …", "diagnosis_regenconf_manually_modified_debian_details": "Cela peut probablement être OK, mais il faut garder un œil dessus …",
"apps_catalog_init_success": "Système de catalogue dapplications initialisé !", "apps_catalog_init_success": "Système de catalogue dapplications initialisé !",
"apps_catalog_failed_to_download": "Impossible de télécharger le catalogue des applications {apps_catalog} : {error}", "apps_catalog_failed_to_download": "Impossible de télécharger le catalogue des applications {apps_catalog} : {error}",
"diagnosis_mail_outgoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer de courrier électronique à dautres serveurs.", "diagnosis_mail_outgoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer des courriels à dautres serveurs.",
"domain_cannot_remove_main_add_new_one": "Vous ne pouvez pas supprimer '{domain:s}' car il sagit du domaine principal et de votre seul domaine. Vous devez dabord ajouter un autre domaine à laide de 'yunohost domain add <another-domain.com>', puis définir comme domaine principal à laide de 'yunohost domain main-domain -n <nom-dun-autre-domaine.com>' et vous pouvez ensuite supprimer le domaine '{domain:s}' à laide de 'yunohost domain remove {domain:s}'.'", "domain_cannot_remove_main_add_new_one": "Vous ne pouvez pas supprimer '{domain:s}' car il sagit du domaine principal et de votre seul domaine. Vous devez dabord ajouter un autre domaine à laide de 'yunohost domain add <another-domain.com>', puis définir comme domaine principal à laide de 'yunohost domain main-domain -n <nom-dun-autre-domaine.com>' et vous pouvez ensuite supprimer le domaine '{domain:s}' à laide de 'yunohost domain remove {domain:s}'.'",
"diagnosis_security_vulnerable_to_meltdown_details": "Pour résoudre ce problème, vous devez mettre à niveau votre système et redémarrer pour charger le nouveau noyau Linux (ou contacter votre fournisseur de serveur si cela ne fonctionne pas). Voir https://meltdownattack.com/ pour plus dinformations.", "diagnosis_security_vulnerable_to_meltdown_details": "Pour résoudre ce problème, vous devez mettre à niveau votre système et redémarrer pour charger le nouveau noyau Linux (ou contacter votre fournisseur de serveur si cela ne fonctionne pas). Voir https://meltdownattack.com/ pour plus dinformations.",
"diagnosis_description_basesystem": "Système de base", "diagnosis_description_basesystem": "Système de base",
@ -596,14 +596,14 @@
"diagnosis_description_web": "Web", "diagnosis_description_web": "Web",
"diagnosis_basesystem_hardware_board": "Le modèle de carte du serveur est {model}", "diagnosis_basesystem_hardware_board": "Le modèle de carte du serveur est {model}",
"diagnosis_basesystem_hardware": "Larchitecture du serveur est {virt} {arch}", "diagnosis_basesystem_hardware": "Larchitecture du serveur est {virt} {arch}",
"group_already_exist_on_system_but_removing_it": "Le groupe {group} est déjà présent dans les groupes du système, mais YunoHost va le supprimer", "group_already_exist_on_system_but_removing_it": "Le groupe {group} est déjà présent dans les groupes du système, mais YunoHost va le supprimer...",
"certmanager_warning_subdomain_dns_record": "Le sous-domaine '{subdomain:s}' ne résout pas vers la même adresse IP que '{domain:s}'. Certaines fonctionnalités seront indisponibles tant que vous naurez pas corrigé cela et regénéré le certificat.", "certmanager_warning_subdomain_dns_record": "Le sous-domaine '{subdomain:s}' ne résout pas vers la même adresse IP que '{domain:s}'. Certaines fonctionnalités seront indisponibles tant que vous naurez pas corrigé cela et regénéré le certificat.",
"domain_cannot_add_xmpp_upload": "Vous ne pouvez pas ajouter de domaine commençant par 'xmpp-upload.'. Ce type de nom est réservé à la fonctionnalité dupload XMPP intégrée dans YunoHost.", "domain_cannot_add_xmpp_upload": "Vous ne pouvez pas ajouter de domaine commençant par 'xmpp-upload.'. Ce type de nom est réservé à la fonctionnalité dupload XMPP intégrée dans YunoHost.",
"diagnosis_mail_outgoing_port_25_ok": "Le serveur de messagerie SMTP peut envoyer des e-mails (le port sortant 25 n'est pas bloqué).", "diagnosis_mail_outgoing_port_25_ok": "Le serveur de messagerie SMTP peut envoyer des courriels (le port sortant 25 n'est pas bloqué).",
"diagnosis_mail_outgoing_port_25_blocked_details": "Vous devez dabord essayer de débloquer le port sortant 25 dans votre interface de routeur Internet ou votre interface dhébergement. (Certains hébergeurs peuvent vous demander de leur envoyer un ticket de support pour cela).", "diagnosis_mail_outgoing_port_25_blocked_details": "Vous devez dabord essayer de débloquer le port sortant 25 dans votre interface de routeur Internet ou votre interface dhébergement. (Certains hébergeurs peuvent vous demander de leur envoyer un ticket de support pour cela).",
"diagnosis_mail_ehlo_bad_answer": "Un service non SMTP a répondu sur le port 25 en IPv{ipversion}", "diagnosis_mail_ehlo_bad_answer": "Un service non SMTP a répondu sur le port 25 en IPv{ipversion}",
"diagnosis_mail_ehlo_bad_answer_details": "Cela peut être dû à une autre machine qui répond au lieu de votre serveur.", "diagnosis_mail_ehlo_bad_answer_details": "Cela peut être dû à une autre machine qui répond au lieu de votre serveur.",
"diagnosis_mail_ehlo_wrong": "Un autre serveur de messagerie SMTP répond sur IPv{ipversion}. Votre serveur ne sera probablement pas en mesure de recevoir des e-mails.", "diagnosis_mail_ehlo_wrong": "Un autre serveur de messagerie SMTP répond sur IPv{ipversion}. Votre serveur ne sera probablement pas en mesure de recevoir des courriel.",
"diagnosis_mail_ehlo_could_not_diagnose": "Impossible de diagnostiquer si le serveur de messagerie postfix est accessible de lextérieur en IPv{ipversion}.", "diagnosis_mail_ehlo_could_not_diagnose": "Impossible de diagnostiquer si le serveur de messagerie postfix est accessible de lextérieur en IPv{ipversion}.",
"diagnosis_mail_ehlo_could_not_diagnose_details": "Erreur : {error}", "diagnosis_mail_ehlo_could_not_diagnose_details": "Erreur : {error}",
"diagnosis_mail_fcrdns_dns_missing": "Aucun DNS inverse nest défini pour IPv{ipversion}. Certains e-mails seront peut-être refusés ou considérés comme des spam.", "diagnosis_mail_fcrdns_dns_missing": "Aucun DNS inverse nest défini pour IPv{ipversion}. Certains e-mails seront peut-être refusés ou considérés comme des spam.",
@ -618,13 +618,13 @@
"diagnosis_mail_queue_too_big": "Trop de-mails en attente dans la file d'attente ({nb_pending} e-mails)", "diagnosis_mail_queue_too_big": "Trop de-mails en attente dans la file d'attente ({nb_pending} e-mails)",
"global_settings_setting_smtp_allow_ipv6": "Autoriser l'utilisation dIPv6 pour recevoir et envoyer du courrier", "global_settings_setting_smtp_allow_ipv6": "Autoriser l'utilisation dIPv6 pour recevoir et envoyer du courrier",
"diagnosis_security_all_good": "Aucune vulnérabilité de sécurité critique na été trouvée.", "diagnosis_security_all_good": "Aucune vulnérabilité de sécurité critique na été trouvée.",
"diagnosis_display_tip": "Pour voir les problèmes détectés, vous pouvez accéder à la section Diagnostic du webadmin ou exécuter « yunohost diagnostic show --issues » à partir de la ligne de commande.", "diagnosis_display_tip": "Pour voir les problèmes détectés, vous pouvez accéder à la section Diagnostic du webadmin ou exécuter « yunohost diagnosis show --issues » à partir de la ligne de commande.",
"diagnosis_ip_global": "IP globale : <code>{global}</code>", "diagnosis_ip_global": "IP globale : <code>{global}</code>",
"diagnosis_ip_local": "IP locale : <code>{local}</code>", "diagnosis_ip_local": "IP locale : <code>{local}</code>",
"diagnosis_dns_point_to_doc": "Veuillez consulter la documentation sur <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si vous avez besoin daide pour configurer les enregistrements DNS.", "diagnosis_dns_point_to_doc": "Veuillez consulter la documentation sur <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si vous avez besoin daide pour configurer les enregistrements DNS.",
"diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Certains fournisseurs ne vous laisseront pas débloquer le port sortant 25 parce quils ne se soucient pas de la neutralité du Net. <br> - Certains dentre eux offrent lalternative d'<a href='https://yunohost.org/#/smtp_relay'>utiliser un serveur de messagerie relai</a> bien que cela implique que le relai sera en mesure despionner votre trafic de messagerie. <br> - Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a> <br> - Vous pouvez également envisager de passer à <a href='https://yunohost.org/#/isp'>un fournisseur plus respectueux de la neutralité du net</a>", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Certains fournisseurs ne vous laisseront pas débloquer le port sortant 25 parce quils ne se soucient pas de la neutralité du Net. <br> - Certains dentre eux offrent lalternative d'<a href='https://yunohost.org/#/smtp_relay'>utiliser un serveur de messagerie relai</a> bien que cela implique que le relai sera en mesure despionner votre trafic de messagerie. <br> - Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a> <br> - Vous pouvez également envisager de passer à <a href='https://yunohost.org/#/isp'>un fournisseur plus respectueux de la neutralité du net</a>",
"diagnosis_mail_ehlo_ok": "Le serveur de messagerie SMTP est accessible de l'extérieur et peut donc recevoir des e-mails !", "diagnosis_mail_ehlo_ok": "Le serveur de messagerie SMTP est accessible de l'extérieur et peut donc recevoir des courriels!",
"diagnosis_mail_ehlo_unreachable": "Le serveur de messagerie SMTP est inaccessible de lextérieur en IPv{ipversion}. Il ne pourra pas recevoir de-mails.", "diagnosis_mail_ehlo_unreachable": "Le serveur de messagerie SMTP est inaccessible de lextérieur en IPv{ipversion}. Il ne pourra pas recevoir des courriels.",
"diagnosis_mail_ehlo_unreachable_details": "Impossible d'ouvrir une connexion sur le port 25 à votre serveur en IPv{ipversion}. Il semble inaccessible. <br> 1. La cause la plus courante de ce problème est que le port 25 <a href='https://yunohost.org/isp_box_config'>n'est pas correctement redirigé vers votre serveur</a>. <br> 2. Vous devez également vous assurer que le service postfix est en cours d'exécution. <br> 3. Sur les configurations plus complexes: assurez-vous qu'aucun pare-feu ou proxy inversé n'interfère.", "diagnosis_mail_ehlo_unreachable_details": "Impossible d'ouvrir une connexion sur le port 25 à votre serveur en IPv{ipversion}. Il semble inaccessible. <br> 1. La cause la plus courante de ce problème est que le port 25 <a href='https://yunohost.org/isp_box_config'>n'est pas correctement redirigé vers votre serveur</a>. <br> 2. Vous devez également vous assurer que le service postfix est en cours d'exécution. <br> 3. Sur les configurations plus complexes: assurez-vous qu'aucun pare-feu ou proxy inversé n'interfère.",
"diagnosis_mail_ehlo_wrong_details": "Le EHLO reçu par le serveur de diagnostique distant en IPv{ipversion} est différent du domaine de votre serveur. <br> EHLO reçu: <code>{wrong_ehlo}</code> <br> Attendu : <code>{right_ehlo}</code> <br> La cause la plus courante ce problème est que le port 25 <a href='https://yunohost.org/isp_box_config'> nest pas correctement redirigé vers votre serveur </a>. Vous pouvez également vous assurer quaucun pare-feu ou proxy inversé ninterfère.", "diagnosis_mail_ehlo_wrong_details": "Le EHLO reçu par le serveur de diagnostique distant en IPv{ipversion} est différent du domaine de votre serveur. <br> EHLO reçu: <code>{wrong_ehlo}</code> <br> Attendu : <code>{right_ehlo}</code> <br> La cause la plus courante ce problème est que le port 25 <a href='https://yunohost.org/isp_box_config'> nest pas correctement redirigé vers votre serveur </a>. Vous pouvez également vous assurer quaucun pare-feu ou proxy inversé ninterfère.",
"diagnosis_mail_fcrdns_nok_alternatives_4": "Certains fournisseurs ne vous laisseront pas configurer votre DNS inversé (ou leur fonctionnalité pourrait être cassée …). Si vous rencontrez des problèmes à cause de cela, envisagez les solutions suivantes : <br> - Certains FAI fournissent lalternative de <a href='https://yunohost.org/#/smtp_relay'>à laide dun relais de serveur de messagerie</a> bien que cela implique que le relais pourra espionner votre trafic de messagerie. <br> - Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a> <br> - Enfin, il est également possible de <a href='https://yunohost.org/#/isp'>changer de fournisseur</a>", "diagnosis_mail_fcrdns_nok_alternatives_4": "Certains fournisseurs ne vous laisseront pas configurer votre DNS inversé (ou leur fonctionnalité pourrait être cassée …). Si vous rencontrez des problèmes à cause de cela, envisagez les solutions suivantes : <br> - Certains FAI fournissent lalternative de <a href='https://yunohost.org/#/smtp_relay'>à laide dun relais de serveur de messagerie</a> bien que cela implique que le relais pourra espionner votre trafic de messagerie. <br> - Une alternative respectueuse de la vie privée consiste à utiliser un VPN *avec une IP publique dédiée* pour contourner ce type de limites. Voir <a href='https://yunohost.org/#/vpn_advantage'>https://yunohost.org/#/vpn_advantage</a> <br> - Enfin, il est également possible de <a href='https://yunohost.org/#/isp'>changer de fournisseur</a>",
@ -637,7 +637,7 @@
"diagnosis_http_hairpinning_issue_details": "C'est probablement à cause de la box/routeur de votre fournisseur d'accès internet. Par conséquent, les personnes extérieures à votre réseau local pourront accéder à votre serveur comme prévu, mais pas les personnes internes au réseau local (comme vous, probablement ?) si elles utilisent le nom de domaine ou l'IP globale. Vous pourrez peut-être améliorer la situation en consultant <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>", "diagnosis_http_hairpinning_issue_details": "C'est probablement à cause de la box/routeur de votre fournisseur d'accès internet. Par conséquent, les personnes extérieures à votre réseau local pourront accéder à votre serveur comme prévu, mais pas les personnes internes au réseau local (comme vous, probablement ?) si elles utilisent le nom de domaine ou l'IP globale. Vous pourrez peut-être améliorer la situation en consultant <a href='https://yunohost.org/dns_local_network'>https://yunohost.org/dns_local_network</a>",
"diagnosis_http_partially_unreachable": "Le domaine {domain} semble inaccessible en HTTP depuis lextérieur du réseau local en IPv{failed}, bien quil fonctionne en IPv{passed}.", "diagnosis_http_partially_unreachable": "Le domaine {domain} semble inaccessible en HTTP depuis lextérieur du réseau local en IPv{failed}, bien quil fonctionne en IPv{passed}.",
"diagnosis_http_nginx_conf_not_up_to_date": "La configuration Nginx de ce domaine semble avoir été modifiée manuellement et empêche YunoHost de diagnostiquer si elle est accessible en HTTP.", "diagnosis_http_nginx_conf_not_up_to_date": "La configuration Nginx de ce domaine semble avoir été modifiée manuellement et empêche YunoHost de diagnostiquer si elle est accessible en HTTP.",
"diagnosis_http_nginx_conf_not_up_to_date_details": "Pour corriger la situation, inspectez la différence avec la ligne de commande en utilisant les outils <cmd>yunohost regen-conf nginx --dry-run --with-diff</cmd> et si vous êtes daccord, appliquez les modifications avec <cmd>yunohost tools regen-conf nginx --force</cmd>.", "diagnosis_http_nginx_conf_not_up_to_date_details": "Pour corriger la situation, inspectez la différence avec la ligne de commande en utilisant les outils <cmd>yunohost tools regen-conf nginx --dry-run --with-diff</cmd> et si vous êtes daccord, appliquez les modifications avec <cmd>yunohost tools regen-conf nginx --force</cmd>.",
"backup_archive_cant_retrieve_info_json": "Impossible d'avoir des informations sur l'archive '{archive}' ... Le fichier info.json ne peut pas être trouvé (ou n'est pas un fichier json valide).", "backup_archive_cant_retrieve_info_json": "Impossible d'avoir des informations sur l'archive '{archive}' ... Le fichier info.json ne peut pas être trouvé (ou n'est pas un fichier json valide).",
"backup_archive_corrupted": "Il semble que l'archive de la sauvegarde '{archive}' est corrompue : {error}", "backup_archive_corrupted": "Il semble que l'archive de la sauvegarde '{archive}' est corrompue : {error}",
"diagnosis_ip_no_ipv6_tip": "L'utilisation de IPv6 n'est pas obligatoire pour le fonctionnement de votre serveur, mais cela contribue à la santé d'Internet dans son ensemble. IPv6 généralement configuré automatiquement par votre système ou votre FAI s'il est disponible. Autrement, vous devrez prendre quelque minutes pour le configurer manuellement à l'aide de cette documentation: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. Si vous ne pouvez pas activer IPv6 ou si c'est trop technique pour vous, vous pouvez aussi ignorer cet avertissement sans que cela pose problème.", "diagnosis_ip_no_ipv6_tip": "L'utilisation de IPv6 n'est pas obligatoire pour le fonctionnement de votre serveur, mais cela contribue à la santé d'Internet dans son ensemble. IPv6 généralement configuré automatiquement par votre système ou votre FAI s'il est disponible. Autrement, vous devrez prendre quelque minutes pour le configurer manuellement à l'aide de cette documentation: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. Si vous ne pouvez pas activer IPv6 ou si c'est trop technique pour vous, vous pouvez aussi ignorer cet avertissement sans que cela pose problème.",
@ -648,6 +648,25 @@
"diagnosis_domain_expiration_warning": "Certains domaines vont expirer prochainement !", "diagnosis_domain_expiration_warning": "Certains domaines vont expirer prochainement !",
"diagnosis_domain_expiration_error": "Certains domaines vont expirer TRÈS PROCHAINEMENT !", "diagnosis_domain_expiration_error": "Certains domaines vont expirer TRÈS PROCHAINEMENT !",
"diagnosis_domain_expires_in": "{domain} expire dans {days} jours.", "diagnosis_domain_expires_in": "{domain} expire dans {days} jours.",
"certmanager_domain_not_diagnosed_yet": "Il n'y a pas encore de résultat de diagnostic pour le domaine %s. Merci de relancer un diagnostic pour les catégories 'Enregistrements DNS' et 'Web' dans la section Diagnostique pour vérifier si le domaine est prêt pour Let's Encrypt. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)", "certmanager_domain_not_diagnosed_yet": "Il n'y a pas encore de résultat de diagnostic pour le domaine {domain}. Merci de relancer un diagnostic pour les catégories 'Enregistrements DNS' et 'Web' dans la section Diagnostique pour vérifier si le domaine est prêt pour Let's Encrypt. (Ou si vous savez ce que vous faites, utilisez '--no-checks' pour désactiver la vérification.)",
"diagnosis_swap_tip": "Merci d'être prudent et conscient que si vous hébergez une partition SWAP sur une carte SD ou un disque SSD, cela risque de réduire drastiquement lespérance de vie du périphérique." "diagnosis_swap_tip": "Merci d'être prudent et conscient que si vous hébergez une partition SWAP sur une carte SD ou un disque SSD, cela risque de réduire drastiquement lespérance de vie du périphérique.",
"restore_already_installed_apps": "Les applications suivantes ne peuvent pas être restaurées car elles sont déjà installées : {apps}",
"regenconf_need_to_explicitly_specify_ssh": "La configuration de ssh a été modifiée manuellement. Vous devez explicitement indiquer la mention --force à \"ssh\" pour appliquer les changements.",
"migration_0015_cleaning_up": "Nettoyage du cache et des paquets qui ne sont plus utiles …",
"migration_0015_specific_upgrade": "Commencement de la mise à jour des paquets du système qui doivent être mis à jour séparément …",
"migration_0015_modified_files": "Veuillez noter que les fichiers suivants ont été modifiés manuellement et pourraient être écrasés à la suite de la mise à niveau : {manually_modified_files}",
"migration_0015_problematic_apps_warning": "Veuillez noter que des applications qui peuvent poser problèmes ont été détectées. Il semble qu'elles n'aient pas été installées à partir du catalogue d'applications YunoHost, ou bien qu'elles ne soient pas signalées comme \"fonctionnelles\". Par conséquent, il n'est pas possible de garantir que les applications suivantes fonctionneront encore après la mise à niveau : {problematic_apps}",
"migration_0015_general_warning": "Veuillez noter que cette migration est une opération délicate. L'équipe YunoHost a fait de son mieux pour la revérifier et la tester, mais la migration pourrait quand même casser des éléments du système ou de ses applications.\n\nIl est donc recommandé :\n…- de faire une sauvegarde de toute donnée ou application critique. Plus d'informations ici https://yunohost.org/backup ;\n…- d'être patient après le lancement de la migration. Selon votre connexion internet et votre matériel, la mise à niveau peut prendre jusqu'à quelques heures.",
"migration_0015_system_not_fully_up_to_date": "Votre système n'est pas entièrement à jour. Veuillez effectuer une mise à jour normale avant de lancer la migration vers Buster.",
"migration_0015_not_enough_free_space": "L'espace libre est très faible dans /var/ ! Vous devriez avoir au moins 1 Go de libre pour effectuer cette migration.",
"migration_0015_not_stretch": "La distribution Debian actuelle n'est pas Stretch !",
"migration_0015_yunohost_upgrade": "Démarrage de la mise à jour de YunoHost …",
"migration_0015_still_on_stretch_after_main_upgrade": "Quelque chose s'est mal passé lors de la mise à niveau, le système semble toujours être sous Debian Stretch",
"migration_0015_main_upgrade": "Démarrage de la mise à niveau générale …",
"migration_0015_patching_sources_list": "Mise à jour du fichier sources.lists …",
"migration_0015_start": "Démarrage de la migration vers Buster",
"migration_description_0015_migrate_to_buster": "Mise à niveau du système vers Debian Buster et YunoHost 4.x",
"diagnosis_dns_try_dyndns_update_force": "La configuration DNS de ce domaine devrait être automatiquement gérée par Yunohost. Si ce n'est pas le cas, vous pouvez essayer de forcer une mise à jour en utilisant <cmd>yunohost dyndns update --force</cmd>.",
"app_packaging_format_not_supported": "Cette application ne peut pas être installée car son format n'est pas pris en charge par votre version de YunoHost. Vous devriez probablement envisager de mettre à jour votre système.",
"migration_0015_weak_certs": "Il a été constaté que les certificats suivants utilisent encore des algorithmes de signature peu robustes et doivent être mis à jour pour être compatibles avec la prochaine version de nginx : {certs}"
} }

View file

@ -1,11 +1,11 @@
{ {
"app_already_installed": "{app:s} è già installata", "app_already_installed": "{app:s} è già installata",
"app_extraction_failed": "Impossibile estrarre i file di installazione", "app_extraction_failed": "Impossibile estrarre i file di installazione",
"app_not_installed": "{app:s} non è installata", "app_not_installed": "Impossibile trovare l'applicazione {app:s} nell'elenco delle applicazioni installate: {all_apps}",
"app_unknown": "Applicazione sconosciuta", "app_unknown": "Applicazione sconosciuta",
"ask_email": "Indirizzo email", "ask_email": "Indirizzo email",
"ask_password": "Password", "ask_password": "Password",
"backup_archive_name_exists": "Il nome dell'archivio del backup è già esistente", "backup_archive_name_exists": "Il nome dell'archivio del backup è già esistente.",
"backup_created": "Backup completo", "backup_created": "Backup completo",
"backup_invalid_archive": "Archivio di backup non valido", "backup_invalid_archive": "Archivio di backup non valido",
"backup_output_directory_not_empty": "La directory di output non è vuota", "backup_output_directory_not_empty": "La directory di output non è vuota",
@ -27,32 +27,32 @@
"user_deleted": "L'utente è stato cancellato", "user_deleted": "L'utente è stato cancellato",
"admin_password": "Password dell'amministrazione", "admin_password": "Password dell'amministrazione",
"admin_password_change_failed": "Impossibile cambiare la password", "admin_password_change_failed": "Impossibile cambiare la password",
"admin_password_changed": "La password dell'amministrazione è stata cambiata", "admin_password_changed": "La password d'amministrazione è stata cambiata",
"app_install_files_invalid": "Non sono validi i file di installazione", "app_install_files_invalid": "Questi file non possono essere installati",
"app_manifest_invalid": "Manifesto dell'applicazione non valido: {error}", "app_manifest_invalid": "C'è qualcosa di scorretto nel manifesto dell'applicazione: {error}",
"app_not_correctly_installed": "{app:s} sembra di non essere installata correttamente", "app_not_correctly_installed": "{app:s} sembra di non essere installata correttamente",
"app_not_properly_removed": "{app:s} non è stata correttamente rimossa", "app_not_properly_removed": "{app:s} non è stata correttamente rimossa",
"action_invalid": "L'azione '{action:s}' non è valida", "action_invalid": "L'azione '{action:s}' non è valida",
"app_removed": "{app:s} è stata rimossa", "app_removed": "{app:s} rimossa",
"app_sources_fetch_failed": "Impossibile riportare i file sorgenti", "app_sources_fetch_failed": "Impossibile riportare i file sorgenti, l'URL è corretto?",
"app_upgrade_failed": "Impossibile aggiornare {app:s}", "app_upgrade_failed": "Impossibile aggiornare {app:s}: {error}",
"app_upgraded": "{app:s} è stata aggiornata", "app_upgraded": "{app:s} aggiornata",
"app_requirements_checking": "Controllo i pacchetti richiesti per {app}", "app_requirements_checking": "Controllo i pacchetti richiesti per {app}...",
"app_requirements_unmeet": "Requisiti non soddisfatti per {app}, il pacchetto {pkgname} ({version}) deve essere {spec}", "app_requirements_unmeet": "Requisiti non soddisfatti per {app}, il pacchetto {pkgname} ({version}) deve essere {spec}",
"ask_firstname": "Nome", "ask_firstname": "Nome",
"ask_lastname": "Cognome", "ask_lastname": "Cognome",
"ask_main_domain": "Dominio principale", "ask_main_domain": "Dominio principale",
"ask_new_admin_password": "Nuova password dell'amministrazione", "ask_new_admin_password": "Nuova password dell'amministrazione",
"backup_app_failed": "Non è possibile fare il backup dell'applicazione '{app:s}'", "backup_app_failed": "Non è possibile fare il backup {app:s}",
"backup_archive_app_not_found": "L'applicazione '{app:s}' non è stata trovata nel archivio di backup", "backup_archive_app_not_found": "{app:s} non è stata trovata nel archivio di backup",
"app_argument_choice_invalid": "Scelta non valida per l'argomento '{name:s}', deve essere uno di {choices:s}", "app_argument_choice_invalid": "Usa una delle seguenti scelte '{choices:s}' per il parametro '{name:s}'",
"app_argument_invalid": "Valore non valido per '{name:s}': {error:s}", "app_argument_invalid": "Scegli un valore valido per il parametro '{name:s}': {error:s}",
"app_argument_required": "L'argomento '{name:s}' è requisito", "app_argument_required": "L'argomento '{name:s}' è requisito",
"app_id_invalid": "Identificativo dell'applicazione non valido", "app_id_invalid": "Identificativo dell'applicazione non valido",
"app_unsupported_remote_type": "Il tipo remoto usato per l'applicazione non è supportato", "app_unsupported_remote_type": "Il tipo remoto usato per l'applicazione non è supportato",
"backup_archive_broken_link": "Non è possibile accedere al archivio di backup (link rotto verso {path:s})", "backup_archive_broken_link": "Non è possibile accedere all'archivio di backup (link rotto verso {path:s})",
"backup_archive_name_unknown": "Archivio di backup locale chiamato '{name:s}' sconosciuto", "backup_archive_name_unknown": "Archivio di backup locale chiamato '{name:s}' sconosciuto",
"backup_archive_open_failed": "Non è possibile aprire l'archivio di backup", "backup_archive_open_failed": "Impossibile aprire l'archivio di backup",
"backup_cleaning_failed": "Non è possibile pulire la directory temporanea di backup", "backup_cleaning_failed": "Non è possibile pulire la directory temporanea di backup",
"backup_creation_failed": "La creazione del backup è fallita", "backup_creation_failed": "La creazione del backup è fallita",
"backup_delete_error": "Impossibile cancellare '{path:s}'", "backup_delete_error": "Impossibile cancellare '{path:s}'",
@ -171,23 +171,23 @@
"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_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_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", "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",
"app_already_installed_cant_change_url": "Questa applicazione è già installata. L'URL non può essere cambiato solo da questa funzione. Guarda se `app changeurl` è disponibile.", "app_already_installed_cant_change_url": "Questa applicazione è già installata. L'URL non può essere cambiato solo da questa funzione. Controlla se `app changeurl` è disponibile.",
"app_already_up_to_date": "{app:s} è già aggiornata", "app_already_up_to_date": "{app:s} è già aggiornata",
"app_change_url_failed_nginx_reload": "Riavvio di nginx fallito. Questo è il risultato di 'nginx -t':\n{nginx_errors:s}", "app_change_url_failed_nginx_reload": "Non riesco a riavviare NGINX. Questo è il risultato di 'nginx -t':\n{nginx_errors:s}",
"app_change_url_identical_domains": "Il vecchio ed il nuovo dominio/percorso_url sono identici ('{domain:s}{path:s}'), nessuna operazione necessaria.", "app_change_url_identical_domains": "Il vecchio ed il nuovo dominio/percorso_url sono identici ('{domain:s}{path:s}'), nessuna operazione necessaria.",
"app_change_url_no_script": "L'applicazione '{app_name:s}' non supporta ancora la modifica dell'URL. Forse dovresti aggiornare l'applicazione.", "app_change_url_no_script": "L'applicazione '{app_name:s}' non supporta ancora la modifica dell'URL. Forse dovresti aggiornarla.",
"app_change_url_success": "URL dell'applicazione {app:s} cambiato con successo in {domain:s}{path:s}", "app_change_url_success": "L'URL dell'applicazione {app:s} è stato cambiato 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_make_default_location_already_used": "Impostazione dell'applicazione '{app}' come predefinita del dominio non riuscita perché il dominio {domain} è già in uso 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_location_unavailable": "Questo URL non è più 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": "Aggiornamento di {app}...",
"app_upgrade_some_app_failed": "Impossibile aggiornare alcune applicazioni", "app_upgrade_some_app_failed": "Impossibile aggiornare alcune applicazioni",
"backup_abstract_method": "Questo metodo di backup non è ancora stato implementato", "backup_abstract_method": "Questo metodo di backup deve essere ancora implementato",
"backup_applying_method_borg": "Inviando tutti i file da salvare nel backup nel deposito borg-backup…", "backup_applying_method_borg": "Invio di tutti i file del backup nel deposito borg-backup...",
"backup_applying_method_copy": "Copiando tutti i files nel 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_custom": "Chiamando il metodo di backup personalizzato '{method:s}'...",
"backup_applying_method_tar": "Creando l'archivio tar del backup…", "backup_applying_method_tar": "Creando l'archivio TAR del backup...",
"backup_archive_system_part_not_available": "La parte di sistema '{part:s}' non è disponibile in questo backup", "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", "backup_archive_writing_error": "Impossibile aggiungere i file '{source:s}' (indicati nell'archivio '{dest:s}') al backup nell'archivio compresso '{archive:s}'",
"backup_ask_for_copying_if_needed": "Alcuni files non possono essere preparati al backup utilizzando il metodo che consente di evitare il consumo temporaneo di spazio nel sistema. Per eseguire il backup, {size:s}MB dovranno essere utilizzati temporaneamente. Sei d'accordo?", "backup_ask_for_copying_if_needed": "Alcuni files non possono essere preparati al backup utilizzando il metodo che consente di evitare il consumo temporaneo di spazio nel sistema. Per eseguire il backup, {size:s}MB dovranno essere utilizzati temporaneamente. Sei d'accordo?",
"backup_borg_not_implemented": "Il metodo di backup Borg non è ancora stato implementato", "backup_borg_not_implemented": "Il metodo di backup Borg non è ancora stato implementato",
"backup_cant_mount_uncompress_archive": "Impossibile montare in modalità sola lettura la cartella di archivio non compressa", "backup_cant_mount_uncompress_archive": "Impossibile montare in modalità sola lettura la cartella di archivio non compressa",
@ -212,15 +212,15 @@
"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.", "aborting": "Annullamento.",
"admin_password_too_long": "Per favore scegli una password più corta di 127 caratteri", "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_not_upgraded": "Impossibile aggiornare le applicazioni '{failed_app}' e di conseguenza l'aggiornamento delle seguenti applicazione è stato cancellato: {apps}",
"app_start_install": "Installando l'applicazione {app}…", "app_start_install": "Installando '{app}'...",
"app_start_remove": "Rimuovendo l'applicazione {app}…", "app_start_remove": "Rimozione di {app}...",
"app_start_backup": "Raccogliendo file da salvare nel backup per {app}…", "app_start_backup": "Raccogliendo file da salvare nel backup per '{app}'...",
"app_start_restore": "Ripristinando l'applicazione {app}…", "app_start_restore": "Ripristino di '{app}'...",
"app_upgrade_several_apps": "Le seguenti app saranno aggiornate : {apps}", "app_upgrade_several_apps": "Le seguenti applicazioni saranno aggiornate : {apps}",
"ask_new_domain": "Nuovo dominio", "ask_new_domain": "Nuovo dominio",
"ask_new_path": "Nuovo percorso", "ask_new_path": "Nuovo percorso",
"backup_actually_backuping": "Creando un archivio di backup con i file raccolti…", "backup_actually_backuping": "Creazione di un archivio di backup con i file raccolti...",
"backup_mount_archive_for_restore": "Preparando l'archivio per il ripristino…", "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_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_renew_success": "Certificato di Let's Encrypt rinnovato con successo per il dominio {domain:s}!",
@ -233,7 +233,7 @@
"password_too_simple_4": "La password deve essere lunga almeno 12 caratteri e contenere numeri, maiuscole e minuscole", "password_too_simple_4": "La password deve essere lunga almeno 12 caratteri e contenere numeri, maiuscole e minuscole",
"users_available": "Utenti disponibili:", "users_available": "Utenti disponibili:",
"yunohost_ca_creation_success": "L'autorità di certificazione locale è stata creata.", "yunohost_ca_creation_success": "L'autorità di certificazione locale è stata creata.",
"app_action_cannot_be_ran_because_required_services_down": "Questa app richiede alcuni servizi che attualmente non sono attivi. Prima di continuare, dovresti provare a riavviare i seguenti servizi (e possibilmente capire perchè questi non siano attivi) : {services}", "app_action_cannot_be_ran_because_required_services_down": "I seguenti servizi dovrebbero essere in funzione per completare questa azione: {services}. Prova a riavviarli per proseguire (e possibilmente cercare di capire come ma non funzionano più).",
"backup_output_symlink_dir_broken": "Hai un collegamento errato alla tua cartella di archiviazione '{path:s}'. Potresti avere delle impostazioni particolari per salvare i tuoi dati su un altro spazio, in questo caso probabilmente ti sei scordato di rimontare o collegare il tuo hard disk o la chiavetta usb.", "backup_output_symlink_dir_broken": "Hai un collegamento errato alla tua cartella di archiviazione '{path:s}'. Potresti avere delle impostazioni particolari per salvare i tuoi dati su un altro spazio, in questo caso probabilmente ti sei scordato di rimontare o collegare il tuo hard disk o la chiavetta usb.",
"certmanager_conflicting_nginx_file": "Impossibile preparare il dominio per il controllo ACME: il file di configurazione nginx {filepath:s} è in conflitto e dovrebbe essere prima rimosso", "certmanager_conflicting_nginx_file": "Impossibile preparare il dominio per il controllo ACME: il file di configurazione nginx {filepath:s} è in conflitto e dovrebbe essere prima rimosso",
"certmanager_couldnt_fetch_intermediate_cert": "Tempo scaduto durante il tentativo di recupero di un certificato intermedio da Let's Encrypt. Installazione/rinnovo non riuscito - per favore riprova più tardi.", "certmanager_couldnt_fetch_intermediate_cert": "Tempo scaduto durante il tentativo di recupero di un certificato intermedio da Let's Encrypt. Installazione/rinnovo non riuscito - per favore riprova più tardi.",
@ -264,7 +264,7 @@
"global_settings_reset_success": "Successo. Le tue impostazioni precedenti sono state salvate in {path:s}", "global_settings_reset_success": "Successo. Le tue impostazioni precedenti sono state salvate in {path:s}",
"global_settings_setting_example_bool": "Esempio di opzione booleana", "global_settings_setting_example_bool": "Esempio di opzione booleana",
"global_settings_setting_example_enum": "Esempio di opzione enum", "global_settings_setting_example_enum": "Esempio di opzione enum",
"already_up_to_date": "Niente da fare! Tutto è già aggiornato!", "already_up_to_date": "Niente da fare. Tutto è già aggiornato.",
"global_settings_setting_example_int": "Esempio di opzione int", "global_settings_setting_example_int": "Esempio di opzione int",
"global_settings_setting_example_string": "Esempio di opzione string", "global_settings_setting_example_string": "Esempio di opzione string",
"global_settings_setting_security_nginx_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server web nginx. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)", "global_settings_setting_security_nginx_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server web nginx. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)",
@ -335,5 +335,19 @@
"migration_0003_not_jessie": "La distribuzione attuale non è Jessie!", "migration_0003_not_jessie": "La distribuzione attuale non è Jessie!",
"migration_0003_system_not_fully_up_to_date": "Il tuo sistema non è completamente aggiornato. Per favore prima esegui un aggiornamento normale prima di migrare a stretch.", "migration_0003_system_not_fully_up_to_date": "Il tuo sistema non è completamente aggiornato. Per favore prima esegui un aggiornamento normale prima di migrare a stretch.",
"this_action_broke_dpkg": "Questa azione ha danneggiato dpkg/apt (i gestori di pacchetti del sistema)… Puoi provare a risolvere questo problema connettendoti via SSH ed eseguendo `sudo dpkg --configure -a`.", "this_action_broke_dpkg": "Questa azione ha danneggiato dpkg/apt (i gestori di pacchetti del sistema)… Puoi provare a risolvere questo problema connettendoti via SSH ed eseguendo `sudo dpkg --configure -a`.",
"app_action_broke_system": "Questa azione sembra avere roto servizi importanti: {services}" "app_action_broke_system": "Questa azione sembra avere rotto questi servizi importanti: {services}",
"app_remove_after_failed_install": "Rimozione dell'applicazione a causa del fallimento dell'installazione...",
"app_install_script_failed": "Si è verificato un errore nello script di installazione dell'applicazione",
"app_install_failed": "Impossibile installare {app}:{error}",
"app_full_domain_unavailable": "Spiacente, questa app deve essere installata su un proprio dominio, ma altre applicazioni sono state installate sul dominio '{domain}'. Dovresti invece usare un sotto-dominio dedicato per questa app.",
"app_upgrade_script_failed": "È stato trovato un errore nello script di aggiornamento dell'applicazione",
"apps_already_up_to_date": "Tutte le applicazioni sono aggiornate",
"apps_catalog_init_success": "Catalogo delle applicazioni inizializzato!",
"apps_catalog_updating": "Aggiornamento del catalogo delle applicazioni…",
"apps_catalog_failed_to_download": "Impossibile scaricare il catalogo delle applicazioni {apps_catalog} : {error}",
"apps_catalog_obsolete_cache": "La cache del catalogo della applicazioni è vuoto o obsoleto.",
"apps_catalog_update_success": "Il catalogo delle applicazioni è stato aggiornato!",
"backup_archive_corrupted": "Sembra che l'archivio di backup '{archive}' sia corrotto: {error}",
"backup_archive_cant_retrieve_info_json": "Impossibile caricare informazione per l'archivio '{archive}'... Impossibile scaricare info.json (oppure non è un json valido).",
"app_packaging_format_not_supported": "Quest'applicazione non può essere installata perché il formato non è supportato dalla vostra versione di YunoHost. Dovreste considerare di aggiornare il vostro sistema."
} }

View file

@ -472,7 +472,7 @@
"migrations_not_pending_cant_skip": "Aquestas migracions son pas en espèra, las podètz pas doncas ignorar : {ids}", "migrations_not_pending_cant_skip": "Aquestas migracions son pas en espèra, las podètz pas doncas ignorar : {ids}",
"app_action_broke_system": "Aquesta accion sembla aver copat de servicis importants : {services}", "app_action_broke_system": "Aquesta accion sembla aver copat de servicis importants : {services}",
"diagnosis_display_tip_web": "Podètz anar a la seccion Diagnostic (dins lecran dacuèlh) per veire los problèmas trobats.", "diagnosis_display_tip_web": "Podètz anar a la seccion Diagnostic (dins lecran dacuèlh) per veire los problèmas trobats.",
"diagnosis_ip_no_ipv6": "Lo servidor a pas dadreça IPv5 activa.", "diagnosis_ip_no_ipv6": "Lo servidor a pas dadreça IPv6 activa.",
"diagnosis_ip_not_connected_at_all": "Lo servidor sembla pas connectat a Internet ?!", "diagnosis_ip_not_connected_at_all": "Lo servidor sembla pas connectat a Internet ?!",
"diagnosis_security_all_good": "Cap de vulnerabilitat de seguretat critica pas trobada.", "diagnosis_security_all_good": "Cap de vulnerabilitat de seguretat critica pas trobada.",
"diagnosis_description_regenconf": "Configuracion sistèma", "diagnosis_description_regenconf": "Configuracion sistèma",
@ -537,7 +537,7 @@
"group_cannot_be_deleted": "Lo grop « {group} » pòt pas èsser suprimit manualament.", "group_cannot_be_deleted": "Lo grop « {group} » pòt pas èsser suprimit manualament.",
"diagnosis_found_warnings": "Trobat {warnings} element(s) que se poirián melhorar per {category}.", "diagnosis_found_warnings": "Trobat {warnings} element(s) que se poirián melhorar per {category}.",
"diagnosis_dns_missing_record": "Segon la configuracion DNS recomandada, vos calriá ajustar un enregistrament DNS\ntipe: {type}\nnom: {name}\nvalor: {value}", "diagnosis_dns_missing_record": "Segon la configuracion DNS recomandada, vos calriá ajustar un enregistrament DNS\ntipe: {type}\nnom: {name}\nvalor: {value}",
"diagnosis_dns_discrepancy": "Segon la configuracion DNS recomandada, la valor per lenregistrament DNS\ntipe: {type}\nnom: {name}\ndeuriá èsser: {current}\nallòc de: {value}", "diagnosis_dns_discrepancy": "La configuracion DNS seguenta sembla pas la configuracion recomandada: <br>Tipe: <code>{type}</code><br>Nom: <code>{name}</code><br>Valors actualas:<code> {current]</code><br>Valor esperada: <code>{value}</code>",
"diagnosis_regenconf_manually_modified_debian_details": "Es pas problematic, mas car téner dagacher...", "diagnosis_regenconf_manually_modified_debian_details": "Es pas problematic, mas car téner dagacher...",
"diagnosis_ports_could_not_diagnose": "Impossible de diagnosticar se los pòrts son accessibles de lexterior.", "diagnosis_ports_could_not_diagnose": "Impossible de diagnosticar se los pòrts son accessibles de lexterior.",
"diagnosis_ports_could_not_diagnose_details": "Error : {error}", "diagnosis_ports_could_not_diagnose_details": "Error : {error}",
@ -580,5 +580,7 @@
"diagnosis_basesystem_hardware": "Larquitectura del servidor es {virt} {arch}", "diagnosis_basesystem_hardware": "Larquitectura del servidor es {virt} {arch}",
"diagnosis_basesystem_hardware_board": "Lo modèl de carta del servidor es {model}", "diagnosis_basesystem_hardware_board": "Lo modèl de carta del servidor es {model}",
"backup_archive_corrupted": "Sembla que larchiu de la salvagarda « {archive} » es corromput: {error}", "backup_archive_corrupted": "Sembla que larchiu de la salvagarda « {archive} » es corromput: {error}",
"diagnosis_domain_expires_in": "{domain} expiraà daquí {days} jorns." "diagnosis_domain_expires_in": "{domain} expiraà daquí {days} jorns.",
"migration_0015_cleaning_up": "Netejatge de la memòria cache e dels paquets pas mai necessaris…",
"restore_already_installed_apps": "Restauracion impossibla de las aplicacions seguentas que son ja installadas : {apps}"
} }

View file

@ -1,3 +1,16 @@
{ {
"password_too_simple_1": "密码长度至少为8个字符" "password_too_simple_1": "密码长度至少为8个字符",
"backup_created": "备份已创建",
"app_start_remove": "正在删除{app}……",
"admin_password_change_failed": "不能修改密码",
"admin_password_too_long": "请选择一个小于127个字符的密码",
"app_upgrade_failed": "不能升级{app:s}{error}",
"app_id_invalid": "无效 app ID",
"app_unknown": "未知应用",
"admin_password_changed": "管理密码已更改",
"aborting": "正在放弃。",
"admin_password": "管理密码",
"app_start_restore": "正在恢复{app}……",
"action_invalid": "无效操作 '{action:s}'",
"ask_lastname": "姓"
} }

View file

@ -0,0 +1,206 @@
#! /usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import moulinette
from moulinette import m18n
from moulinette.utils.log import configure_logging
from moulinette.interfaces.cli import colorize, get_locale
def is_installed():
return os.path.isfile('/etc/yunohost/installed')
def cli(debug, quiet, output_as, timeout, args, parser):
init_logging(interface="cli", debug=debug, quiet=quiet)
# Check that YunoHost is installed
if not is_installed():
check_command_is_valid_before_postinstall(args)
ret = moulinette.cli(
args,
output_as=output_as,
timeout=timeout,
top_parser=parser
)
sys.exit(ret)
def api(debug, host, port):
init_logging(interface="api", debug=debug)
def is_installed_api():
return {'installed': is_installed()}
# FIXME : someday, maybe find a way to disable route /postinstall if
# postinstall already done ...
ret = moulinette.api(
host=host,
port=port,
routes={('GET', '/installed'): is_installed_api},
)
sys.exit(ret)
def check_command_is_valid_before_postinstall(args):
allowed_if_not_postinstalled = ['tools postinstall',
'tools versions',
'backup list',
'backup restore',
'log display']
if (len(args) < 2 or (args[0] + ' ' + args[1] not in allowed_if_not_postinstalled)):
init_i18n()
print(colorize(m18n.g('error'), 'red') + " " + m18n.n('yunohost_not_installed'))
sys.exit(1)
def init(interface="cli", debug=False, quiet=False, logdir="/var/log/yunohost"):
"""
This is a small util function ONLY meant to be used to initialize a Yunohost
context when ran from tests or from scripts.
"""
init_logging(interface=interface, debug=debug, quiet=quiet, logdir=logdir)
init_i18n()
from moulinette.core import MoulinetteLock
lock = MoulinetteLock("yunohost", timeout=30)
lock.acquire()
return lock
def init_i18n():
# This should only be called when not willing to go through moulinette.cli
# or moulinette.api but still willing to call m18n.n/g...
m18n.load_namespace('yunohost')
m18n.set_locale(get_locale())
def init_logging(interface="cli",
debug=False,
quiet=False,
logdir="/var/log/yunohost"):
logfile = os.path.join(logdir, "yunohost-%s.log" % interface)
if not os.path.isdir(logdir):
os.makedirs(logdir, 0o750)
# ####################################################################### #
# Logging configuration for CLI (or any other interface than api...) #
# ####################################################################### #
if interface != "api":
configure_logging({
'version': 1,
'main_logger': "yunohost",
'disable_existing_loggers': True,
'formatters': {
'tty-debug': {
'format': '%(relativeCreated)-4d %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'tty': {
'level': 'DEBUG' if debug else 'INFO',
'class': 'moulinette.interfaces.cli.TTYHandler',
'formatter': 'tty-debug' if debug else '',
},
'file': {
'class': 'logging.FileHandler',
'formatter': 'precise',
'filename': logfile,
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if not quiet else ['file'],
'propagate': False,
},
'moulinette': {
'level': 'DEBUG',
'handlers': [],
'propagate': True,
},
'moulinette.interface': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if not quiet else ['file'],
'propagate': False,
},
},
'root': {
'level': 'DEBUG',
'handlers': ['file', 'tty'] if debug else ['file'],
},
})
# ####################################################################### #
# Logging configuration for API #
# ####################################################################### #
else:
configure_logging({
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'console': {
'format': '%(relativeCreated)-5d %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
'precise': {
'format': '%(asctime)-15s %(levelname)-8s %(name)s %(funcName)s - %(fmessage)s'
},
},
'filters': {
'action': {
'()': 'moulinette.utils.log.ActionFilter',
},
},
'handlers': {
'api': {
'level': 'DEBUG' if debug else 'INFO',
'class': 'moulinette.interfaces.api.APIQueueHandler',
},
'file': {
'class': 'logging.handlers.WatchedFileHandler',
'formatter': 'precise',
'filename': logfile,
'filters': ['action'],
},
'console': {
'class': 'logging.StreamHandler',
'formatter': 'console',
'stream': 'ext://sys.stdout',
'filters': ['action'],
},
},
'loggers': {
'yunohost': {
'level': 'DEBUG',
'handlers': ['file', 'api'] + ['console'] if debug else [],
'propagate': False,
},
'moulinette': {
'level': 'DEBUG',
'handlers': [],
'propagate': True,
},
},
'root': {
'level': 'DEBUG',
'handlers': ['file'] + ['console'] if debug else [],
},
})

View file

@ -90,6 +90,8 @@ def app_catalog(full=False, with_categories=False):
"description": infos['manifest']['description'], "description": infos['manifest']['description'],
"level": infos["level"], "level": infos["level"],
} }
else:
infos["manifest"]["arguments"] = _set_default_ask_questions(infos["manifest"]["arguments"])
# Trim info for categories if not using --full # Trim info for categories if not using --full
for category in catalog["categories"]: for category in catalog["categories"]:
@ -109,7 +111,6 @@ def app_catalog(full=False, with_categories=False):
return {"apps": catalog["apps"], "categories": catalog["categories"]} return {"apps": catalog["apps"], "categories": catalog["categories"]}
# Old legacy function... # Old legacy function...
def app_fetchlist(): def app_fetchlist():
logger.warning("'yunohost app fetchlist' is deprecated. Please use 'yunohost tools update --apps' instead") logger.warning("'yunohost app fetchlist' is deprecated. Please use 'yunohost tools update --apps' instead")
@ -169,6 +170,7 @@ def app_info(app, full=False):
return ret return ret
ret["manifest"] = local_manifest ret["manifest"] = local_manifest
ret["manifest"]["arguments"] = _set_default_ask_questions(ret["manifest"]["arguments"])
ret['settings'] = settings ret['settings'] = settings
absolute_app_name, _ = _parse_app_instance_name(app) absolute_app_name, _ = _parse_app_instance_name(app)
@ -182,11 +184,21 @@ def app_info(app, full=False):
def _app_upgradable(app_infos): def _app_upgradable(app_infos):
from packaging import version
# Determine upgradability # Determine upgradability
# In case there is neither update_time nor install_time, we assume the app can/has to be upgraded # In case there is neither update_time nor install_time, we assume the app can/has to be upgraded
if not app_infos.get("from_catalog", None): # Firstly use the version to know if an upgrade is available
app_is_in_catalog = bool(app_infos.get("from_catalog"))
installed_version = version.parse(app_infos.get("version", "0~ynh0"))
version_in_catalog = version.parse(app_infos.get("from_catalog", {}).get("manifest", {}).get("version", "0~ynh0"))
if app_is_in_catalog and '~ynh' in str(installed_version) and '~ynh' in str(version_in_catalog):
if installed_version < version_in_catalog:
return "yes"
if not app_is_in_catalog:
return "url_required" return "url_required"
if not app_infos["from_catalog"].get("lastUpdate") or not app_infos["from_catalog"].get("git"): if not app_infos["from_catalog"].get("lastUpdate") or not app_infos["from_catalog"].get("git"):
return "url_required" return "url_required"
@ -376,6 +388,7 @@ def app_change_url(operation_logger, app, domain, path):
env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_ID"] = app_id
env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NAME"] = app
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
env_dict["YNH_APP_OLD_DOMAIN"] = old_domain env_dict["YNH_APP_OLD_DOMAIN"] = old_domain
env_dict["YNH_APP_OLD_PATH"] = old_path env_dict["YNH_APP_OLD_PATH"] = old_path
@ -439,7 +452,7 @@ def app_change_url(operation_logger, app, domain, path):
hook_callback('post_app_change_url', args=args_list, env=env_dict) hook_callback('post_app_change_url', args=args_list, env=env_dict)
def app_upgrade(app=[], url=None, file=None): def app_upgrade(app=[], url=None, file=None, force=False):
""" """
Upgrade app Upgrade app
@ -449,8 +462,10 @@ def app_upgrade(app=[], url=None, file=None):
url -- Git url to fetch for upgrade url -- Git url to fetch for upgrade
""" """
from packaging import version
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
from yunohost.permission import permission_sync_to_user from yunohost.permission import permission_sync_to_user
from yunohost.regenconf import manually_modified_files
apps = app apps = app
# If no app is specified, upgrade all apps # If no app is specified, upgrade all apps
@ -488,12 +503,41 @@ def app_upgrade(app=[], url=None, file=None):
elif app_dict["upgradable"] == "url_required": elif app_dict["upgradable"] == "url_required":
logger.warning(m18n.n('custom_app_url_required', app=app_instance_name)) logger.warning(m18n.n('custom_app_url_required', app=app_instance_name))
continue continue
elif app_dict["upgradable"] == "yes": elif app_dict["upgradable"] == "yes" or force:
manifest, extracted_app_folder = _fetch_app_from_git(app_instance_name) manifest, extracted_app_folder = _fetch_app_from_git(app_instance_name)
else: else:
logger.success(m18n.n('app_already_up_to_date', app=app_instance_name)) logger.success(m18n.n('app_already_up_to_date', app=app_instance_name))
continue continue
# Manage upgrade type and avoid any upgrade if there is nothing to do
upgrade_type = "UNKNOWN"
# Get current_version and new version
app_new_version = version.parse(manifest.get("version", "?"))
app_current_version = version.parse(app_dict.get("version", "?"))
if "~ynh" in str(app_current_version) and "~ynh" in str(app_new_version):
if app_current_version >= app_new_version and not force:
# In case of upgrade from file or custom repository
# No new version available
logger.success(m18n.n('app_already_up_to_date', app=app_instance_name))
# Save update time
now = int(time.time())
app_setting(app_instance_name, 'update_time', now)
app_setting(app_instance_name, 'current_revision', manifest.get('remote', {}).get('revision', "?"))
continue
elif app_current_version > app_new_version:
upgrade_type = "DOWNGRADE_FORCED"
elif app_current_version == app_new_version:
upgrade_type = "UPGRADE_FORCED"
else:
app_current_version_upstream, app_current_version_pkg = str(app_current_version).split("~ynh")
app_new_version_upstream, app_new_version_pkg = str(app_new_version).split("~ynh")
if app_current_version_upstream == app_new_version_upstream:
upgrade_type = "UPGRADE_PACKAGE"
elif app_current_version_pkg == app_new_version_pkg:
upgrade_type = "UPGRADE_APP"
else:
upgrade_type = "UPGRADE_FULL"
# Check requirements # Check requirements
_check_manifest_requirements(manifest, app_instance_name=app_instance_name) _check_manifest_requirements(manifest, app_instance_name=app_instance_name)
_assert_system_is_sane_for_app(manifest, "pre") _assert_system_is_sane_for_app(manifest, "pre")
@ -512,12 +556,18 @@ def app_upgrade(app=[], url=None, file=None):
env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_ID"] = app_id
env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
env_dict["YNH_APP_UPGRADE_TYPE"] = upgrade_type
env_dict["YNH_APP_MANIFEST_VERSION"] = str(app_new_version)
env_dict["YNH_APP_CURRENT_VERSION"] = str(app_current_version)
# We'll check that the app didn't brutally edit some system configuration
manually_modified_files_before_install = manually_modified_files()
# Attempt to patch legacy helpers ... # Attempt to patch legacy helpers ...
_patch_legacy_helpers(extracted_app_folder) _patch_legacy_helpers(extracted_app_folder)
# Apply dirty patch to make php5 apps compatible with php7 # Apply dirty patch to make php5 apps compatible with php7
_patch_php5(extracted_app_folder) _patch_legacy_php_versions(extracted_app_folder)
# Start register change on system # Start register change on system
related_to = [('app', app_instance_name)] related_to = [('app', app_instance_name)]
@ -563,6 +613,12 @@ def app_upgrade(app=[], url=None, file=None):
logger.error(m18n.n("app_upgrade_failed", app=app_instance_name, error=str(e))) logger.error(m18n.n("app_upgrade_failed", app=app_instance_name, error=str(e)))
failure_message_with_debug_instructions = operation_logger.error(str(e)) failure_message_with_debug_instructions = operation_logger.error(str(e))
# We'll check that the app didn't brutally edit some system configuration
manually_modified_files_after_install = manually_modified_files()
manually_modified_files_by_app = set(manually_modified_files_after_install) - set(manually_modified_files_before_install)
if manually_modified_files_by_app:
logger.error("Packagers /!\\ This app manually modified some system configuration files! This should not happen! If you need to do so, you should implement a proper conf_regen hook. Those configuration were affected:\n - " + '\n -'.join(manually_modified_files_by_app))
# If upgrade failed or broke the system, # If upgrade failed or broke the system,
# raise an error and interrupt all other pending upgrades # raise an error and interrupt all other pending upgrades
if upgrade_failed or broke_the_system: if upgrade_failed or broke_the_system:
@ -625,7 +681,8 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback from yunohost.hook import hook_add, hook_remove, hook_exec, hook_callback
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user, user_permission_update from yunohost.permission import user_permission_list, permission_create, permission_url, permission_delete, permission_sync_to_user
from yunohost.regenconf import manually_modified_files
# Fetch or extract sources # Fetch or extract sources
if not os.path.exists(INSTALL_TMP): if not os.path.exists(INSTALL_TMP):
@ -725,17 +782,21 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
_patch_legacy_helpers(extracted_app_folder) _patch_legacy_helpers(extracted_app_folder)
# Apply dirty patch to make php5 apps compatible with php7 # Apply dirty patch to make php5 apps compatible with php7
_patch_php5(extracted_app_folder) _patch_legacy_php_versions(extracted_app_folder)
# Prepare env. var. to pass to script # Prepare env. var. to pass to script
env_dict = _make_environment_dict(args_odict) env_dict = _make_environment_dict(args_odict)
env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_ID"] = app_id
env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) env_dict["YNH_APP_INSTANCE_NUMBER"] = str(instance_number)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
# Start register change on system # Start register change on system
operation_logger.extra.update({'env': env_dict}) operation_logger.extra.update({'env': env_dict})
# We'll check that the app didn't brutally edit some system configuration
manually_modified_files_before_install = manually_modified_files()
# Tell the operation_logger to redact all password-type args # Tell the operation_logger to redact all password-type args
# Also redact the % escaped version of the password that might appear in # Also redact the % escaped version of the password that might appear in
# the 'args' section of metadata (relevant for password with non-alphanumeric char) # the 'args' section of metadata (relevant for password with non-alphanumeric char)
@ -820,6 +881,12 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
logger.error(m18n.n("app_install_failed", app=app_id, error=str(e))) logger.error(m18n.n("app_install_failed", app=app_id, error=str(e)))
failure_message_with_debug_instructions = operation_logger.error(str(e)) failure_message_with_debug_instructions = operation_logger.error(str(e))
# We'll check that the app didn't brutally edit some system configuration
manually_modified_files_after_install = manually_modified_files()
manually_modified_files_by_app = set(manually_modified_files_after_install) - set(manually_modified_files_before_install)
if manually_modified_files_by_app:
logger.error("Packagers /!\\ This app manually modified some system configuration files! This should not happen! If you need to do so, you should implement a proper conf_regen hook. Those configuration were affected:\n - " + '\n -'.join(manually_modified_files_by_app))
# If the install failed or broke the system, we remove it # If the install failed or broke the system, we remove it
if install_failed or broke_the_system: if install_failed or broke_the_system:
@ -834,6 +901,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
env_dict_remove["YNH_APP_ID"] = app_id env_dict_remove["YNH_APP_ID"] = app_id
env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict_remove["YNH_APP_INSTANCE_NAME"] = app_instance_name
env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number) env_dict_remove["YNH_APP_INSTANCE_NUMBER"] = str(instance_number)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
# Execute remove script # Execute remove script
operation_logger_remove = OperationLogger('remove_on_failed_install', operation_logger_remove = OperationLogger('remove_on_failed_install',
@ -847,6 +915,7 @@ def app_install(operation_logger, app, label=None, args=None, no_remove_on_failu
os.path.join(extracted_app_folder, 'scripts/remove'), os.path.join(extracted_app_folder, 'scripts/remove'),
args=[app_instance_name], env=env_dict_remove args=[app_instance_name], env=env_dict_remove
)[0] )[0]
# Here again, calling hook_exec could fail miserably, or get # Here again, calling hook_exec could fail miserably, or get
# manually interrupted (by mistake or because script was stuck) # manually interrupted (by mistake or because script was stuck)
# In that case we still want to proceed with the rest of the # In that case we still want to proceed with the rest of the
@ -1014,7 +1083,7 @@ def app_remove(operation_logger, app):
# Apply dirty patch to make php5 apps compatible with php7 (e.g. the remove # Apply dirty patch to make php5 apps compatible with php7 (e.g. the remove
# script might date back from jessie install) # script might date back from jessie install)
_patch_php5(app_setting_path) _patch_legacy_php_versions(app_setting_path)
manifest = _get_manifest_of_app(app_setting_path) manifest = _get_manifest_of_app(app_setting_path)
@ -1029,6 +1098,7 @@ def app_remove(operation_logger, app):
env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_ID"] = app_id
env_dict["YNH_APP_INSTANCE_NAME"] = app env_dict["YNH_APP_INSTANCE_NAME"] = app
env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb) env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)
env_dict["YNH_APP_MANIFEST_VERSION"] = manifest.get("version", "?")
operation_logger.extra.update({'env': env_dict}) operation_logger.extra.update({'env': env_dict})
operation_logger.flush() operation_logger.flush()
@ -1340,7 +1410,7 @@ def app_ssowatconf():
url = _sanitized_absolute_url(perm_info["url"]) url = _sanitized_absolute_url(perm_info["url"])
perm_info["url"] = url perm_info["url"] = url
if "visitors" in perm_info["allowed"]: if "visitors" in perm_info["allowed"]:
if url not in unprotected_urls: if url not in unprotected_urls and url not in skipped_urls:
unprotected_urls.append(url) unprotected_urls.append(url)
# Legacy stuff : we remove now protected-urls that might have been declared as unprotected earlier... # Legacy stuff : we remove now protected-urls that might have been declared as unprotected earlier...
@ -1867,6 +1937,9 @@ def _get_app_settings(app_id):
with open(os.path.join( with open(os.path.join(
APPS_SETTING_PATH, app_id, 'settings.yml')) as f: APPS_SETTING_PATH, app_id, 'settings.yml')) as f:
settings = yaml.load(f) settings = yaml.load(f)
# If label contains unicode char, this may later trigger issues when building strings...
# FIXME: this should be propagated to read_yaml so that this fix applies everywhere I think...
settings = {k:_encode_string(v) for k,v in settings.items()}
if app_id == settings['id']: if app_id == settings['id']:
return settings return settings
except (IOError, TypeError, KeyError): except (IOError, TypeError, KeyError):
@ -2072,12 +2145,62 @@ def _get_manifest_of_app(path):
manifest["arguments"]["install"] = install_arguments manifest["arguments"]["install"] = install_arguments
return manifest
elif os.path.exists(os.path.join(path, "manifest.json")): elif os.path.exists(os.path.join(path, "manifest.json")):
return read_json(os.path.join(path, "manifest.json")) manifest = read_json(os.path.join(path, "manifest.json"))
else: else:
raise YunohostError("There doesn't seem to be any manifest file in %s ... It looks like an app was not correctly installed/removed." % path, raw_msg=True) raise YunohostError("There doesn't seem to be any manifest file in %s ... It looks like an app was not correctly installed/removed." % path, raw_msg=True)
manifest["arguments"] = _set_default_ask_questions(manifest["arguments"])
return manifest
def _set_default_ask_questions(arguments):
# arguments is something like
# { "install": [
# { "name": "domain",
# "type": "domain",
# ....
# },
# { "name": "path",
# "type": "path"
# ...
# },
# ...
# ],
# "upgrade": [ ... ]
# }
# We set a default for any question with these matching (type, name)
# type namei
# N.B. : this is only for install script ... should be reworked for other
# scripts if we supports args for other scripts in the future...
questions_with_default = [("domain", "domain"), # i18n: app_manifest_install_ask_domain
("path", "path"), # i18n: app_manifest_install_ask_path
("password", "password"), # i18n: app_manifest_install_ask_password
("user", "admin"), # i18n: app_manifest_install_ask_admin
("boolean", "is_public")] # i18n: app_manifest_install_ask_is_public
for script_name, arg_list in arguments.items():
# We only support questions for install so far, and for other
if script_name != "install":
continue
for arg in arg_list:
# Do not override 'ask' field if provided by app ?... Or shall we ?
# if "ask" in arg:
# continue
# If this arg corresponds to a question with default ask message...
if any((arg.get("type"), arg["name"]) == question for question in questions_with_default):
# The key is for example "app_manifest_install_ask_domain"
key = "app_manifest_%s_ask_%s" % (script_name, arg["name"])
arg["ask"] = m18n.n(key)
return arguments
def _get_git_last_commit_hash(repository, reference='HEAD'): def _get_git_last_commit_hash(repository, reference='HEAD'):
""" """
@ -2765,12 +2888,6 @@ def _read_apps_catalog_list():
Read the json corresponding to the list of apps catalogs Read the json corresponding to the list of apps catalogs
""" """
# Legacy code - can be removed after moving to buster (if the migration got merged before buster)
if os.path.exists('/etc/yunohost/appslists.json'):
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("futureproof_apps_catalog_system")
migration.run()
try: try:
list_ = read_yaml(APPS_CATALOG_CONF) list_ = read_yaml(APPS_CATALOG_CONF)
# Support the case where file exists but is empty # Support the case where file exists but is empty
@ -2927,8 +3044,8 @@ def _assert_system_is_sane_for_app(manifest, when):
# Some apps use php-fpm or php5-fpm which is now php7.0-fpm # Some apps use php-fpm or php5-fpm which is now php7.0-fpm
def replace_alias(service): def replace_alias(service):
if service in ["php-fpm", "php5-fpm"]: if service in ["php-fpm", "php5-fpm", "php7.0-fpm"]:
return "php7.0-fpm" return "php7.3-fpm"
else: else:
return service return service
services = [replace_alias(s) for s in services] services = [replace_alias(s) for s in services]
@ -2936,7 +3053,7 @@ def _assert_system_is_sane_for_app(manifest, when):
# We only check those, mostly to ignore "custom" services # We only check those, mostly to ignore "custom" services
# (added by apps) and because those are the most popular # (added by apps) and because those are the most popular
# services # services
service_filter = ["nginx", "php7.0-fpm", "mysql", "postfix"] service_filter = ["nginx", "php7.3-fpm", "mysql", "postfix"]
services = [str(s) for s in services if s in service_filter] services = [str(s) for s in services if s in service_filter]
if "nginx" not in services: if "nginx" not in services:
@ -2961,11 +3078,24 @@ def _assert_system_is_sane_for_app(manifest, when):
raise YunohostError("this_action_broke_dpkg") raise YunohostError("this_action_broke_dpkg")
def _patch_php5(app_folder): LEGACY_PHP_VERSION_REPLACEMENTS = [
("/etc/php5", "/etc/php/7.3"),
("/etc/php/7.0", "/etc/php/7.3"),
("/var/run/php5-fpm", "/var/run/php/php7.3-fpm"),
("/var/run/php/php7.0-fpm", "/var/run/php/php7.3-fpm"),
("php5", "php7.3"),
("php7.0", "php7.3"),
('phpversion="${phpversion:-7.0}"', 'phpversion="${phpversion:-7.3}"'), # Many helpers like the composer ones use 7.0 by default ...
('"$phpversion" == "7.0"', '$(bc <<< "$phpversion >= 7.3") -eq 1') # patch ynh_install_php to refuse installing/removing php <= 7.3
]
def _patch_legacy_php_versions(app_folder):
files_to_patch = [] files_to_patch = []
files_to_patch.extend(glob.glob("%s/conf/*" % app_folder)) files_to_patch.extend(glob.glob("%s/conf/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder)) files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/*/*" % app_folder))
files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder)) files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder))
files_to_patch.append("%s/manifest.json" % app_folder) files_to_patch.append("%s/manifest.json" % app_folder)
files_to_patch.append("%s/manifest.toml" % app_folder) files_to_patch.append("%s/manifest.toml" % app_folder)
@ -2976,12 +3106,32 @@ def _patch_php5(app_folder):
if not os.path.isfile(filename): if not os.path.isfile(filename):
continue continue
c = "sed -i -e 's@/etc/php5@/etc/php/7.0@g' " \ c = "sed -i " \
"-e 's@/var/run/php5-fpm@/var/run/php/php7.0-fpm@g' " \ + "".join("-e 's@{pattern}@{replace}@g' ".format(pattern=p, replace=r) for p, r in LEGACY_PHP_VERSION_REPLACEMENTS) \
"-e 's@php5@php7.0@g' " \ + "%s" % filename
"%s" % filename
os.system(c) os.system(c)
def _patch_legacy_php_versions_in_settings(app_folder):
settings = read_yaml(os.path.join(app_folder, 'settings.yml'))
if settings.get("fpm_config_dir") == "/etc/php/7.0/fpm":
settings["fpm_config_dir"] = "/etc/php/7.3/fpm"
if settings.get("fpm_service") == "php7.0-fpm":
settings["fpm_service"] = "php7.3-fpm"
if settings.get("phpversion") == "7.0":
settings["phpversion"] = "7.3"
# We delete these checksums otherwise the file will appear as manually modified
list_to_remove = ["checksum__etc_php_7.0_fpm_pool",
"checksum__etc_nginx_conf.d"]
settings = {k: v for k, v in settings.items()
if not any(k.startswith(to_remove) for to_remove in list_to_remove)}
write_to_yaml(app_folder + '/settings.yml', settings)
def _patch_legacy_helpers(app_folder): def _patch_legacy_helpers(app_folder):
files_to_patch = [] files_to_patch = []

View file

@ -43,7 +43,13 @@ from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml from moulinette.utils.filesystem import read_file, mkdir, write_to_yaml, read_yaml
from yunohost.app import ( from yunohost.app import (
app_info, _is_installed, _parse_app_instance_name, _patch_php5, dump_app_log_extract_for_debugging, _patch_legacy_helpers app_info, _is_installed,
_parse_app_instance_name,
dump_app_log_extract_for_debugging,
_patch_legacy_helpers,
_patch_legacy_php_versions,
_patch_legacy_php_versions_in_settings,
LEGACY_PHP_VERSION_REPLACEMENTS
) )
from yunohost.hook import ( from yunohost.hook import (
hook_list, hook_info, hook_callback, hook_exec, CUSTOM_HOOK_FOLDER hook_list, hook_info, hook_callback, hook_exec, CUSTOM_HOOK_FOLDER
@ -53,6 +59,7 @@ from yunohost.regenconf import regen_conf
from yunohost.log import OperationLogger from yunohost.log import OperationLogger
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError
from yunohost.utils.packages import ynh_packages_version from yunohost.utils.packages import ynh_packages_version
from yunohost.settings import settings_get
BACKUP_PATH = '/home/yunohost.backup' BACKUP_PATH = '/home/yunohost.backup'
ARCHIVES_PATH = '%s/archives' % BACKUP_PATH ARCHIVES_PATH = '%s/archives' % BACKUP_PATH
@ -219,8 +226,8 @@ class BackupManager():
backup_manager = BackupManager(name="mybackup", description="bkp things") backup_manager = BackupManager(name="mybackup", description="bkp things")
# Add backup method to apply # Add backup method to apply
backup_manager.add(BackupMethod.create('copy', backup_manager, '/mnt/local_fs')) backup_manager.add('copy', output_directory='/mnt/local_fs')
backup_manager.add(BackupMethod.create('tar', backup_manager, '/mnt/remote_fs')) backup_manager.add('tar', output_directory='/mnt/remote_fs')
# Define targets to be backuped # Define targets to be backuped
backup_manager.set_system_targets(["data"]) backup_manager.set_system_targets(["data"])
@ -233,7 +240,7 @@ class BackupManager():
backup_manager.backup() backup_manager.backup()
""" """
def __init__(self, name=None, description='', work_dir=None): def __init__(self, name=None, description='', methods=[], work_dir=None):
""" """
BackupManager constructor BackupManager constructor
@ -251,7 +258,6 @@ class BackupManager():
self.created_at = int(time.time()) self.created_at = int(time.time())
self.apps_return = {} self.apps_return = {}
self.system_return = {} self.system_return = {}
self.methods = []
self.paths_to_backup = [] self.paths_to_backup = []
self.size_details = { self.size_details = {
'system': {}, 'system': {},
@ -270,6 +276,9 @@ class BackupManager():
self.work_dir = os.path.join(BACKUP_PATH, 'tmp', name) self.work_dir = os.path.join(BACKUP_PATH, 'tmp', name)
self._init_work_dir() self._init_work_dir()
# Initialize backup methods
self.methods = [BackupMethod.create(method, self, repo=work_dir) for method in methods]
# #
# Misc helpers # # Misc helpers #
# #
@ -309,17 +318,6 @@ class BackupManager():
"""Initialize preparation directory """Initialize preparation directory
Ensure the working directory exists and is empty Ensure the working directory exists and is empty
exception:
backup_output_directory_not_empty -- (YunohostError) Raised if the
directory was given by the user and isn't empty
(TODO) backup_cant_clean_tmp_working_directory -- (YunohostError)
Raised if the working directory isn't empty, is temporary and can't
be automaticcaly cleaned
(TODO) backup_cant_create_working_directory -- (YunohostError) Raised
if iyunohost can't create the working directory
""" """
# FIXME replace isdir by exists ? manage better the case where the path # FIXME replace isdir by exists ? manage better the case where the path
@ -503,10 +501,6 @@ class BackupManager():
files to backup files to backup
hooks/ -- restore scripts associated to system backup scripts are hooks/ -- restore scripts associated to system backup scripts are
copied here copied here
Exceptions:
"backup_nothings_done" -- (YunohostError) This exception is raised if
nothing has been listed.
""" """
self._collect_system_files() self._collect_system_files()
@ -673,10 +667,6 @@ class BackupManager():
Args: Args:
app -- (string) an app instance name (already installed) to backup app -- (string) an app instance name (already installed) to backup
Exceptions:
backup_app_failed -- Raised at the end if the app backup script
execution failed
""" """
from yunohost.permission import user_permission_list from yunohost.permission import user_permission_list
@ -735,18 +725,6 @@ class BackupManager():
# Actual backup archive creation / method management # # Actual backup archive creation / method management #
# #
def add(self, method):
"""
Add a backup method that will be applied after the files collection step
Args:
method -- (BackupMethod) A backup method. Currently, you can use those:
TarBackupMethod
CopyBackupMethod
CustomBackupMethod
"""
self.methods.append(method)
def backup(self): def backup(self):
"""Apply backup methods""" """Apply backup methods"""
@ -809,7 +787,7 @@ class RestoreManager():
""" """
RestoreManager allow to restore a past backup archive RestoreManager allow to restore a past backup archive
Currently it's a tar.gz file, but it could be another kind of archive Currently it's a tar file, but it could be another kind of archive
Public properties: Public properties:
info (getter)i # FIXME info (getter)i # FIXME
@ -835,14 +813,12 @@ class RestoreManager():
return restore_manager.result return restore_manager.result
""" """
def __init__(self, name, repo=None, method='tar'): def __init__(self, name, method='tar'):
""" """
RestoreManager constructor RestoreManager constructor
Args: Args:
name -- (string) Archive name name -- (string) Archive name
repo -- (string|None) Repository where is this archive, it could be a
path (default: /home/yunohost.backup/archives)
method -- (string) Method name to use to mount the archive method -- (string) Method name to use to mount the archive
""" """
# Retrieve and open the archive # Retrieve and open the archive
@ -870,9 +846,6 @@ class RestoreManager():
def _read_info_files(self): def _read_info_files(self):
""" """
Read the info file from inside an archive Read the info file from inside an archive
Exceptions:
backup_archive_cant_retrieve_info_json -- Raised if we can't read the info
""" """
# Retrieve backup info # Retrieve backup info
info_file = os.path.join(self.work_dir, "info.json") info_file = os.path.join(self.work_dir, "info.json")
@ -917,8 +890,6 @@ class RestoreManager():
""" """
from permission import permission_sync_to_user from permission import permission_sync_to_user
successfull_apps = self.targets.list("apps", include=["Success", "Warning"])
permission_sync_to_user() permission_sync_to_user()
if os.path.ismount(self.work_dir): if os.path.ismount(self.work_dir):
@ -1030,10 +1001,6 @@ class RestoreManager():
Use the mount method from the BackupMethod instance and read info about Use the mount method from the BackupMethod instance and read info about
this archive this archive
Exceptions:
restore_removing_tmp_dir_failed -- Raised if it's not possible to remove
the working directory
""" """
self.work_dir = os.path.join(BACKUP_PATH, "tmp", self.name) self.work_dir = os.path.join(BACKUP_PATH, "tmp", self.name)
@ -1107,11 +1074,6 @@ class RestoreManager():
def assert_enough_free_space(self): def assert_enough_free_space(self):
""" """
Check available disk space Check available disk space
Exceptions:
restore_may_be_not_enough_disk_space -- Raised if there isn't enough
space to cover the security margin space
restore_not_enough_disk_space -- Raised if there isn't enough space
""" """
free_space = free_space_in_directory(BACKUP_PATH) free_space = free_space_in_directory(BACKUP_PATH)
@ -1141,7 +1103,7 @@ class RestoreManager():
self._postinstall_if_needed() self._postinstall_if_needed()
# Apply dirty patch to redirect php5 file on php7 # Apply dirty patch to redirect php5 file on php7
self._patch_backup_csv_file() self._patch_legacy_php_versions_in_csv_file()
self._restore_system() self._restore_system()
self._restore_apps() self._restore_apps()
@ -1150,9 +1112,9 @@ class RestoreManager():
finally: finally:
self.clean() self.clean()
def _patch_backup_csv_file(self): def _patch_legacy_php_versions_in_csv_file(self):
""" """
Apply dirty patch to redirect php5 file on php7 Apply dirty patch to redirect php5 and php7.0 files to php7.3
""" """
backup_csv = os.path.join(self.work_dir, 'backup.csv') backup_csv = os.path.join(self.work_dir, 'backup.csv')
@ -1160,32 +1122,27 @@ class RestoreManager():
if not os.path.isfile(backup_csv): if not os.path.isfile(backup_csv):
return return
contains_php5 = False replaced_something = False
with open(backup_csv) as csvfile: with open(backup_csv) as csvfile:
reader = csv.DictReader(csvfile, fieldnames=['source', 'dest']) reader = csv.DictReader(csvfile, fieldnames=['source', 'dest'])
newlines = [] newlines = []
for row in reader: for row in reader:
if 'php5' in row['source']: for pattern, replace in LEGACY_PHP_VERSION_REPLACEMENTS:
contains_php5 = True if pattern in row['source']:
row['source'] = row['source'].replace('/etc/php5', '/etc/php/7.0') \ replaced_something = True
.replace('/var/run/php5-fpm', '/var/run/php/php7.0-fpm') \ row['source'] = row['source'].replace(pattern, replace)
.replace('php5', 'php7')
newlines.append(row) newlines.append(row)
if not contains_php5: if not replaced_something:
return return
try:
with open(backup_csv, 'w') as csvfile: with open(backup_csv, 'w') as csvfile:
writer = csv.DictWriter(csvfile, writer = csv.DictWriter(csvfile,
fieldnames=['source', 'dest'], fieldnames=['source', 'dest'],
quoting=csv.QUOTE_ALL) quoting=csv.QUOTE_ALL)
for row in newlines: for row in newlines:
writer.writerow(row) writer.writerow(row)
except (IOError, OSError, csv.Error) as e:
logger.warning(m18n.n('backup_php5_to_php7_migration_may_fail',
error=str(e)))
def _restore_system(self): def _restore_system(self):
""" Restore user and system parts """ """ Restore user and system parts """
@ -1197,7 +1154,7 @@ class RestoreManager():
return return
from yunohost.user import user_group_list from yunohost.user import user_group_list
from yunohost.permission import permission_create, permission_delete, user_permission_update, user_permission_list, permission_sync_to_user from yunohost.permission import permission_create, permission_delete, user_permission_list, permission_sync_to_user
# Backup old permission for apps # Backup old permission for apps
# We need to do that because in case of an app is installed we can't remove the permission for this app # We need to do that because in case of an app is installed we can't remove the permission for this app
@ -1244,12 +1201,11 @@ class RestoreManager():
# #
# Legacy code # Legacy code
if not "all_users" in user_group_list()["groups"].keys(): if not "all_users" in user_group_list()["groups"].keys():
from yunohost.tools import _get_migration_by_name from yunohost.utils.legacy import SetupGroupPermissions
setup_group_permission = _get_migration_by_name("setup_group_permission")
# Update LDAP schema restart slapd # Update LDAP schema restart slapd
logger.info(m18n.n("migration_0011_update_LDAP_schema")) logger.info(m18n.n("migration_0011_update_LDAP_schema"))
regen_conf(names=['slapd'], force=True) regen_conf(names=['slapd'], force=True)
setup_group_permission.migrate_LDAP_db() SetupGroupPermissions.migrate_LDAP_db()
# Remove all permission for all app which is still in the LDAP # Remove all permission for all app which is still in the LDAP
for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys(): for permission_name in user_permission_list(ignore_system_perms=True)["permissions"].keys():
@ -1293,14 +1249,9 @@ class RestoreManager():
Args: Args:
app_instance_name -- (string) The app name to restore (no app with this app_instance_name -- (string) The app name to restore (no app with this
name should be already install) name should be already install)
Exceptions:
restore_already_installed_app -- Raised if an app with this app instance
name already exists
restore_app_failed -- Raised if the restore bash script failed
""" """
from yunohost.user import user_group_list from yunohost.user import user_group_list
from yunohost.permission import permission_create, permission_delete, user_permission_list, user_permission_update, permission_sync_to_user from yunohost.permission import permission_create, permission_delete, user_permission_list, permission_sync_to_user
def copytree(src, dst, symlinks=False, ignore=None): def copytree(src, dst, symlinks=False, ignore=None):
for item in os.listdir(src): for item in os.listdir(src):
@ -1334,7 +1285,8 @@ class RestoreManager():
_patch_legacy_helpers(app_settings_in_archive) _patch_legacy_helpers(app_settings_in_archive)
# Apply dirty patch to make php5 apps compatible with php7 # Apply dirty patch to make php5 apps compatible with php7
_patch_php5(app_settings_in_archive) _patch_legacy_php_versions(app_settings_in_archive)
_patch_legacy_php_versions_in_settings(app_settings_in_archive)
# Delete _common.sh file in backup # Delete _common.sh file in backup
common_file = os.path.join(app_backup_in_archive, '_common.sh') common_file = os.path.join(app_backup_in_archive, '_common.sh')
@ -1389,9 +1341,8 @@ class RestoreManager():
else: else:
# Otherwise, we need to migrate the legacy permissions of this # Otherwise, we need to migrate the legacy permissions of this
# app (included in its settings.yml) # app (included in its settings.yml)
from yunohost.tools import _get_migration_by_name from yunohost.utils.legacy import SetupGroupPermissions
setup_group_permission = _get_migration_by_name("setup_group_permission") SetupGroupPermissions.migrate_app_permission(app=app_instance_name)
setup_group_permission.migrate_app_permission(app=app_instance_name)
# Prepare env. var. to pass to script # Prepare env. var. to pass to script
env_dict = self._get_env_var(app_instance_name) env_dict = self._get_env_var(app_instance_name)
@ -1500,7 +1451,7 @@ class BackupMethod(object):
TarBackupMethod TarBackupMethod
--------------- ---------------
This method compresses all files to backup in a .tar.gz archive. When This method compresses all files to backup in a .tar archive. When
restoring, it untars the required parts. restoring, it untars the required parts.
CustomBackupMethod CustomBackupMethod
@ -1525,7 +1476,24 @@ class BackupMethod(object):
method.mount() method.mount()
""" """
def __init__(self, manager, repo=None): @classmethod
def create(cls, method, manager, **kwargs):
"""
Factory method to create instance of BackupMethod
Args:
method -- (string) The method name of an existing BackupMethod. If the
name is unknown the CustomBackupMethod will be tried
*args -- Specific args for the method, could be the repo target by the
method
Return a BackupMethod instance
"""
known_methods = {c.method_name:c for c in BackupMethod.__subclasses__()}
backup_method = known_methods.get(method, CustomBackupMethod)
return backup_method(manager, method=method, **kwargs)
def __init__(self, manager, repo=None, **kwargs):
""" """
BackupMethod constructors BackupMethod constructors
@ -1610,10 +1578,6 @@ class BackupMethod(object):
def clean(self): def clean(self):
""" """
Umount sub directories of working dirextories and delete it if temporary Umount sub directories of working dirextories and delete it if temporary
Exceptions:
backup_cleaning_failed -- Raise if we were not able to unmount sub
directories of the working directories
""" """
if self.need_mount(): if self.need_mount():
if not _recursive_umount(self.work_dir): if not _recursive_umount(self.work_dir):
@ -1625,9 +1589,6 @@ class BackupMethod(object):
def _check_is_enough_free_space(self): def _check_is_enough_free_space(self):
""" """
Check free space in repository or output directory before to backup Check free space in repository or output directory before to backup
Exceptions:
not_enough_disk_space -- Raise if there isn't enough space.
""" """
# TODO How to do with distant repo or with deduplicated backup ? # TODO How to do with distant repo or with deduplicated backup ?
backup_size = self.manager.size backup_size = self.manager.size
@ -1649,9 +1610,6 @@ class BackupMethod(object):
The usage of binding could be strange for a user because the du -sb The usage of binding could be strange for a user because the du -sb
command will return that the working directory is big. command will return that the working directory is big.
Exceptions:
backup_unable_to_organize_files
""" """
paths_needed_to_be_copied = [] paths_needed_to_be_copied = []
for path in self.manager.paths_to_backup: for path in self.manager.paths_to_backup:
@ -1749,36 +1707,6 @@ class BackupMethod(object):
else: else:
shutil.copy(path['source'], dest) shutil.copy(path['source'], dest)
@classmethod
def create(cls, method, manager, *args):
"""
Factory method to create instance of BackupMethod
Args:
method -- (string) The method name of an existing BackupMethod. If the
name is unknown the CustomBackupMethod will be tried
... -- Specific args for the method, could be the repo target by the
method
Return a BackupMethod instance
"""
if not isinstance(method, basestring):
methods = []
for m in method:
methods.append(BackupMethod.create(m, manager, *args))
return methods
bm_class = {
'copy': CopyBackupMethod,
'tar': TarBackupMethod,
'borg': BorgBackupMethod
}
if method in ["copy", "tar", "borg"]:
return bm_class[method](manager, *args)
else:
return CustomBackupMethod(manager, method=method, *args)
class CopyBackupMethod(BackupMethod): class CopyBackupMethod(BackupMethod):
@ -1787,12 +1715,7 @@ class CopyBackupMethod(BackupMethod):
could be the inverse for restoring could be the inverse for restoring
""" """
def __init__(self, manager, repo=None): method_name = "copy"
super(CopyBackupMethod, self).__init__(manager, repo)
@property
def method_name(self):
return 'copy'
def backup(self): def backup(self):
""" Copy prepared files into a the repo """ """ Copy prepared files into a the repo """
@ -1818,10 +1741,6 @@ class CopyBackupMethod(BackupMethod):
def mount(self): def mount(self):
""" """
Mount the uncompress backup in readonly mode to the working directory Mount the uncompress backup in readonly mode to the working directory
Exceptions:
backup_no_uncompress_archive_dir -- Raised if the repo doesn't exists
backup_cant_mount_uncompress_archive -- Raised if the binding failed
""" """
# FIXME: This code is untested because there is no way to run it from # FIXME: This code is untested because there is no way to run it from
# the ynh cli # the ynh cli
@ -1848,33 +1767,25 @@ class CopyBackupMethod(BackupMethod):
class TarBackupMethod(BackupMethod): class TarBackupMethod(BackupMethod):
""" method_name = "tar"
This class compress all files to backup in archive.
"""
def __init__(self, manager, repo=None):
super(TarBackupMethod, self).__init__(manager, repo)
@property
def method_name(self):
return 'tar'
@property @property
def _archive_file(self): def _archive_file(self):
"""Return the compress archive path"""
if isinstance(self.manager, BackupManager) and settings_get("backup.compress_tar_archives"):
return os.path.join(self.repo, self.name + '.tar.gz') return os.path.join(self.repo, self.name + '.tar.gz')
f = os.path.join(self.repo, self.name + '.tar')
if os.path.exists(f + ".gz"):
f += ".gz"
return f
def backup(self): def backup(self):
""" """
Compress prepared files Compress prepared files
It adds the info.json in /home/yunohost.backup/archives and if the It adds the info.json in /home/yunohost.backup/archives and if the
compress archive isn't located here, add a symlink to the archive to. compress archive isn't located here, add a symlink to the archive to.
Exceptions:
backup_archive_open_failed -- Raised if we can't open the archive
backup_creation_failed -- Raised if we can't write in the
compress archive
""" """
if not os.path.exists(self.repo): if not os.path.exists(self.repo):
@ -1885,7 +1796,7 @@ class TarBackupMethod(BackupMethod):
# Open archive file for writing # Open archive file for writing
try: try:
tar = tarfile.open(self._archive_file, "w:gz") tar = tarfile.open(self._archive_file, "w:gz" if self._archive_file.endswith(".gz") else "w")
except: except:
logger.debug("unable to open '%s' for writing", logger.debug("unable to open '%s' for writing",
self._archive_file, exc_info=1) self._archive_file, exc_info=1)
@ -1909,26 +1820,20 @@ class TarBackupMethod(BackupMethod):
# If backuped to a non-default location, keep a symlink of the archive # If backuped to a non-default location, keep a symlink of the archive
# to that location # to that location
link = os.path.join(ARCHIVES_PATH, self.name + '.tar.gz') link = os.path.join(ARCHIVES_PATH, self.name + '.tar')
if not os.path.isfile(link): if not os.path.isfile(link):
os.symlink(self._archive_file, link) os.symlink(self._archive_file, link)
def mount(self): def mount(self):
""" """
Mount the archive. We avoid copy to be able to restore on system without Mount the archive. We avoid intermediate copies to be able to restore on system with low free space.
too many space.
Exceptions:
backup_archive_open_failed -- Raised if the archive can't be open
backup_archive_corrupted -- Raised if the archive appears corrupted
backup_archive_cant_retrieve_info_json -- If the info.json file can't be retrieved
""" """
super(TarBackupMethod, self).mount() super(TarBackupMethod, self).mount()
# Mount the tarball # Mount the tarball
logger.debug(m18n.n("restore_extracting")) logger.debug(m18n.n("restore_extracting"))
try: try:
tar = tarfile.open(self._archive_file, "r:gz") tar = tarfile.open(self._archive_file, "r:gz" if self._archive_file.endswith(".gz") else "r")
except: except:
logger.debug("cannot open backup archive '%s'", logger.debug("cannot open backup archive '%s'",
self._archive_file, exc_info=1) self._archive_file, exc_info=1)
@ -1997,7 +1902,7 @@ class TarBackupMethod(BackupMethod):
tar.close() tar.close()
def copy(self, file, target): def copy(self, file, target):
tar = tarfile.open(self._archive_file, "r:gz") tar = tarfile.open(self._archive_file, "r:gz" if self._archive_file.endswith(".gz") else "r")
file_to_extract = tar.getmember(file) file_to_extract = tar.getmember(file)
# Remove the path # Remove the path
file_to_extract.name = os.path.basename(file_to_extract.name) file_to_extract.name = os.path.basename(file_to_extract.name)
@ -2005,26 +1910,6 @@ class TarBackupMethod(BackupMethod):
tar.close() tar.close()
class BorgBackupMethod(BackupMethod):
@property
def method_name(self):
return 'borg'
def backup(self):
""" Backup prepared files with borg """
super(CopyBackupMethod, self).backup()
# TODO run borg create command
raise YunohostError('backup_borg_not_implemented')
def mount(self, mnt_path):
raise YunohostError('backup_borg_not_implemented')
def copy(self, file, target):
raise YunohostError('backup_borg_not_implemented')
class CustomBackupMethod(BackupMethod): class CustomBackupMethod(BackupMethod):
""" """
@ -2033,21 +1918,16 @@ class CustomBackupMethod(BackupMethod):
/etc/yunohost/hooks.d/backup_method/ /etc/yunohost/hooks.d/backup_method/
""" """
method_name = "custom"
def __init__(self, manager, repo=None, method=None, **kwargs): def __init__(self, manager, repo=None, method=None, **kwargs):
super(CustomBackupMethod, self).__init__(manager, repo) super(CustomBackupMethod, self).__init__(manager, repo)
self.args = kwargs self.args = kwargs
self.method = method self.method = method
self._need_mount = None self._need_mount = None
@property
def method_name(self):
return 'borg'
def need_mount(self): def need_mount(self):
"""Call the backup_method hook to know if we need to organize files """Call the backup_method hook to know if we need to organize files
Exceptions:
backup_custom_need_mount_error -- Raised if the hook failed
""" """
if self._need_mount is not None: if self._need_mount is not None:
return self._need_mount return self._need_mount
@ -2062,9 +1942,6 @@ class CustomBackupMethod(BackupMethod):
def backup(self): def backup(self):
""" """
Launch a custom script to backup Launch a custom script to backup
Exceptions:
backup_custom_backup_error -- Raised if the custom script failed
""" """
ret = hook_callback('backup_method', [self.method], ret = hook_callback('backup_method', [self.method],
@ -2078,9 +1955,6 @@ class CustomBackupMethod(BackupMethod):
def mount(self): def mount(self):
""" """
Launch a custom script to mount the custom archive Launch a custom script to mount the custom archive
Exceptions:
backup_custom_mount_error -- Raised if the custom script failed
""" """
super(CustomBackupMethod, self).mount() super(CustomBackupMethod, self).mount()
ret = hook_callback('backup_method', [self.method], ret = hook_callback('backup_method', [self.method],
@ -2102,7 +1976,7 @@ class CustomBackupMethod(BackupMethod):
# #
def backup_create(name=None, description=None, methods=[], def backup_create(name=None, description=None, methods=[],
output_directory=None, no_compress=False, output_directory=None,
system=[], apps=[]): system=[], apps=[]):
""" """
Create a backup local archive Create a backup local archive
@ -2112,7 +1986,6 @@ def backup_create(name=None, description=None, methods=[],
description -- Short description of the backup description -- Short description of the backup
method -- Method of backup to use method -- Method of backup to use
output_directory -- Output directory for the backup output_directory -- Output directory for the backup
no_compress -- Do not create an archive file
system -- List of system elements to backup system -- List of system elements to backup
apps -- List of application names to backup apps -- List of application names to backup
""" """
@ -2127,6 +2000,10 @@ def backup_create(name=None, description=None, methods=[],
if name and name in backup_list()['archives']: if name and name in backup_list()['archives']:
raise YunohostError('backup_archive_name_exists') raise YunohostError('backup_archive_name_exists')
# By default we backup using the tar method
if not methods:
methods = ['tar']
# Validate output_directory option # Validate output_directory option
if output_directory: if output_directory:
output_directory = os.path.abspath(output_directory) output_directory = os.path.abspath(output_directory)
@ -2137,20 +2014,12 @@ def backup_create(name=None, description=None, methods=[],
output_directory): output_directory):
raise YunohostError('backup_output_directory_forbidden') raise YunohostError('backup_output_directory_forbidden')
# Check that output directory is empty if "copy" in methods:
if os.path.isdir(output_directory) and no_compress and \ if not output_directory:
os.listdir(output_directory):
raise YunohostError('backup_output_directory_not_empty')
elif no_compress:
raise YunohostError('backup_output_directory_required') raise YunohostError('backup_output_directory_required')
# Check that output directory is empty
# Define methods (retro-compat) elif os.path.isdir(output_directory) and os.listdir(output_directory):
if not methods: raise YunohostError('backup_output_directory_not_empty')
if no_compress:
methods = ['copy']
else:
methods = ['tar'] # In future, borg will be the default actions
# If no --system or --apps given, backup everything # If no --system or --apps given, backup everything
if system is None and apps is None: if system is None and apps is None:
@ -2164,23 +2033,12 @@ def backup_create(name=None, description=None, methods=[],
# Create yunohost archives directory if it does not exists # Create yunohost archives directory if it does not exists
_create_archive_dir() _create_archive_dir()
# Prepare files to backup # Initialize backup manager
if no_compress:
backup_manager = BackupManager(name, description,
work_dir=output_directory)
else:
backup_manager = BackupManager(name, description)
# Add backup methods backup_manager = BackupManager(name, description, methods=methods, work_dir=output_directory)
if output_directory:
methods = BackupMethod.create(methods, backup_manager, output_directory)
else:
methods = BackupMethod.create(methods, backup_manager)
for method in methods:
backup_manager.add(method)
# Add backup targets (system and apps) # Add backup targets (system and apps)
backup_manager.set_system_targets(system) backup_manager.set_system_targets(system)
backup_manager.set_apps_targets(apps) backup_manager.set_apps_targets(apps)
@ -2284,9 +2142,17 @@ def backup_list(with_info=False, human_readable=False):
""" """
# Get local archives sorted according to last modification time # Get local archives sorted according to last modification time
archives = sorted(glob("%s/*.tar.gz" % ARCHIVES_PATH), key=lambda x: os.path.getctime(x)) # (we do a realpath() to resolve symlinks)
archives = glob("%s/*.tar.gz" % ARCHIVES_PATH) + glob("%s/*.tar" % ARCHIVES_PATH)
archives = set([os.path.realpath(archive) for archive in archives])
archives = sorted(archives, key=lambda x: os.path.getctime(x))
# Extract only filename without the extension # Extract only filename without the extension
archives = [os.path.basename(f)[:-len(".tar.gz")] for f in archives] def remove_extension(f):
if f.endswith(".tar.gz"):
return os.path.basename(f)[:-len(".tar.gz")]
else:
return os.path.basename(f)[:-len(".tar")]
archives = [remove_extension(f) for f in archives]
if with_info: if with_info:
d = OrderedDict() d = OrderedDict()
@ -2314,9 +2180,11 @@ def backup_info(name, with_details=False, human_readable=False):
human_readable -- Print sizes in human readable format human_readable -- Print sizes in human readable format
""" """
archive_file = '%s/%s.tar.gz' % (ARCHIVES_PATH, name) archive_file = '%s/%s.tar' % (ARCHIVES_PATH, name)
# Check file exist (even if it's a broken symlink) # Check file exist (even if it's a broken symlink)
if not os.path.lexists(archive_file):
archive_file += ".gz"
if not os.path.lexists(archive_file): if not os.path.lexists(archive_file):
raise YunohostError('backup_archive_name_unknown', name=name) raise YunohostError('backup_archive_name_unknown', name=name)
@ -2332,7 +2200,7 @@ def backup_info(name, with_details=False, human_readable=False):
info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name)
if not os.path.exists(info_file): if not os.path.exists(info_file):
tar = tarfile.open(archive_file, "r:gz") tar = tarfile.open(archive_file, "r:gz" if archive_file.endswith(".gz") else "r")
info_dir = info_file + '.d' info_dir = info_file + '.d'
try: try:
@ -2368,7 +2236,7 @@ def backup_info(name, with_details=False, human_readable=False):
# Retrieve backup size # Retrieve backup size
size = info.get('size', 0) size = info.get('size', 0)
if not size: if not size:
tar = tarfile.open(archive_file, "r:gz") tar = tarfile.open(archive_file, "r:gz" if archive_file.endswith(".gz") else "r")
size = reduce(lambda x, y: getattr(x, 'size', x) + getattr(y, 'size', y), size = reduce(lambda x, y: getattr(x, 'size', x) + getattr(y, 'size', y),
tar.getmembers()) tar.getmembers())
tar.close() tar.close()
@ -2428,7 +2296,9 @@ def backup_delete(name):
hook_callback('pre_backup_delete', args=[name]) hook_callback('pre_backup_delete', args=[name])
archive_file = '%s/%s.tar.gz' % (ARCHIVES_PATH, name) archive_file = '%s/%s.tar' % (ARCHIVES_PATH, name)
if os.path.exists(archive_file + ".gz"):
archive_file += '.gz'
info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name) info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name)
files_to_delete = [archive_file, info_file] files_to_delete = [archive_file, info_file]

View file

@ -27,7 +27,6 @@ import sys
import shutil import shutil
import pwd import pwd
import grp import grp
import smtplib
import subprocess import subprocess
import glob import glob
@ -369,12 +368,11 @@ def certificate_renew(domain_list, force=False, no_checks=False, email=False, st
if not no_checks: if not no_checks:
try: try:
_check_domain_is_ready_for_ACME(domain) _check_domain_is_ready_for_ACME(domain)
except: except Exception as e:
msg = "Certificate renewing for %s failed !" % (domain) logger.error(e)
logger.error(msg)
if email: if email:
logger.error("Sending email with details to root ...") logger.error("Sending email with details to root ...")
_email_renewing_failed(domain, msg) _email_renewing_failed(domain, e)
continue continue
logger.info( logger.info(
@ -468,6 +466,7 @@ Subject: %s
%s %s
""" % (from_, to_, subject_, text) """ % (from_, to_, subject_, text)
import smtplib
smtp = smtplib.SMTP("localhost") smtp = smtplib.SMTP("localhost")
smtp.sendmail(from_, [to_], message) smtp.sendmail(from_, [to_], message)
smtp.quit() smtp.quit()

View file

@ -1,15 +0,0 @@
import subprocess
import glob
from yunohost.tools import Migration
from moulinette.utils.filesystem import chown
class MyMigration(Migration):
"Change certificates group permissions from 'metronome' to 'ssl-cert'"
all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem")
def run(self):
for filename in self.all_certificate_files:
chown(filename, uid="root", gid="ssl-cert")

View file

@ -1,86 +0,0 @@
import glob
import os
import requests
import base64
import time
import json
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.dyndns import _guess_current_dyndns_domain
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG"
def run(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None):
if domain is None or private_key_path is None:
try:
(domain, private_key_path) = _guess_current_dyndns_domain(dyn_host)
assert "+157" in private_key_path
except (YunohostError, AssertionError):
logger.info(m18n.n("migrate_tsig_not_needed"))
return
logger.info(m18n.n('migrate_tsig_start', domain=domain))
public_key_path = private_key_path.rsplit(".private", 1)[0] + ".key"
public_key_md5 = open(public_key_path).read().strip().split(' ')[-1]
os.system('cd /etc/yunohost/dyndns && '
'dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER %s' % domain)
os.system('chmod 600 /etc/yunohost/dyndns/*.key /etc/yunohost/dyndns/*.private')
# +165 means that this file store a hmac-sha512 key
new_key_path = glob.glob('/etc/yunohost/dyndns/*+165*.key')[0]
public_key_sha512 = open(new_key_path).read().strip().split(' ', 6)[-1]
try:
r = requests.put('https://%s/migrate_key_to_sha512/' % (dyn_host),
data={
'public_key_md5': base64.b64encode(public_key_md5),
'public_key_sha512': base64.b64encode(public_key_sha512),
}, timeout=30)
except requests.ConnectionError:
raise YunohostError('no_internet_connection')
if r.status_code != 201:
try:
error = json.loads(r.text)['error']
except Exception:
# failed to decode json
error = r.text
import traceback
from StringIO import StringIO
stack = StringIO()
traceback.print_stack(file=stack)
logger.error(stack.getvalue())
# Migration didn't succeed, so we rollback and raise an exception
os.system("mv /etc/yunohost/dyndns/*+165* /tmp")
raise YunohostError('migrate_tsig_failed', domain=domain,
error_code=str(r.status_code), error=error)
# remove old certificates
os.system("mv /etc/yunohost/dyndns/*+157* /tmp")
# sleep to wait for dyndns cache invalidation
logger.info(m18n.n('migrate_tsig_wait'))
time.sleep(60)
logger.info(m18n.n('migrate_tsig_wait_2'))
time.sleep(60)
logger.info(m18n.n('migrate_tsig_wait_3'))
time.sleep(30)
logger.info(m18n.n('migrate_tsig_wait_4'))
time.sleep(30)
logger.info(m18n.n('migrate_tsig_end'))
return

View file

@ -1,379 +0,0 @@
import glob
import os
from shutil import copy2
from moulinette import m18n, msettings
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output
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
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_ynh_package_version
from yunohost.utils.network import get_network_interfaces
from yunohost.firewall import firewall_allow, firewall_disallow
logger = getActionLogger('yunohost.migration')
YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"]
class MyMigration(Migration):
"Upgrade the system to Debian Stretch and Yunohost 3.0"
mode = "manual"
def run(self):
self.logfile = "/var/log/yunohost/{}.log".format(self.name)
self.check_assertions()
logger.info(m18n.n("migration_0003_start", logfile=self.logfile))
# Preparing the upgrade
self.restore_original_nginx_conf_if_needed()
logger.info(m18n.n("migration_0003_patching_sources_list"))
self.patch_apt_sources_list()
self.backup_files_to_keep()
self.apt_update()
apps_packages = self.get_apps_equivs_packages()
self.unhold(["metronome"])
self.hold(YUNOHOST_PACKAGES + apps_packages + ["fail2ban"])
# Main dist-upgrade
logger.info(m18n.n("migration_0003_main_upgrade"))
_run_service_command("stop", "mysql")
self.apt_dist_upgrade(conf_flags=["old", "miss", "def"])
_run_service_command("start", "mysql")
if self.debian_major_version() == 8:
raise YunohostError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile)
# Specific upgrade for fail2ban...
logger.info(m18n.n("migration_0003_fail2ban_upgrade"))
self.unhold(["fail2ban"])
# Don't move this if folder already exists. If it does, we probably are
# running this script a 2nd, 3rd, ... time but /etc/fail2ban will
# be re-created only for the first dist-upgrade of fail2ban
if not os.path.exists("/etc/fail2ban.old"):
os.system("mv /etc/fail2ban /etc/fail2ban.old")
self.apt_dist_upgrade(conf_flags=["new", "miss", "def"])
_run_service_command("restart", "fail2ban")
self.disable_predicable_interface_names()
# Clean the mess
os.system("apt autoremove --assume-yes")
os.system("apt clean --assume-yes")
# We moved to port 587 for SMTP
# https://busylog.net/smtp-tls-ssl-25-465-587/
firewall_allow("Both", 587)
firewall_disallow("Both", 465)
# Upgrade yunohost packages
logger.info(m18n.n("migration_0003_yunohost_upgrade"))
self.restore_files_to_keep()
self.unhold(YUNOHOST_PACKAGES + apps_packages)
self.upgrade_yunohost_packages()
def debian_major_version(self):
# The python module "platform" and lsb_release are not reliable because
# on some setup, they still return Release=8 even after upgrading to
# stretch ... (Apparently this is related to OVH overriding some stuff
# with /etc/lsb-release for instance -_-)
# Instead, we rely on /etc/os-release which should be the raw info from
# the distribution...
return int(check_output("grep VERSION_ID /etc/os-release | head -n 1 | tr '\"' ' ' | cut -d ' ' -f2"))
def yunohost_major_version(self):
return int(get_ynh_package_version("yunohost")["version"].split('.')[0])
def check_assertions(self):
# Be on jessie (8.x) and yunohost 2.x
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 9.x but yunohost package
# would still be in 2.x...
if not self.debian_major_version() == 8 \
and not self.yunohost_major_version() == 2:
raise YunohostError("migration_0003_not_jessie")
# Have > 1 Go free space on /var/ ?
if free_space_in_directory("/var/") / (1024**3) < 1.0:
raise YunohostError("There is not enough free space in /var/ to run the migration. You need at least 1GB free space")
# Check system is up to date
# (but we don't if 'stretch' is already in the sources.list ...
# which means maybe a previous upgrade crashed and we're re-running it)
if " stretch " not in read_file("/etc/apt/sources.list"):
self.apt_update()
apt_list_upgradable = check_output("apt list --upgradable -a")
if "upgradable" in apt_list_upgradable:
raise YunohostError("migration_0003_system_not_fully_up_to_date")
@property
def disclaimer(self):
# Avoid having a super long disclaimer + uncessary check if we ain't
# on jessie / yunohost 2.x anymore
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 9.x but yunohost package
# would still be in 2.x...
if not self.debian_major_version() == 8 \
and not self.yunohost_major_version() == 2:
return None
# Get list of problematic apps ? I.e. not official or community+working
problematic_apps = unstable_apps()
problematic_apps = "".join(["\n - " + app for app in problematic_apps])
# Manually modified files ? (c.f. yunohost service regen-conf)
modified_files = manually_modified_files()
# We also have a specific check for nginx.conf which some people
# modified and needs to be upgraded...
if "/etc/nginx/nginx.conf" in manually_modified_files_compared_to_debian_default():
modified_files.append("/etc/nginx/nginx.conf")
modified_files = "".join(["\n - " + f for f in modified_files])
message = m18n.n("migration_0003_general_warning")
if problematic_apps:
message += "\n\n" + m18n.n("migration_0003_problematic_apps_warning", problematic_apps=problematic_apps)
if modified_files:
message += "\n\n" + m18n.n("migration_0003_modified_files", manually_modified_files=modified_files)
return message
def patch_apt_sources_list(self):
sources_list = glob.glob("/etc/apt/sources.list.d/*.list")
sources_list.append("/etc/apt/sources.list")
# This :
# - replace single 'jessie' occurence by 'stretch'
# - comments lines containing "backports"
# - replace 'jessie/updates' by 'strech/updates' (or same with a -)
# - switch yunohost's repo to forge
for f in sources_list:
command = "sed -i -e 's@ jessie @ stretch @g' " \
"-e '/backports/ s@^#*@#@' " \
"-e 's@ jessie/updates @ stretch/updates @g' " \
"-e 's@ jessie-updates @ stretch-updates @g' " \
"-e 's@repo.yunohost@forge.yunohost@g' " \
"{}".format(f)
os.system(command)
def get_apps_equivs_packages(self):
command = "dpkg --get-selections" \
" | grep -v deinstall" \
" | awk '{print $1}'" \
" | { grep 'ynh-deps$' || true; }"
output = check_output(command).strip()
return output.split('\n') if output else []
def hold(self, packages):
for package in packages:
os.system("apt-mark hold {}".format(package))
def unhold(self, packages):
for package in packages:
os.system("apt-mark unhold {}".format(package))
def apt_update(self):
command = "apt-get update"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
os.system(command)
def upgrade_yunohost_packages(self):
#
# Here we use a dirty hack to run a command after the current
# "yunohost tools migrations migrate", because the upgrade of
# yunohost will also trigger another "yunohost tools migrations migrate"
# (also the upgrade of the package, if executed from the webadmin, is
# likely to kill/restart the api which is in turn likely to kill this
# command before it ends...)
#
MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock"
upgrade_command = ""
upgrade_command += " DEBIAN_FRONTEND=noninteractive"
upgrade_command += " APT_LISTCHANGES_FRONTEND=none"
upgrade_command += " apt-get install"
upgrade_command += " --assume-yes "
upgrade_command += " ".join(YUNOHOST_PACKAGES)
# We also install php-zip and php7.0-acpu to fix an issue with
# nextcloud and kanboard that need it when on stretch.
upgrade_command += " php-zip php7.0-apcu"
upgrade_command += " 2>&1 | tee -a {}".format(self.logfile)
wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK)
command = "({} && {}; echo 'Migration complete!') &".format(wait_until_end_of_yunohost_command,
upgrade_command)
logger.debug("Running command :\n{}".format(command))
os.system(command)
def apt_dist_upgrade(self, conf_flags):
# Make apt-get happy
os.system("echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections")
# Don't send an email to root about the postgresql migration. It should be handled automatically after.
os.system("echo 'postgresql-common postgresql-common/obsolete-major seen true' | debconf-set-selections")
command = ""
command += " DEBIAN_FRONTEND=noninteractive"
command += " APT_LISTCHANGES_FRONTEND=none"
command += " apt-get"
command += " --fix-broken --show-upgraded --assume-yes"
for conf_flag in conf_flags:
command += ' -o Dpkg::Options::="--force-conf{}"'.format(conf_flag)
command += " dist-upgrade"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
is_api = msettings.get('interface') == 'api'
if is_api:
callbacks = (
lambda l: logger.info(l.rstrip()),
lambda l: logger.warning(l.rstrip()),
)
call_async_output(command, callbacks, shell=True)
else:
# We do this when running from the cli to have the output of the
# command showing in the terminal, since 'info' channel is only
# enabled if the user explicitly add --verbose ...
os.system(command)
# Those are files that should be kept and restored before the final switch
# to yunohost 3.x... They end up being modified by the various dist-upgrades
# (or need to be taken out momentarily), which then blocks the regen-conf
# as they are flagged as "manually modified"...
files_to_keep = [
"/etc/mysql/my.cnf",
"/etc/nslcd.conf",
"/etc/postfix/master.cf",
"/etc/fail2ban/filter.d/yunohost.conf"
]
def backup_files_to_keep(self):
logger.debug("Backuping specific files to keep ...")
# Create tmp directory if it does not exists
tmp_dir = os.path.join("/tmp/", self.name)
if not os.path.exists(tmp_dir):
os.mkdir(tmp_dir, 0o700)
for f in self.files_to_keep:
dest_file = f.strip('/').replace("/", "_")
# If the file is already there, we might be re-running the migration
# because it previously crashed. Hence we keep the existing file.
if os.path.exists(os.path.join(tmp_dir, dest_file)):
continue
copy2(f, os.path.join(tmp_dir, dest_file))
def restore_files_to_keep(self):
logger.debug("Restoring specific files to keep ...")
tmp_dir = os.path.join("/tmp/", self.name)
for f in self.files_to_keep:
dest_file = f.strip('/').replace("/", "_")
copy2(os.path.join(tmp_dir, dest_file), f)
# On some setups, /etc/nginx/nginx.conf got edited. But this file needs
# to be upgraded because of the way the new module system works for nginx.
# (in particular, having the line that include the modules at the top)
#
# So here, if it got edited, we force the restore of the original conf
# *before* starting the actual upgrade...
#
# An alternative strategy that was attempted was to hold the nginx-common
# package and have a specific upgrade for it like for fail2ban, but that
# leads to apt complaining about not being able to upgrade for shitty
# reasons >.>
def restore_original_nginx_conf_if_needed(self):
if "/etc/nginx/nginx.conf" not in manually_modified_files_compared_to_debian_default():
return
if not os.path.exists("/etc/nginx/nginx.conf"):
return
# If stretch is in the sources.list, we already started migrating on
# stretch so we don't re-do this
if " stretch " in read_file("/etc/apt/sources.list"):
return
backup_dest = "/home/yunohost.conf/backup/nginx.conf.bkp_before_stretch"
logger.warning(m18n.n("migration_0003_restoring_origin_nginx_conf",
backup_dest=backup_dest))
os.system("mv /etc/nginx/nginx.conf %s" % backup_dest)
command = ""
command += " DEBIAN_FRONTEND=noninteractive"
command += " APT_LISTCHANGES_FRONTEND=none"
command += " apt-get"
command += " --fix-broken --show-upgraded --assume-yes"
command += ' -o Dpkg::Options::="--force-confmiss"'
command += " install --reinstall"
command += " nginx-common"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
is_api = msettings.get('interface') == 'api'
if is_api:
callbacks = (
lambda l: logger.info(l.rstrip()),
lambda l: logger.warning(l.rstrip()),
)
call_async_output(command, callbacks, shell=True)
else:
# We do this when running from the cli to have the output of the
# command showing in the terminal, since 'info' channel is only
# enabled if the user explicitly add --verbose ...
os.system(command)
def disable_predicable_interface_names(self):
# Try to see if currently used interface names are predictable ones or not...
# If we ain't using "eth0" or "wlan0", assume we are using predictable interface
# names and therefore they shouldnt be disabled
network_interfaces = get_network_interfaces().keys()
if "eth0" not in network_interfaces and "wlan0" not in network_interfaces:
return
interfaces_config = read_file("/etc/network/interfaces")
if "eth0" not in interfaces_config and "wlan0" not in interfaces_config:
return
# Disable predictive interface names
# c.f. https://unix.stackexchange.com/a/338730
os.system("ln -s /dev/null /etc/systemd/network/99-default.link")

View file

@ -1,99 +0,0 @@
import os
import glob
from shutil import copy2
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.service import _run_service_command
logger = getActionLogger('yunohost.migration')
PHP5_POOLS = "/etc/php5/fpm/pool.d"
PHP7_POOLS = "/etc/php/7.0/fpm/pool.d"
PHP5_SOCKETS_PREFIX = "/var/run/php5-fpm"
PHP7_SOCKETS_PREFIX = "/run/php/php7.0-fpm"
MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}".format(PHP5_POOLS)
class MyMigration(Migration):
"Migrate php5-fpm 'pool' conf files to php7 stuff"
dependencies = ["migrate_to_stretch"]
def run(self):
# Get list of php5 pool files
php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS))
# Keep only basenames
php5_pool_files = [os.path.basename(f) for f in php5_pool_files]
# Ignore the "www.conf" (default stuff, probably don't want to touch it ?)
php5_pool_files = [f for f in php5_pool_files if f != "www.conf"]
for f in php5_pool_files:
# Copy the files to the php7 pool
src = "{}/{}".format(PHP5_POOLS, f)
dest = "{}/{}".format(PHP7_POOLS, f)
copy2(src, dest)
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest)
os.system(c)
# Also add a comment that it was automatically moved from php5
# (for human traceability and backward migration)
c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest)
os.system(c)
# Some old comments starting with '#' instead of ';' are not
# compatible in php7
c = "sed -i 's/^#/;#/g' {}".format(dest)
os.system(c)
# Reload/restart the php pools
_run_service_command("restart", "php7.0-fpm")
_run_service_command("enable", "php7.0-fpm")
os.system("systemctl stop php5-fpm")
os.system("systemctl disable php5-fpm")
os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy
# Get list of nginx conf file
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf")
for f in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f)
os.system(c)
# Reload nginx
_run_service_command("reload", "nginx")
def backward(self):
# Get list of php7 pool files
php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS))
# Keep only files which have the migration comment
php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT]
# Delete those files
for f in php7_pool_files:
os.remove(f)
# Reload/restart the php pools
_run_service_command("stop", "php7.0-fpm")
os.system("systemctl start php5-fpm")
# Get list of nginx conf file
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf")
for f in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f)
os.system(c)
# Reload nginx
_run_service_command("reload", "nginx")

View file

@ -1,41 +0,0 @@
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.utils.filesystem import free_space_in_directory, space_used_by_directory
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch"
dependencies = ["migrate_to_stretch"]
def run(self):
if not self.package_is_installed("postgresql-9.4"):
logger.warning(m18n.n("migration_0005_postgresql_94_not_installed"))
return
if not self.package_is_installed("postgresql-9.6"):
raise YunohostError("migration_0005_postgresql_96_not_installed")
if not space_used_by_directory("/var/lib/postgresql/9.4") > free_space_in_directory("/var/lib/postgresql"):
raise YunohostError("migration_0005_not_enough_space", path="/var/lib/postgresql/")
subprocess.check_call("service postgresql stop", shell=True)
subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True)
subprocess.check_call("pg_upgradecluster -m upgrade 9.4 main", shell=True)
subprocess.check_call("pg_dropcluster --stop 9.4 main", shell=True)
subprocess.check_call("service postgresql start", shell=True)
def package_is_installed(self, package_name):
p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True)
p.communicate()
return p.returncode == 0

View file

@ -1,78 +0,0 @@
import spwd
import crypt
import random
import string
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import run_commands, check_output
from moulinette.utils.filesystem import append_to_file
from moulinette.authenticators.ldap import Authenticator
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin", "root", "test", "rpi"]
class MyMigration(Migration):
"Synchronize admin and root passwords"
def run(self):
new_hash = self._get_admin_hash()
self._replace_root_hash(new_hash)
logger.info(m18n.n("root_password_replaced_by_admin_password"))
@property
def mode(self):
# If the root password is still a "default" value,
# then this is an emergency and migration shall
# be applied automatically
#
# Otherwise, as playing with root password is touchy,
# we set this as a manual migration.
return "auto" if self._is_root_pwd_listed(SMALL_PWD_LIST) else "manual"
@property
def disclaimer(self):
if self._is_root_pwd_listed(SMALL_PWD_LIST):
return None
return m18n.n("migration_0006_disclaimer")
def _get_admin_hash(self):
"""
Fetch the admin hash from the LDAP db using slapcat
"""
admin_hash = check_output("slapcat \
| grep 'dn: cn=admin,dc=yunohost,dc=org' -A20 \
| grep userPassword -A2 \
| tr -d '\n ' \
| tr ':' ' ' \
| awk '{print $2}' \
| base64 -d \
| sed 's/{CRYPT}//g'")
return admin_hash
def _replace_root_hash(self, new_hash):
hash_root = spwd.getspnam("root").sp_pwd
with open('/etc/shadow', 'r') as before_file:
before = before_file.read()
with open('/etc/shadow', 'w') as after_file:
after_file.write(before.replace("root:" + hash_root,
"root:" + new_hash))
def _is_root_pwd_listed(self, pwd_list):
hash_root = spwd.getspnam("root").sp_pwd
for password in pwd_list:
if hash_root == crypt.crypt(password, hash_root):
return True
return False

View file

@ -1,70 +0,0 @@
import os
import re
from shutil import copyfile
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import mkdir, rm
from yunohost.tools import Migration
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
logger = getActionLogger('yunohost.migration')
SSHD_CONF = '/etc/ssh/sshd_config'
class MyMigration(Migration):
"""
This is the first step of a couple of migrations that ensure SSH conf is
managed by YunoHost (even if the "from_script" flag is present, which was
previously preventing it from being managed by YunoHost)
The goal of this first (automatic) migration is to make sure that the
sshd_config is managed by the regen-conf mechanism.
If the from_script flag exists, then we keep the current SSH conf such that it
will appear as "manually modified" to the regenconf.
In step 2 (manual), the admin will be able to choose wether or not to actually
use the recommended configuration, with an appropriate disclaimer.
"""
def run(self):
# Check if deprecated DSA Host Key is in config
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
dsa = False
for line in open(SSHD_CONF):
if re.match(dsa_rgx, line) is not None:
dsa = True
break
if dsa:
settings_set("service.ssh.allow_deprecated_dsa_hostkey", True)
# Here, we make it so that /etc/ssh/sshd_config is managed
# by the regen conf (in particular in the case where the
# from_script flag is present - in which case it was *not*
# managed by the regenconf)
# But because we can't be sure the user wants to use the
# recommended conf, we backup then restore the /etc/ssh/sshd_config
# right after the regenconf, such that it will appear as
# "manually modified".
if os.path.exists('/etc/yunohost/from_script'):
rm('/etc/yunohost/from_script')
copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp')
regen_conf(names=['ssh'], force=True)
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
# Restart ssh and rollback if it failed
if not _run_service_command('restart', 'ssh'):
# We don't rollback completely but it should be enough
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
if not _run_service_command('restart', 'ssh'):
raise YunohostError("migration_0007_cannot_restart")
else:
raise YunohostError("migration_0007_cancelled")

View file

@ -1,105 +0,0 @@
import os
import re
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import chown
from yunohost.tools import Migration
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
logger = getActionLogger('yunohost.migration')
SSHD_CONF = '/etc/ssh/sshd_config'
class MyMigration(Migration):
"""
In this second step, the admin is asked if it's okay to use
the recommended SSH configuration - which also implies
disabling deprecated DSA key.
This has important implications in the way the user may connect
to its server (key change, and a spooky warning might be given
by SSH later)
A disclaimer explaining the various things to be aware of is
shown - and the user may also choose to skip this migration.
"""
dependencies = ["ssh_conf_managed_by_yunohost_step1"]
def run(self):
settings_set("service.ssh.allow_deprecated_dsa_hostkey", False)
regen_conf(names=['ssh'], force=True)
# Update local archives folder permissions, so that
# admin can scp archives out of the server
if os.path.isdir(ARCHIVES_PATH):
chown(ARCHIVES_PATH, uid="admin", gid="root")
@property
def mode(self):
# If the conf is already up to date
# and no DSA key is used, then we're good to go
# and the migration can be done automatically
# (basically nothing shall change)
ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None)
current_hash = _calculate_hash(SSHD_CONF)
dsa = settings_get("service.ssh.allow_deprecated_dsa_hostkey")
if ynh_hash == current_hash and not dsa:
return "auto"
return "manual"
@property
def disclaimer(self):
if self.mode == "auto":
return None
# Detect key things to be aware of before enabling the
# recommended configuration
dsa_key_enabled = False
ports = []
root_login = []
port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$'
root_rgx = r'^[ \t]*PermitRootLogin[ \t]([^# \t]*)[ \t]*(?:#.*)?$'
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
for line in open(SSHD_CONF):
ports = ports + re.findall(port_rgx, line)
root_login = root_login + re.findall(root_rgx, line)
if not dsa_key_enabled and re.match(dsa_rgx, line) is not None:
dsa_key_enabled = True
custom_port = ports != ['22'] and ports != []
root_login_enabled = root_login and root_login[-1] != 'no'
# Build message
message = m18n.n("migration_0008_general_disclaimer")
if custom_port:
message += "\n\n" + m18n.n("migration_0008_port")
if root_login_enabled:
message += "\n\n" + m18n.n("migration_0008_root")
if dsa_key_enabled:
message += "\n\n" + m18n.n("migration_0008_dsa")
if custom_port or root_login_enabled or dsa_key_enabled:
message += "\n\n" + m18n.n("migration_0008_warning")
else:
message += "\n\n" + m18n.n("migration_0008_no_warning")
return message

View file

@ -1,39 +0,0 @@
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 run(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)

View file

@ -1,13 +0,0 @@
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate from official.json to apps.json (outdated, replaced by migration 13)"
def run(self):
logger.info("This migration is oudated and doesn't do anything anymore. The migration 13 will handle this instead.")
pass

View file

@ -1,16 +0,0 @@
import glob
import re
from yunohost.tools import Migration
from moulinette.utils.filesystem import read_file, write_to_file
class MyMigration(Migration):
"Force authentication in md5 for local connexions"
all_hba_files = glob.glob("/etc/postgresql/*/*/pg_hba.conf")
def run(self):
for filename in self.all_hba_files:
pg_hba_in = read_file(filename)
write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)password", "local\\1all\\2all\\3md5", pg_hba_in))

View file

@ -1,51 +0,0 @@
import os
import shutil
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json
from yunohost.tools import Migration
from yunohost.app import (_initialize_apps_catalog_system,
_update_apps_catalog,
APPS_CATALOG_CACHE,
APPS_CATALOG_CONF)
logger = getActionLogger('yunohost.migration')
LEGACY_APPS_CATALOG_CONF = '/etc/yunohost/appslists.json'
LEGACY_APPS_CATALOG_CONF_BACKUP = LEGACY_APPS_CATALOG_CONF + ".old"
class MyMigration(Migration):
"Migrate to the new future-proof apps catalog system"
def run(self):
if not os.path.exists(LEGACY_APPS_CATALOG_CONF):
logger.info("No need to do anything")
# Destroy old lecacy cache
if os.path.exists(APPS_CATALOG_CACHE):
shutil.rmtree(APPS_CATALOG_CACHE)
# and legacy cron
if os.path.exists("/etc/cron.daily/yunohost-fetch-appslists"):
os.remove("/etc/cron.daily/yunohost-fetch-appslists")
# Backup the legacy file
try:
legacy_catalogs = read_json(LEGACY_APPS_CATALOG_CONF)
# If there's only one catalog, we assume it's just the old official catalog
# Otherwise, warn the (power-?)users that they should migrate their old catalogs manually
if len(legacy_catalogs) > 1:
logger.warning("It looks like you had additional apps_catalog in the configuration file %s! YunoHost now uses %s instead, but it won't migrate your custom apps_catalog. You should do this manually. The old file has been backuped in %s." % (LEGACY_APPS_CATALOG_CONF, APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP))
except Exception as e:
logger.warning("Unable to parse the legacy conf %s (error : %s) ... migrating anyway" % (LEGACY_APPS_CATALOG_CONF, str(e)))
if os.path.exists(LEGACY_APPS_CATALOG_CONF):
os.rename(LEGACY_APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP)
_initialize_apps_catalog_system()
_update_apps_catalog()

View file

@ -1,31 +0,0 @@
import os
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json
from yunohost.tools import Migration
from yunohost.app import app_setting, APPS_SETTING_PATH
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"""Remove legacy app status.json files"""
def run(self):
apps = os.listdir(APPS_SETTING_PATH)
for app in apps:
status_file = os.path.join(APPS_SETTING_PATH, app, "status.json")
if not os.path.exists(status_file):
continue
try:
status = read_json(status_file)
current_revision = status.get("remote", {}).get("revision", "?")
app_setting(app, 'current_revision', current_revision)
except Exception as e:
logger.warning("Could not migrate status.json from app %s: %s", (app, str(e)))
else:
os.system("rm %s" % status_file)

View file

@ -0,0 +1,244 @@
import glob
import os
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output
from moulinette.utils.filesystem import read_file
from yunohost.tools import Migration, tools_update, tools_upgrade
from yunohost.app import unstable_apps
from yunohost.regenconf import manually_modified_files
from yunohost.utils.filesystem import free_space_in_directory
from yunohost.utils.packages import get_ynh_package_version, _list_upgradable_apt_packages
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Upgrade the system to Debian Buster and Yunohost 4.x"
mode = "manual"
def run(self):
self.check_assertions()
logger.info(m18n.n("migration_0015_start"))
#
# Make sure certificates do not use weak signature hash algorithms (md5, sha1)
# otherwise nginx will later refuse to start which result in
# catastrophic situation
#
self.validate_and_upgrade_cert_if_necessary()
#
# Patch sources.list
#
logger.info(m18n.n("migration_0015_patching_sources_list"))
self.patch_apt_sources_list()
tools_update(system=True)
# Tell libc6 it's okay to restart system stuff during the upgrade
os.system("echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections")
# Don't send an email to root about the postgresql migration. It should be handled automatically after.
os.system("echo 'postgresql-common postgresql-common/obsolete-major seen true' | debconf-set-selections")
#
# Specific packages upgrades
#
logger.info(m18n.n("migration_0015_specific_upgrade"))
# Update unscd independently, was 0.53-1+yunohost on stretch (custom build of ours) but now it's 0.53-1+b1 on vanilla buster,
# which for apt appears as a lower version (hence the --allow-downgrades and the hardcoded version number)
unscd_version = check_output('dpkg -s unscd | grep "^Version: " | cut -d " " -f 2')
if "yunohost" in unscd_version:
new_version = check_output("LC_ALL=C apt policy unscd 2>/dev/null | grep -v '\\*\\*\\*' | grep http -B1 | head -n 1 | awk '{print $1}'").strip()
if new_version:
self.apt_install('unscd=%s --allow-downgrades' % new_version)
else:
logger.warning("Could not identify which version of unscd to install")
# Upgrade libpam-modules independently, small issue related to willing to overwrite a file previously provided by Yunohost
libpammodules_version = check_output('dpkg -s libpam-modules | grep "^Version: " | cut -d " " -f 2')
if not libpammodules_version.startswith("1.3"):
self.apt_install('libpam-modules -o Dpkg::Options::="--force-overwrite"')
#
# Main upgrade
#
logger.info(m18n.n("migration_0015_main_upgrade"))
apps_packages = self.get_apps_equivs_packages()
self.hold(apps_packages)
tools_upgrade(system=True, allow_yunohost_upgrade=False)
if self.debian_major_version() == 9:
raise YunohostError("migration_0015_still_on_stretch_after_main_upgrade")
# Clean the mess
logger.info(m18n.n("migration_0015_cleaning_up"))
os.system("apt autoremove --assume-yes")
os.system("apt clean --assume-yes")
#
# Yunohost upgrade
#
logger.info(m18n.n("migration_0015_yunohost_upgrade"))
self.unhold(apps_packages)
tools_upgrade(system=True)
def debian_major_version(self):
# The python module "platform" and lsb_release are not reliable because
# on some setup, they may still return Release=9 even after upgrading to
# buster ... (Apparently this is related to OVH overriding some stuff
# with /etc/lsb-release for instance -_-)
# Instead, we rely on /etc/os-release which should be the raw info from
# the distribution...
return int(check_output("grep VERSION_ID /etc/os-release | head -n 1 | tr '\"' ' ' | cut -d ' ' -f2"))
def yunohost_major_version(self):
return int(get_ynh_package_version("yunohost")["version"].split('.')[0])
def check_assertions(self):
# Be on stretch (9.x) and yunohost 3.x
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be > 9.x but yunohost package
# would still be in 3.x...
if not self.debian_major_version() == 9 \
and not self.yunohost_major_version() == 3:
raise YunohostError("migration_0015_not_stretch")
# Have > 1 Go free space on /var/ ?
if free_space_in_directory("/var/") / (1024**3) < 1.0:
raise YunohostError("migration_0015_not_enough_free_space")
# Check system is up to date
# (but we don't if 'stretch' is already in the sources.list ...
# which means maybe a previous upgrade crashed and we're re-running it)
if " buster " not in read_file("/etc/apt/sources.list"):
tools_update(system=True)
upgradable_system_packages = list(_list_upgradable_apt_packages())
if upgradable_system_packages:
raise YunohostError("migration_0015_system_not_fully_up_to_date")
@property
def disclaimer(self):
# Avoid having a super long disclaimer + uncessary check if we ain't
# on stretch / yunohost 3.x anymore
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 10.x but yunohost package
# would still be in 3.x...
if not self.debian_major_version() == 9 \
and not self.yunohost_major_version() == 3:
return None
# Get list of problematic apps ? I.e. not official or community+working
problematic_apps = unstable_apps()
problematic_apps = "".join(["\n - " + app for app in problematic_apps])
# Manually modified files ? (c.f. yunohost service regen-conf)
modified_files = manually_modified_files()
modified_files = "".join(["\n - " + f for f in modified_files])
message = m18n.n("migration_0015_general_warning")
message = "N.B.: This migration has been tested by the community over the last few months but has only been declared stable recently. If your server hosts critical services and if you are not too confident with debugging possible issues, we recommend you to wait a little bit more while we gather more feedback and polish things up. If on the other hand you are relatively confident with debugging small issues that may arise, you are encouraged to run this migration ;)! You can read about remaining known issues and feedback from the community here: https://forum.yunohost.org/t/12195\n\n" + message
if problematic_apps:
message += "\n\n" + m18n.n("migration_0015_problematic_apps_warning", problematic_apps=problematic_apps)
if modified_files:
message += "\n\n" + m18n.n("migration_0015_modified_files", manually_modified_files=modified_files)
return message
def patch_apt_sources_list(self):
sources_list = glob.glob("/etc/apt/sources.list.d/*.list")
sources_list.append("/etc/apt/sources.list")
# This :
# - replace single 'stretch' occurence by 'buster'
# - comments lines containing "backports"
# - replace 'stretch/updates' by 'strech/updates' (or same with -)
for f in sources_list:
command = "sed -i -e 's@ stretch @ buster @g' " \
"-e '/backports/ s@^#*@#@' " \
"-e 's@ stretch/updates @ buster/updates @g' " \
"-e 's@ stretch-@ buster-@g' " \
"{}".format(f)
os.system(command)
def get_apps_equivs_packages(self):
command = "dpkg --get-selections" \
" | grep -v deinstall" \
" | awk '{print $1}'" \
" | { grep 'ynh-deps$' || true; }"
output = check_output(command).strip()
return output.split('\n') if output else []
def hold(self, packages):
for package in packages:
os.system("apt-mark hold {}".format(package))
def unhold(self, packages):
for package in packages:
os.system("apt-mark unhold {}".format(package))
def apt_install(self, cmd):
def is_relevant(l):
return "Reading database ..." not in l.rstrip()
callbacks = (
lambda l: logger.info("+ " + l.rstrip() + "\r") if is_relevant(l) else logger.debug(l.rstrip() + "\r"),
lambda l: logger.warning(l.rstrip()),
)
cmd = "LC_ALL=C DEBIAN_FRONTEND=noninteractive APT_LISTCHANGES_FRONTEND=none apt install --quiet -o=Dpkg::Use-Pty=0 --fix-broken --assume-yes " + cmd
logger.debug("Running: %s" % cmd)
call_async_output(cmd, callbacks, shell=True)
def validate_and_upgrade_cert_if_necessary(self):
active_certs = set(check_output("grep -roh '/.*crt.pem' /etc/nginx/").strip().split("\n"))
cmd = "LC_ALL=C openssl x509 -in %s -text -noout | grep -i 'Signature Algorithm:' | awk '{print $3}' | uniq"
default_crt = '/etc/yunohost/certs/yunohost.org/crt.pem'
default_key = '/etc/yunohost/certs/yunohost.org/key.pem'
default_signature = check_output(cmd % default_crt).strip() if default_crt in active_certs else None
if default_signature is not None and (default_signature.startswith("md5") or default_signature.startswith("sha1")):
logger.warning("%s is using a pretty old certificate incompatible with newer versions of nginx ... attempting to regenerate a fresh one" % default_crt)
os.system("mv %s %s.old" % (default_crt, default_crt))
os.system("mv %s %s.old" % (default_key, default_key))
ret = os.system("/usr/share/yunohost/hooks/conf_regen/02-ssl init")
if ret != 0 or not os.path.exists(default_crt):
logger.error("Upgrading the certificate failed ... reverting")
os.system("mv %s.old %s" % (default_crt, default_crt))
os.system("mv %s.old %s" % (default_key, default_key))
signatures = {cert: check_output(cmd % cert).strip() for cert in active_certs}
def cert_is_weak(cert):
sig = signatures[cert]
return sig.startswith("md5") or sig.startswith("sha1")
weak_certs = [cert for cert in signatures.keys() if cert_is_weak(cert)]
if weak_certs:
raise YunohostError("migration_0015_weak_certs", certs=", ".join(weak_certs))

View file

@ -0,0 +1,73 @@
import os
import glob
from shutil import copy2
from moulinette.utils.log import getActionLogger
from yunohost.app import _is_installed, _patch_legacy_php_versions_in_settings
from yunohost.tools import Migration
from yunohost.service import _run_service_command
logger = getActionLogger('yunohost.migration')
PHP70_POOLS = "/etc/php/7.0/fpm/pool.d"
PHP73_POOLS = "/etc/php/7.3/fpm/pool.d"
PHP70_SOCKETS_PREFIX = "/run/php/php7.0-fpm"
PHP73_SOCKETS_PREFIX = "/run/php/php7.3-fpm"
MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}".format(PHP70_POOLS)
class MyMigration(Migration):
"Migrate php7.0-fpm 'pool' conf files to php7.3"
dependencies = ["migrate_to_buster"]
def run(self):
# Get list of php7.0 pool files
php70_pool_files = glob.glob("{}/*.conf".format(PHP70_POOLS))
# Keep only basenames
php70_pool_files = [os.path.basename(f) for f in php70_pool_files]
# Ignore the "www.conf" (default stuff, probably don't want to touch it ?)
php70_pool_files = [f for f in php70_pool_files if f != "www.conf"]
for f in php70_pool_files:
# Copy the files to the php7.3 pool
src = "{}/{}".format(PHP70_POOLS, f)
dest = "{}/{}".format(PHP73_POOLS, f)
copy2(src, dest)
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, dest)
os.system(c)
# Also add a comment that it was automatically moved from php7.0
# (for human traceability and backward migration)
c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest)
os.system(c)
app_id = os.path.basename(f)[:-len(".conf")]
if _is_installed(app_id):
_patch_legacy_php_versions_in_settings("/etc/yunohost/apps/%s/" % app_id)
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/%s.conf" % app_id)
for f in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP70_SOCKETS_PREFIX, PHP73_SOCKETS_PREFIX, f)
os.system(c)
os.system("rm /etc/logrotate.d/php7.0-fpm") # We remove this otherwise the logrotate cron will be unhappy
# Reload/restart the php pools
_run_service_command("restart", "php7.3-fpm")
_run_service_command("enable", "php7.3-fpm")
os.system("systemctl stop php7.0-fpm")
os.system("systemctl disable php7.0-fpm")
# Reload nginx
_run_service_command("reload", "nginx")

View file

@ -0,0 +1,65 @@
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.utils.filesystem import free_space_in_directory, space_used_by_directory
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate DBs from Postgresql 9.6 to 11 after migrating to Buster"
dependencies = ["migrate_to_buster"]
def run(self):
if not self.package_is_installed("postgresql-9.6"):
logger.warning(m18n.n("migration_0017_postgresql_96_not_installed"))
return
if not self.package_is_installed("postgresql-11"):
raise YunohostError("migration_0017_postgresql_11_not_installed")
# Make sure there's a 9.6 cluster
try:
self.runcmd("pg_lsclusters | grep -q '^9.6 '")
except Exception:
logger.warning("It looks like there's not active 9.6 cluster, so probably don't need to run this migration")
return
if not space_used_by_directory("/var/lib/postgresql/9.6") > free_space_in_directory("/var/lib/postgresql"):
raise YunohostError("migration_0017_not_enough_space", path="/var/lib/postgresql/")
self.runcmd("systemctl stop postgresql")
self.runcmd("pg_dropcluster --stop 11 main || true") # We do not trigger an exception if the command fails because that probably means cluster 11 doesn't exists, which is fine because it's created during the pg_upgradecluster)
self.runcmd("pg_upgradecluster -m upgrade 9.6 main")
self.runcmd("pg_dropcluster --stop 9.6 main")
self.runcmd("systemctl start postgresql")
def package_is_installed(self, package_name):
(returncode, out, err) = self.runcmd("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), raise_on_errors=False)
return returncode == 0
def runcmd(self, cmd, raise_on_errors=True):
logger.debug("Running command: " + cmd)
p = subprocess.Popen(cmd,
shell=True,
executable='/bin/bash',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
returncode = p.returncode
if raise_on_errors and returncode != 0:
raise YunohostError("Failed to run command '{}'.\nreturncode: {}\nstdout:\n{}\nstderr:\n{}\n".format(cmd, returncode, out, err))
out = out.strip().split("\n")
return (returncode, out, err)

View file

@ -0,0 +1,108 @@
import os
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.firewall import firewall_reload
from yunohost.service import service_restart
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate legacy iptables rules from stretch that relied on xtable and should now rely on nftable"
dependencies = ["migrate_to_buster"]
def run(self):
self.do_ipv4 = os.system("iptables -w -L >/dev/null") == 0
self.do_ipv6 = os.system("ip6tables -w -L >/dev/null") == 0
if not self.do_ipv4:
logger.warning(m18n.n('iptables_unavailable'))
if not self.do_ipv6:
logger.warning(m18n.n('ip6tables_unavailable'))
backup_folder = "/home/yunohost.backup/premigration/xtable_to_nftable/"
if not os.path.exists(backup_folder):
os.makedirs(backup_folder, 0o750)
self.backup_rules_ipv4 = os.path.join(backup_folder, "legacy_rules_ipv4")
self.backup_rules_ipv6 = os.path.join(backup_folder, "legacy_rules_ipv6")
# Backup existing legacy rules to be able to rollback
if self.do_ipv4 and not os.path.exists(self.backup_rules_ipv4):
self.runcmd("iptables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ?
self.runcmd("iptables-legacy-save > %s" % self.backup_rules_ipv4)
assert open(self.backup_rules_ipv4).read().strip(), "Uhoh backup of legacy ipv4 rules is empty !?"
if self.do_ipv6 and not os.path.exists(self.backup_rules_ipv6):
self.runcmd("ip6tables-legacy -L >/dev/null") # For some reason if we don't do this, iptables-legacy-save is empty ?
self.runcmd("ip6tables-legacy-save > %s" % self.backup_rules_ipv6)
assert open(self.backup_rules_ipv6).read().strip(), "Uhoh backup of legacy ipv6 rules is empty !?"
# We inject the legacy rules (iptables-legacy) into the new iptable (just "iptables")
try:
if self.do_ipv4:
self.runcmd("iptables-legacy-save | iptables-restore")
if self.do_ipv6:
self.runcmd("ip6tables-legacy-save | ip6tables-restore")
except Exception as e:
self.rollback()
raise YunohostError("migration_0018_failed_to_migrate_iptables_rules", error=e)
# Reset everything in iptables-legacy
# Stolen from https://serverfault.com/a/200642
try:
if self.do_ipv4:
self.runcmd(
"iptables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat
" /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept
" /COMMIT/ { print $0; }'" # Keep the line COMMIT
" | iptables-legacy-restore")
if self.do_ipv6:
self.runcmd(
"ip6tables-legacy-save | awk '/^[*]/ { print $1 }" # Keep lines like *raw, *filter and *nat
" /^:[A-Z]+ [^-]/ { print $1 \" ACCEPT\" ; }" # Turn all policies to accept
" /COMMIT/ { print $0; }'" # Keep the line COMMIT
" | ip6tables-legacy-restore")
except Exception as e:
self.rollback()
raise YunohostError("migration_0018_failed_to_reset_legacy_rules", error=e)
# You might be wondering "uh but is it really useful to
# iptables-legacy-save | iptables-restore considering firewall_reload()
# flush/resets everything anyway ?"
# But the answer is : firewall_reload() only resets the *filter table.
# On more complex setups (e.g. internet cube or docker) you will also
# have rules in the *nat (or maybe *raw?) sections of iptables.
firewall_reload()
service_restart("fail2ban")
def rollback(self):
if self.do_ipv4:
self.runcmd("iptables-legacy-restore < %s" % self.backup_rules_ipv4)
if self.do_ipv6:
self.runcmd("iptables-legacy-restore < %s" % self.backup_rules_ipv6)
def runcmd(self, cmd, raise_on_errors=True):
logger.debug("Running command: " + cmd)
p = subprocess.Popen(cmd,
shell=True,
executable='/bin/bash',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
returncode = p.returncode
if raise_on_errors and returncode != 0:
raise YunohostError("Failed to run command '{}'.\nreturncode: {}\nstdout:\n{}\nstderr:\n{}\n".format(cmd, returncode, out, err))
out = out.strip().split("\n")
return (returncode, out, err)

View file

@ -27,7 +27,6 @@
import re import re
import os import os
import time import time
import smtplib
from moulinette import m18n, msettings from moulinette import m18n, msettings
from moulinette.utils import log from moulinette.utils import log
@ -452,7 +451,7 @@ class Diagnoser():
key = "diagnosis_description_" + id_ key = "diagnosis_description_" + id_
descr = m18n.n(key) descr = m18n.n(key)
# If no description available, fallback to id # If no description available, fallback to id
return descr if descr != key else id_ return descr if descr.decode('utf-8') != key else id_
@staticmethod @staticmethod
def i18n(report, force_remove_html_tags=False): def i18n(report, force_remove_html_tags=False):
@ -478,6 +477,7 @@ class Diagnoser():
meta_data.update(item.get("data", {})) meta_data.update(item.get("data", {}))
html_tags = re.compile(r'<[^>]+>') html_tags = re.compile(r'<[^>]+>')
def m18n_(info): def m18n_(info):
if not isinstance(info, tuple) and not isinstance(info, list): if not isinstance(info, tuple) and not isinstance(info, list):
info = (info, {}) info = (info, {})
@ -583,6 +583,7 @@ Subject: %s
%s %s
""" % (from_, to_, subject_, disclaimer, content) """ % (from_, to_, subject_, disclaimer, content)
import smtplib
smtp = smtplib.SMTP("localhost") smtp = smtplib.SMTP("localhost")
smtp.sendmail(from_, [to_], message) smtp.sendmail(from_, [to_], message)
smtp.quit() smtp.quit()

View file

@ -90,6 +90,10 @@ def domain_add(operation_logger, domain, dyndns=False):
operation_logger.start() operation_logger.start()
# Lower domain to avoid some edge cases issues
# See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765
domain = domain.lower()
# DynDNS domain # DynDNS domain
if dyndns: if dyndns:
@ -611,15 +615,15 @@ def _get_DKIM(domain):
dkim = re.match(( dkim = re.match((
r'^(?P<host>[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+' r'^(?P<host>[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+'
'[^"]*"v=(?P<v>[^";]+);' '[^"]*"v=(?P<v>[^";]+);'
'[\s"]*k=(?P<k>[^";]+);' r'[\s"]*k=(?P<k>[^";]+);'
'[\s"]*p=(?P<p>[^";]+)'), dkim_content, re.M | re.S '[\s"]*p=(?P<p>[^";]+)'), dkim_content, re.M | re.S
) )
else: else:
dkim = re.match(( dkim = re.match((
r'^(?P<host>[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+' r'^(?P<host>[a-z_\-\.]+)[\s]+([0-9]+[\s]+)?IN[\s]+TXT[\s]+'
'[^"]*"v=(?P<v>[^";]+);' '[^"]*"v=(?P<v>[^";]+);'
'[\s"]*h=(?P<h>[^";]+);' r'[\s"]*h=(?P<h>[^";]+);'
'[\s"]*k=(?P<k>[^";]+);' r'[\s"]*k=(?P<k>[^";]+);'
'[\s"]*p=(?P<p>[^";]+)'), dkim_content, re.M | re.S '[\s"]*p=(?P<p>[^";]+)'), dkim_content, re.M | re.S
) )

View file

@ -205,22 +205,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
key = keys[0] key = keys[0]
# This mean that hmac-md5 is used
# (Re?)Trigger the migration to sha256 and return immediately.
# The actual update will be done in next run.
if "+157" in key:
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("migrate_to_tsig_sha256")
try:
migration.run(dyn_host, domain, key)
except Exception as e:
logger.error(m18n.n('migrations_migration_has_failed',
exception=e,
number=migration.number,
name=migration.name),
exc_info=1)
return
# Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me' # Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me'
host = domain.split('.')[1:] host = domain.split('.')[1:]
host = '.'.join(host) host = '.'.join(host)

View file

@ -24,13 +24,8 @@
Manage firewall rules Manage firewall rules
""" """
import os import os
import sys
import yaml import yaml
try:
import miniupnpc import miniupnpc
except ImportError:
sys.stderr.write('Error: Yunohost CLI Require miniupnpc lib\n')
sys.exit(1)
from moulinette import m18n from moulinette import m18n
from yunohost.utils.error import YunohostError from yunohost.utils.error import YunohostError

Some files were not shown because too many files have changed in this diff Show more